主な質問に対する私の答え: 「xx == 0 が false である x の浮動小数点値はありますか?」つまり、少なくとも Intel プロセッサでの浮動小数点の実装では、"+" および "-" 演算で算術アンダーフローが発生しないため、xx == 0 が false である x を見つけることができません。同じことが、IEEE 754-2008 をサポートするすべてのプロセッサに当てはまります(以下の参考文献を参照)。
別のあなたの質問に対する私の短い答え: if (xy == 0) は if (x == y) とまったく同じくらい安全なので、 assert(xx == 0) は問題ありません。 xy)。
理由は以下。float/double の数値は、仮数と 2 進指数の形式でメモリに保持されます。標準ケースでは、仮数は正規化されています: >= 0.5 かつ < 1<float.h>
です。IEEE 浮動小数点標準からいくつかの定数を見つけることができます。私たちにとって興味深いのは、次のことだけです
#define DBL_MIN 2.2250738585072014e-308 /* min positive value */
#define DBL_MIN_10_EXP (-307) /* min decimal exponent */
#define DBL_MIN_EXP (-1021) /* min binary exponent */
しかし、 DBL_MIN未満の倍数を使用できることを誰もが知っているわけではありません。DBL_MIN 未満の数値で算術演算を行う場合、この数値は正規化されないため、「丸め誤差」なしで整数 (仮数部のみの演算) と同じようにこの数値を操作できます。
備考: 個人的には「丸め誤差」という言葉は使わないようにしています。これらの操作は、浮動小数点数のような同じコンピューター番号の +、-、*、および / 操作と同じではありません。浮動小数点数のサブセットには決定論的な操作があり、それぞれのビット数が明確に定義された形式 (仮数、指数) で保存できます。このような float のサブセットは、computer float numberと名付けることができます。したがって、従来の浮動小数点演算の結果は、コンピューターの浮動小数点数セットに射影されます。このような射影操作は決定論的であり、if x1 >= x2 then x1*y >= x2*y のような多くの機能を備えています。
長い発言で申し訳ありませんが、本題に戻ります。
DBL_MIN より小さい数値で操作した場合に何が得られるかを正確に示すために、C で小さなプログラムを作成しました。
#include <stdio.h>
#include <float.h>
#include <math.h>
void DumpDouble(double d)
{
unsigned char *b = (unsigned char *)&d;
int i;
for (i=1; i<=sizeof(d); i++) {
printf ("%02X", b[sizeof(d)-i]);
}
printf ("\n");
}
int main()
{
double x, m, y, z;
int exp;
printf ("DBL_MAX=%.16e\n", DBL_MAX);
printf ("DBL_MAX in binary form: ");
DumpDouble(DBL_MAX);
printf ("DBL_MIN=%.16e\n", DBL_MIN);
printf ("DBL_MIN in binary form: ");
DumpDouble(DBL_MIN);
// Breaks the floating point number x into its binary significand
// (a floating point value between 0.5(included) and 1.0(excluded))
// and an integral exponent for 2
x = DBL_MIN;
m = frexp (x, &exp);
printf ("DBL_MIN has mantissa=%.16e and exponent=%d\n", m, exp);
printf ("mantissa of DBL_MIN in binary form: ");
DumpDouble(m);
// ldexp() returns the resulting floating point value from
// multiplying x (the significand) by 2
// raised to the power of exp (the exponent).
x = ldexp (0.5, DBL_MIN_EXP); // -1021
printf ("the number (x) constructed from mantissa 0.5 and exponent=DBL_MIN_EXP (%d) in binary form: ", DBL_MIN_EXP);
DumpDouble(x);
y = ldexp (0.5000000000000001, DBL_MIN_EXP);
m = frexp (y, &exp);
printf ("the number (y) constructed from mantissa 0.5000000000000001 and exponent=DBL_MIN_EXP (%d) in binary form: ", DBL_MIN_EXP);
DumpDouble(y);
printf ("mantissa of this number saved as double will be displayed by printf(%%.16e) as %.16e and exponent=%d\n", m, exp);
y = ldexp ((1 + DBL_EPSILON)/2, DBL_MIN_EXP);
m = frexp (y, &exp);
printf ("the number (y) constructed from mantissa (1+DBL_EPSILON)/2 and exponent=DBL_MIN_EXP (%d) in binary form: ", DBL_MIN_EXP);
DumpDouble(y);
printf ("mantissa of this number saved as double will be displayed by printf(%%.16e) as %.16e and exponent=%d\n", m, exp);
z = y - x;
m = frexp (z, &exp);
printf ("z=y-x in binary form: ");
DumpDouble(z);
printf ("z will be displayed by printf(%%.16e) as %.16e\n", z);
printf ("z has mantissa=%.16e and exponent=%d\n", m, exp);
if (x == y)
printf ("\"if (x == y)\" say x == y\n");
else
printf ("\"if (x == y)\" say x != y\n");
if ((x-y) == 0)
printf ("\"if ((x-y) == 0)\" say \"(x-y) == 0\"\n");
else
printf ("\"if ((x-y) == 0)\" say \"(x-y) != 0\"\n");
}
このコードは、次の出力を生成しました。
DBL_MAX=1.7976931348623157e+308
DBL_MAX in binary form: 7FEFFFFFFFFFFFFF
DBL_MIN=2.2250738585072014e-308
DBL_MIN in binary form: 0010000000000000
DBL_MIN has mantissa=5.0000000000000000e-001 and exponent=-1021
mantissa of DBL_MIN in binary form: 3FE0000000000000
the number (x) constructed from mantissa 0.5 and exponent=DBL_MIN_EXP (-1021) in binary form: 0010000000000000
the number (y) constructed from mantissa 0.5000000000000001 and exponent=DBL_MIN_EXP (-1021) in binary form: 0010000000000001
mantissa of this number saved as double will be displayed by printf(%.16e) as 5.0000000000000011e-001 and exponent=-1021
the number (y) constructed from mantissa (1+DBL_EPSILON)/2 and exponent=DBL_MIN_EXP (-1021) in binary form: 0010000000000001
mantissa of this number saved as double will be displayed by printf(%.16e) as 5.0000000000000011e-001 and exponent=-1021
z=y-x in binary form: 0000000000000001
z will be displayed by printf(%.16e) as 4.9406564584124654e-324
z has mantissa=5.0000000000000000e-001 and exponent=-1073
"if (x == y)" say x != y
"if ((x-y) == 0)" say "(x-y) != 0"
したがって、DBL_MIN 未満の数値で作業すると、正規化されないことがわかります (「 」を参照0000000000000001
)。これらの数値は、整数と同様に「エラー」なしで処理しています。したがって、割り当てた場合、 y=x
thenif (x-y == 0)
は とまったく同じように安全if (x == y)
であり、正常にassert(x-x == 0)
動作します。この例では、z = 0.5 * 2 ^(-1073) = 1 * 2 ^(-1072) です。この数は実際には double で保存できる最小の数です。DBL_MIN 未満の数値を使用したすべての算術演算は、整数に 2 ^(-1072) を乗算した場合と同様に機能します。
したがって、Intel プロセッサを搭載した Windows 7 コンピュータではアンダーフローの問題は発生しません。誰かが別のプロセッサを持っている場合、私たちの結果を比較するのは興味深いでしょう.
- または + 操作で算術アンダーフローを生成する方法を誰か知っていますか? 私の実験はそのように見えますが、それは不可能です。
EDITED : コードとメッセージを読みやすくするために、コードを少し変更しました。
追加リンク: 私の実験によると、私の Intel Core 2 CPU ではhttp://grouper.ieee.org/groups/754/faq.html#underflowが完全に正しいことがわかりました。この計算方法では、「+」および「-」の浮動小数点演算でアンダーフローが発生しません。私の結果は、Strict (/fp:strict) または Precise (/fp:precise) Microsoft Visual C コンパイラ スイッチに依存していません ( http://msdn.microsoft.com/en-us/library/e7s85ffb%28VS.80%29を参照)。 .aspxおよびhttp://msdn.microsoft.com/en-us/library/Aa289157 )
もう1つ(おそらく最後のリンク)リンクと私の最終的な発言: http://en.wikipedia.org/wiki/Subnormal_numbersの良いリファレンスを見つけました。ここでは、以前に書いたものと同じように説明されています。非正規数または非正規化数 ( IEEE 754-2008などでサブノーマル数と呼ばれることが多い) を含めるには、次のステートメントに従います。
「非正規数は、浮動小数点数の加算と減算が決してアンダーフローしないという保証を提供します。近くにある 2 つの浮動小数点数には、常に表現可能なゼロ以外の差があります。段階的アンダーフローがないと、値が等しくなくても、減算 a-b がアンダーフローしてゼロになる可能性があります。」</p>
したがって、すべての結果は、IEEE 754-2008 をサポートするすべてのプロセッサで正しくなければなりません。