Floating point arithmetic varies between g++ and c

2019-07-23 15:30发布

I have come across a bug that seems to be platform dependent. I am getting different results for clang++ and g++ however only on my 32-Debian Machine. I was always under the impression that IEEE 754 was standardized and that all compilers that abide by the standard would have the same behavior. Please let me know if I am wrong, I am just very confused about this. Also, I realize that depending on floating point comparison is generally not a good idea.

#define DEBUG(line) std::cout <<"\t\t" << #line << " => " << line << "\n";
#include <iostream>
int main() {
    double x = 128.0, y = 255.0;
    std::cout << "\n";
    DEBUG(  x/y)
    DEBUG(  ((x/y) == 128.0/255.0)) 
    DEBUG(  (128.0/255.0)   )
    DEBUG(  ((x/y)-(x/y)))
    DEBUG(  ((x/y)-(128.0/255.0))   )  
    DEBUG(  ((128.0/255.0)-0.501961) ) 
    std::cout << "\n";  
    return 0;
}

And here is my output

[~/Desktop/tests]$ g++ float_compare.cc -o fc
[~/Desktop/tests]$./fc

        x/y => 0.501961
        ((x/y) == 128.0/255.0) => 0
        (128.0/255.0) => 0.501961
        ((x/y)-(x/y)) => 0
        ((x/y)-(128.0/255.0)) => 6.9931e-18
        ((128.0/255.0)-0.501961) => -2.15686e-07

[~/Desktop/tests]$clang++ float_compare.cc -o fc
[~/Desktop/tests]$./fc

        x/y => 0.501961
        ((x/y) == 128.0/255.0) => 1
        (128.0/255.0) => 0.501961
        ((x/y)-(x/y)) => 0
        ((x/y)-(128.0/255.0)) => 0
        ((128.0/255.0)-0.501961) => -2.15686e-07

1条回答
Rolldiameter
2楼-- · 2019-07-23 16:08

The Standard allows intermediate results to use extended precision, even in full compliance mode (which many compilers aren't in by default). It says in [basic.fundamental]:

This International Standard imposes no requirements on the accuracy of floating-point operations

For your particular situation comparing g++ and clang, see https://gcc.gnu.org/wiki/FloatingPointMath

Without any explicit options, GCC assumes round to nearest or even and does not care about signalling NaNs.

also

For legacy x86 processors without SSE2 support, and for m68080 processors, GCC is only able to fully comply with IEEE 754 semantics for the IEEE double extended (long double) type. Operations on IEEE double precision and IEEE single precision values are performed using double extended precision. In order to have these operations rounded correctly, GCC would have to save the FPU control and status words, enable rounding to 24 or 53 mantissa bits and then restore the FPU state. This would be far too expensive.

Since extended precision is different between SSE (64-bit) and x87 (80-bit), the results of compile-time computations may depend not only on the compiler and its version, but also what flags the compiler was built with.

The way you know that IEEE 754 rules are not in effect is by checking numeric_limits<T>::is_iec559.

查看更多
登录 后发表回答