最後に更新があります!
少し話があります。
MS Visual Studio 2008 (64 ビット PC の Windows 7 で実行) でコンパイルされた C プログラムで、マシンのイプシロン (条件 1.0 + イプシロン = 1.0 を満たす最大のイプシロン > 0) を計算したかったのです。double と float の精度が異なることを知っているので、両方の答えを見たかったのです。そのため、次のプログラムを作成しました。
#include <stdio.h>
typedef double float_type;
int main()
{
float_type eps = 1.0;
while ((float_type) 1.0 + eps / (float_type) 2.0 > (float_type) 1.0)
eps = eps / (float_type) 2.0;
printf("%g\n", eps);
return 0;
}
2.22045e-16 という double 型と float 型の両方に対して同じ答えが得られたことに非常に驚きました。double は float の 2 倍のメモリを占有し、より正確なはずなので、これは奇妙でした。その後、ウィキペディアを調べて、そこからサンプル コードを取得しました。
#include <stdio.h>
int main(int argc, char **argv)
{
float machEps = 1.0f;
do {
machEps /= 2.0f;
} while ((float)(1.0 + (machEps/2.0)) != 1.0);
printf( "\nCalculated Machine epsilon: %G\n", machEps );
return 0;
}
それが正しく機能したとき、私はさらに驚きました!2 つのプログラムの基本的な違いを理解しようと何度か試みた後、次の事実がわかりました。ループ条件を
while ((float_type) (1.0 + eps / (float_type) 2.0) > (float_type) 1.0)
まあ、それはあなたが言う謎です。ああ、本当の謎は次のとおりです。比較:
while ((float) (1.0 + eps / 2.0f) > 1.0f)
正解(1.19209e-07)と
while ((float) (1.0f + eps / 2.0f) > 1.0f)
float では正しくなく、double では正しい答えが得られました (2.22045e-16)。
実際、それは完全に間違っています。結果は逆になるはずです。これは、デフォルトで 1.0 のような定数が (標準に従って) コンパイラによって double として扱われ、それが算術式に存在する場合、他のすべてのオペランドが double に昇格されるためです。逆に、1.0f を書き込むと、すべてのオペランドが float になり、昇格は行われません。それでも、まったく異なる結果が得られます。
これらすべてのテストの後、gcc を使用して Linux でプログラムを実行してコンパイルしようとしました。当然のことながら、私が期待したとおりに印刷されました(正解)。したがって、これは Visual Studio のバグだと思います。皆さんを笑わせるために (その瞬間まで私の投稿を読んだ人がいるとしたら、何が疑わしいのでしょう ^_^) 別の比較を行います。
float c = 1.0;
while ((float) (c + eps / 2.0f) > 1.0f)
これはVSでは正しく動作しませんが...
const float c = 1.0;
1.19209e-07 の正解が得られます。
問題の根源はバグのある VS 2008 コンパイラであるという私の正しさを誰か教えてください (あなたのマシンでバグを確認できますか?)。また、新しいバージョンの MS VS 2010 でケースをテストしていただければ幸いです。ありがとうございます。
アップデート。 MS Visual Studio 2013 では、私が言及した最初のプログラムは予期しない結果なしで動作します。float と double に対して適切な答えが得られます。これをすべての浮動小数点モデル (正確、厳密、高速) で確認しましたが、何も変わりませんでした。したがって、この場合、VS 2008 にはバグがあったようです。