8

最近、私は投稿を読んでいました: Herb Sutter による GOTW からの Double or Nothing 以下のプログラムの説明に少し混乱しています:

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

このコードが、あるマシンで 1 秒間実行されると仮定します。このようなコードはばかげているという点には同意します。

ただし、xからfloatに変更した場合の問題に関する説明によるdoubleと、一部のコンパイラでは、コンピューターが永久に実行され続けます。説明は、標準からの次の引用に基づいています。

C++ 標準のセクション 3.9.1/8 からの引用:

浮動小数点型には、float、double、long double の 3 つがあります。double 型は少なくとも float と同じ精度を提供し、型 long double は少なくとも double と同じ精度を提供します。float 型の値のセットは、double 型の値のセットのサブセットです。double 型の値のセットは、long double 型の値のセットのサブセットです。

コードの質問は次のとおりです。

「double」を「float」に変更すると、どのくらいの時間がかかると予想されますか? なんで?

与えられた説明は次のとおりです。

float が 0 から 1e8 までのすべての整数値を正確に表現できるかどうかに応じて、おそらく約 1 秒 (特定の実装では、float は double よりも速いか、同じくらい速いか、または遅いかもしれません) かかるか、永遠にかかります。

上記の標準からの引用は、double で表現できるが float では表現できない値が存在する可能性があることを意味します。特に、一部の一般的なプラットフォームやコンパイラでは、double は [0,1e8] のすべての整数値を正確に表すことができますが、float はできません。

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

私の質問は:

float が を表すことさえできない場合1e8は、初期化時にすでにオーバーフローが発生しているはずfloat x = 1e8です。では、どうしてコンピュータを永遠に動かすことができるのでしょうか?

ここで簡単な例を試しました(そうではありませんdoubleint

#include <iostream>

int main()
{
   int a = 4444444444444444444;
   std::cout << "a " << a << std::endl;
   return 0;
}
It outputs: a -1357789412

これは、コンパイラが指定された数値をint型で表現できない場合、オーバーフローが発生することを意味します。

じゃあ読み間違えた?私が逃したポイントは何ですか?xから未定義の動作に変更されdoubleていますか?float

ありがとうございました!

4

2 に答える 2

9

キーワードは「まさに」。

float1e8あなたが異常なタイプを持っていない限り、正確にさえ表すことができますfloat。しかし、それはすべての小さな値を正確に表現できるという意味ではありません。たとえば、通常2^25+1 = 33554433は 26 ビットの精度を必要とし、float(通常は 23+1 ビットの精度を持つ) で正確に表現できず2^25-1 = 33554431、25 を必要とするもできません。精度のビット。

これらの数値は両方とも として表され2^25 = 33554432、次に

33554432.0f - 1 == 33554432.0f

ループします。(前にループにヒットしますが、そのループには適切な 10 進数表現があります ;)

整数演算ではx - 1 != xfor allxがありますが、浮動小数点演算ではありません。

通常の 23+1 ビットの精度しかない場合でもループが終了する可能性があることに注意してください。これfloatは、標準では浮動小数点計算を型よりも高い精度で実行できるため、計算が十分に高い精度で実行される場合です。 (たとえば、通常のdouble52+1 ビット)、すべての減算が変更されxます。

于 2013-05-16T15:02:48.100 に答える
0

連続する x 値の値を計算するこの単純な変更を試してください。

#include <iostream>
using namespace std;

int main()
 {
     float x = 1e8;
     while( x > 0 )
     {
        cout << x << endl;
        --x;
     }
 }

float の一部の実装では、float の値が 1e8 またはその領域でスタックしていることがわかります。これは float が数値を格納する方法によるものです。浮動小数点数は、可能なすべての 10 進数値を表すことはできません (また、ビット制限された表現もできません)。そのため、浮動小数点数で非常に大きな値を処理する場合、基本的には 10 進数をいくらか累乗したものになります。この 10 進数の値が、最後のビットが切り捨てられる値で終わる場合は、切り上げられることを意味します。あなたが最終的に得られるのは、それ自体に減少する(その後、元に戻る)値です。

于 2013-05-16T15:01:46.580 に答える