1. ホーム
  2. c++

[解決済み] 最適化を有効にすると浮動小数点演算の結果が異なる - コンパイラのバグ?

2022-10-04 12:12:39

質問

以下のコードは、Visual Studio 2008で最適化ありでもなしでも動作します。しかし、それは唯一の最適化(O0)せずにg + +上で動作します。

#include <cstdlib>
#include <iostream>
#include <cmath>

double round(double v, double digit)
{
    double pow = std::pow(10.0, digit);
    double t = v * pow;
    //std::cout << "t:" << t << std::endl;
    double r = std::floor(t + 0.5);
    //std::cout << "r:" << r << std::endl;
    return r / pow;
}

int main(int argc, char *argv[])
{
    std::cout << round(4.45, 1) << std::endl;
    std::cout << round(4.55, 1) << std::endl;
}

と出力されるはずです。

4.5
4.6

しかし、最適化されたg++ ( O1 - O3 ) が出力されます。

4.5
4.5

を追加すると volatile をつけるとうまくいくので、何か最適化のバグがあるのでしょうか?

g++ 4.1.2、4.4.4でテストしています。

以下はideoneでの結果です。 http://ideone.com/Rz937

そして、g++でテストするオプションはシンプルです。

g++ -O2 round.cpp

もっと面白いのは /fp:fast をオンにした場合でも、結果は正しく表示されます。

さらなる質問です。

私は疑問に思ったのですが、常に -ffloat-store をオンにする必要があるのでしょうか?

なぜなら、私がテストした g++ のバージョンは で出荷されています。 CentOS / レッドハットリナックス 5 および CentOS/Redhat 6 .

これらのプラットフォームで多くのプログラムをコンパイルしましたが、プログラム内に予期せぬバグが発生しないか心配です。私の C++ コードと使用したライブラリのすべてにそのような問題があるかどうかを調査するのは少し難しいようです。何か提案はありますか?

であっても、なぜ /fp:fast をオンにしても、Visual Studio 2008 が動作するのはなぜか?この問題では、Visual Studio 2008 の方が g++ よりも信頼性が高いような気がしますが?

どのように解決するのですか?

Intel x86 プロセッサは、内部で 80 ビット拡張精度を使用するのに対し double は通常 64 ビット幅です。異なる最適化レベルは、CPU からの浮動小数点値がメモリに保存され、その結果 80 ビット精度から 64 ビット精度に丸められる頻度に影響を及ぼします。

を使用します。 -ffloat-store gccオプションを使用すると、異なる最適化レベルで同じ浮動小数点結果を得ることができます。

別の方法として long double 型は、80ビットから64ビット精度への丸めを避けるために、gccでは通常80ビット幅です。

man gcc がすべてを語っています。

   -ffloat-store
       Do not store floating point variables in registers, and inhibit
       other options that might change whether a floating point value is
       taken from a register or memory.

       This option prevents undesirable excess precision on machines such
       as the 68000 where the floating registers (of the 68881) keep more
       precision than a "double" is supposed to have.  Similarly for the
       x86 architecture.  For most programs, the excess precision does
       only good, but a few programs rely on the precise definition of
       IEEE floating point.  Use -ffloat-store for such programs, after
       modifying them to store all pertinent intermediate computations
       into variables.


x86_64 ビルドでは、コンパイラは SSE レジスタを次のように使用します。 floatdouble をデフォルトで使用することで、拡張精度が使用されず、この問題が発生しないようにしました。

gcc コンパイラオプション -mfpmath はそれを制御します。