6

http://www.gotw.ca/gotw/067.htmに例があります

int main()
{
  double x = 1e8;
  //float x = 1e8;
  while( x > 0 )
  {
    --x;
  }
}

double を float に変更すると、VS2008 では無限ループになります。ゴウの説明によると:

float が 0 から 1e8 までのすべての整数値を正確に表現できない場合はどうなるでしょうか? その後、変更されたプログラムはカウントダウンを開始しますが、最終的には表現できない値 N に到達し、 N-1 == N (浮動小数点の精度が不十分なため) になります...そして、ループはスタックしたままになります。プログラムが実行されているマシンの電源がなくなるまで、その値で。

私が理解していることから、IEEE754 float は単精度 (32 ビット) であり、float の範囲は +/- 3.4e +/- 38 であり、有効桁数は 7 桁である必要があります。

しかし、私はまだこれがどのように起こるのか正確には理解していません. 誰かがこのビットを説明しようとすることができますか?

少し追加情報: double x = 1e8 を使用すると、約 1 秒で終了しました。float x = 1e8 に変更すると、はるかに長く実行されます (5 分後も実行されます) float x = 1e7;。 1秒くらいで終わりました。

私のテスト環境は VS2008 です。

ところで、基本的な IEEE 754 形式の説明は、既に理解しているので質問していません。

ありがとう

4

4 に答える 4

8

議論のために、浮動小数点数を 10 進数 7 桁で表し、仮数を 10 進数 2 桁で表すプロセッサがあると仮定しましょう。したがって、数値 1e8 は次のように格納されます。

1.000 000 e 08

(「.」と「e」は実際に格納する必要はありません。)

したがって、「1e8 - 1」を計算する必要があります。1 は次のように表されます。

1.000 000 e 00

さて、引き算を行うために、まず無限の精度で引き算を行い、次に「.」の前の最初の桁が正規化されるようにします。は 1 から 9 の間であり、最後に最も近い表現可能な値に丸められます (ブレーク オン イーブンなど)。"1e8 - 1" の無限精度の結果は

0.99 999 999 e 08

または正規化

9.9 999 999 e 07

ご覧のとおり、無限精度の結果には、アーキテクチャが実際に提供するものよりも 1 桁多い仮数が必要です。したがって、無限に正確な結果を有効数字 7 桁に丸める (そして再正規化する) 必要があります。

1.000 000 e 08

したがって、「1e8 - 1 == 1e8」になり、ループは終了しません。

実際には、IEEE 754 バイナリ浮動小数点数を使用していますが、これは少し異なりますが、原理はほぼ同じです。

于 2011-08-09T13:58:40.810 に答える
3

操作x--は (この場合) と同等x = x - 1です。つまり、 の元の値xが取得され、1減算され (IEEE 754-1985 で義務付けられている無限精度を使用)、結果がfloat値空間の次の値に丸められます。

数値の丸め結果は1.0e8f + i以下のi in [-10;10]とおりです。

 -10: 9.9999992E7     (binary +|10011001|01111101011110000011111)
  -9: 9.9999992E7     (binary +|10011001|01111101011110000011111)
  -8: 9.9999992E7     (binary +|10011001|01111101011110000011111)
  -7: 9.9999992E7     (binary +|10011001|01111101011110000011111)
  -6: 9.9999992E7     (binary +|10011001|01111101011110000011111)
  -5: 9.9999992E7     (binary +|10011001|01111101011110000011111)
  -4: 1.0E8           (binary +|10011001|01111101011110000100000)
  -3: 1.0E8           (binary +|10011001|01111101011110000100000)
  -2: 1.0E8           (binary +|10011001|01111101011110000100000)
  -1: 1.0E8           (binary +|10011001|01111101011110000100000)
   0: 1.0E8           (binary +|10011001|01111101011110000100000)
   1: 1.0E8           (binary +|10011001|01111101011110000100000)
   2: 1.0E8           (binary +|10011001|01111101011110000100000)
   3: 1.0E8           (binary +|10011001|01111101011110000100000)
   4: 1.0E8           (binary +|10011001|01111101011110000100000)
   5: 1.00000008E8    (binary +|10011001|01111101011110000100001)
   6: 1.00000008E8    (binary +|10011001|01111101011110000100001)
   7: 1.00000008E8    (binary +|10011001|01111101011110000100001)
   8: 1.00000008E8    (binary +|10011001|01111101011110000100001)
   9: 1.00000008E8    (binary +|10011001|01111101011110000100001)
  10: 1.00000008E8    (binary +|10011001|01111101011110000100001)

1.0e8fしたがって、と1.0e8f + 4および他のいくつかの数が同じ表現を持っていることがわかります。IEEE 754-1985 浮動小数点形式の詳細を既に知っているので、残りの桁が丸められている必要があることもわかります。

于 2011-08-09T14:37:19.400 に答える
1

n - 1nの両方が浮動小数点数の近似的な性質のために同じ表現を持つ場合、 n - 1の結果はどうなりますか?

于 2011-08-09T12:16:19.910 に答える
1

表現できない値に「到達」することに関して、ハーブは非常に難解な浮動小数点表現の可能性を含めていたと思います。

通常の浮動小数点表現では、カウントダウンが成功するように、そのような値で開始するか (つまり、最初の値に固執する)、正確に表現できるゼロを中心とする連続した整数の範囲内のどこかにいることになります。

IEEE 754 の場合、通常 C++ の 32 ビット表現にfloatは 23 ビットの仮数があり、通常 C++ の 64 ビット表現にdoubleは 52 ビットの仮数があります。これはdouble、少なくとも -(2^52-1) ... 2^52-1 の範囲の整数を正確に表すことができることを意味します。範囲をさらに 2 倍に拡張できるかどうかはよくわかりません。考えると少しめまいがします。:-)

乾杯 & hth.,

于 2011-08-09T12:22:46.393 に答える