bugfix> c++ > 投稿
#include <cstdio>
int main(void)
{
    int val = 500;
    printf("%d\n", (int)((long double)val / 500));
    printf("%d\n", (int)((long double)500 / 500));
}

明らかにそれは出力する必要があります 1 1 。しかし、あなたがそれをコンパイルすると -Ofast 、出力します 0 1 、 なぜ?

そして、あなたが変更した場合 500 他の値(など 400 )そしてコンパイル -Ofast 、それでも出力されます 1 1

コンパイラエクスプローラー -Ofast :https://gcc.godbolt.org/z/YkX7fB

この行が問題を引き起こしているようです。

回答 3 件
  • -Ofast-ffast-math が有効になっていると、一部の操作が別のより高速な方法で計算される可能性があります。あなたの場合、 (long double)val / 500) 次のように計算できます (long double)val * (1.0L / 500)) 。これは、比較すると、生成されたアセンブリで確認できます。 -O2 そして -Ofast 次の機能の場合:

    long double f(long double a)
    {
        return a / 500.0L;
    }
    
    

    で生成されたアセンブリ -O2 関与する fdiv 命令、アセンブリはで生成されます -Ofast 関与する fmul 手順については、https://gcc.godbolt.org/z/58VHxbを参照してください。

    次に、1/500、つまり0.002は、で表すことができません。 long double 丁度。したがって、いくつかの丸めが発生し、あなたの場合、この丸めはたまたまダウンしているようです。これは、次の式で確認できます。

    500.0L * (1.0L / 500.0L) < 1.0L
    
    

    これは次のように評価されます true :https://gcc.godbolt.org/z/zMcjxJ。したがって、正確に格納されている乗数は0.002-です。いくつかの非常に小さなデルタ

    最後に、乗算の結果は500 *(0.002-デルタ)= 1-いくつかの小さな値。そして、この値がに変換されたとき int 、切り捨てられるため、結果は int は0です。

  • -Ofast

    Disregard strict standards compliance. -Ofast すべてを有効にします -O3 最適化。また、すべての標準準拠プログラムに有効ではない最適化も可能になります。オンになります -ffast-math-fallow-store-data-races およびFortran固有の[...]

    -ffast-math

    Sets the options -fno-math-errno-funsafe-math-optimizations-ffinite-math-only-fno-rounding-math-fno-signaling-nans-fcx-limited-range そして -fexcess-precision=fast

    このオプションにより、プリプロセッサマクロが発生します __FAST_MATH__ 定義します。

    このオプションは誰によってもオンにされません -O ほかのオプション -Ofast IEEEまたはISOの規則/数学関数の仕様の正確な実装に依存するプログラムの出力が正しくなくなる可能性があるためです。ただし、これらの仕様の保証を必要としないプログラムでは、より高速なコードが生成される可能性があります。

    結論:使用しないでください -ffast-math あなたが今得たような驚きを喜んで受けない限り。

  • 表示されているプログラムスニペットに「問題」がある場合でも、浮動小数点数を操作するのは間違った方法です。

    多かれ少なかれ、浮動小数点数に「正確な値」があるかどうかをプログラムに尋ねます。この場合は「1」です。わかりました。正確に言うと、値が「<1」または「>= 1」で、「1付近」の値の場合は、2つの回答の分割限界付近です。しかし、他の人がすでに書いているように(または、ウィキペディアで簡単に見つけることができます...)、浮動小数点数の精度は限られています。したがって、そのような逸脱は発生する可能性があり、発生する可能性があります。

    したがって、結論に達すると、浮動小数点から整数への変換を行うときは常に丸めを使用する必要があります。つまり、 '(int)round(floating_point_value)'です。

あなたの答え