llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
27.9k stars 11.51k forks source link

std::complex seems to use unsafe floating point math by default #22615

Open llvmbot opened 9 years ago

llvmbot commented 9 years ago
Bugzilla Link 22241
Version 3.4
OS Linux
Reporter LLVM Bugzilla Contributor
CC @chandlerc,@DougGregor,@hfinkel,@rotateright

Extended Description

Hi.

Consider the following code:

include

include

include

int main() { std::complex c(-61.887073591767951,-60.052083270252012); double a = (1.0 / c).real();

std::cout << std::setprecision(17) << " " << a << std::endl;

}

With g++ and gfortran (with corresponding Fortran code) and Octave (ditto), this outputs:

-8.3223357032193145 (ends with 45)

However, clang++ outputs:

-0.0083223357032193128 (ends with 28)

The thing is, when compiling with g++ -ffast-math, I get the same as I get in clang++ (without any optimization or -ffast-math).

So: "g++ -ffast-math complex.cpp" gives the same output as "clang++ complex.cpp"

I'm on Fedora 21 with clang++ --version:

clang version 3.4.2 (tags/RELEASE_34/dot2-final) Target: x86_64-redhat-linux-gnu Thread model: posix

llvmbot commented 9 years ago

I have tried this using clang 3.6.0-RC3 and that version gives -0.0083223357032193145, whereas 3.5.1 gave -0.0083223357032193128

This is independent of the employed optimization level (-O0 to -Ofast). I don't know, what has changed, but wanted to share my observation.

rotateright commented 9 years ago

Thanks, Chandler!

I think I at least understand the difference in gcc results via: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64677

I'm still not sure which answer (if any) is wrong or how to prove which answer is better.

chandlerc commented 9 years ago

While I've not looked into this specifically, please note that the C standard provides special rules for the mathematical operations between a real and complex number. Because std::complex is implemented in terms of C _Complex numbers, those rules actually govern how we implement these operations.

I'm not sure that this is relevant here, but it is one of the more subtle aspects of determining what the "correct" result is in the case of mixed real/complex arithmetic.

rotateright commented 9 years ago

clang and gcc have the same bit representation for the input values:

clang: movabsq $-4589465388246309647, %rax ## imm = 0xC04EF18BA0A0E4F1 movabsq $-4589723640175316136, %rax ## imm = 0xC04E06AAAA233358

gcc: .long 2694898929 = 0xA0A0E4F1 .long -1068568181 = 0xC04EF18B .long 2854433624 = 0xAA233358 .long -1068628310 = 0xC04E06AA

Interestingly, "gcc -O1" precomputes the "wrong" result. There's no math at all in that binary. Clang apparently doesn't have that optimization because it has to do lots of math ops to arrive at the resulting:

0xbf810b4cfd5f8f56 (-0.0083223357032193128)

The supposedly correct result from gcc -O0 is 1 ULP greater: 0xbf810b4cfd5f8f57 (-0.0083223357032193145)

rotateright commented 9 years ago

gcc 4.8.2 returns the "wrong" (not sure yet if it's really wrong or not) output at -O1 too. So either gcc is also doing something wrong (-O1 shouldn't enable any kind of fast-math AFAIK) or something else is wrong here.

llvmbot commented 9 years ago

NOTE: I messed up leaving e-3 for the first output!