Bugzilla – Bug 216
[llvmgcc] floating-point unary minus is incorrect for +0.0
Last modified: 2004-02-03 20:41:09
You need to log in before you can comment on or make changes to this bug.
LLVM does not have unary operators, so unary minus is emulated as "0 - x". This is not correct for IEEE floating-point numbers. In particular, the negation of "0.0" should be "-0.0", not "0.0", because positive and negative zeroes are distinct in IEEE. I will attach a test program that illustrates this difference. I don't know whether LLVM's treatment is also incorrect for NaN's and Inf's... On the PowerPC, this could be easily fixed by emulating floating-point unary minus as "-0.0 - x" (rather than "+0.0 - x"). However, (a) I don't know whether this also works for NaN's and Inf's, and (b) I don't know whether this works in general on other processors. You may need to rethink the decision to omit true unary operators from the LLVM IR. You could argue that IEEE floating-point is not a strict requirement of the C/C++ standards, but in practice that is what people expect (and get, from gcc).
Created an attachment (id=68) [details] test case (minus.c) Compile this and run with arguments (e.g.): 3.14 -3.14 0.0 -0.0 Using gcc, all 4 tests say "ok". With llvm-gcc, one of the tests (0.0) fails.
You are absolutely correctly: this needs to be addressed. There are also other missing pieces, such as support for unordered comparisons, though that is less glaring of an ommission. My current plan is for the Add,Sub,Mul,Div, & Rem operators to all indicate "non-strict" or "fast" operators when used on floating point operands. This would mean, for example, that all of the LLVM transformations could treat Add/Mul as if there were associative, regardless of whether the operands were floating point or not. To support strict floating point, new operators would be added 'addfp', 'negfp', ... ? Which would be required to support IEEE semantics precisely. I think that this is a good balance between supporting things like non-strict FP in Java, GCC's -ffast-math, etc... while still maintaining good support for numerical programming. Do you think this would solve the problem as you see it? If so, do you want to come up with a minimal recommended list of operators to add? -Chris ps, on X86 at least, GCC does not come close to providing "what people expect" from IEEE math, even with -ffloat-store. :( Not that that is a reason for LLVM to be as bad. At least the LLVM register allocation decisions cannot effect results.
The more I read up about this, the more I believe that compiling negation into '-0.0 - X' is correct for all IEEE machines. Unfortunately, I can't find proof of this on google. I'm inclined to change the C front-end in the meantime, as +0.0 - X certainly isn't correct. What do you think, is there anyone that you know who might know for certain? -Chris
I surreptitiously started a thread on the GCC list about this topic, and got the following response from Geoff Keating: """I looked this up. '-0.0 - X' is the same as '-0.0 + (-X)'. When -X != 0.0, the result will be -X, so that's OK. Suppose X is +/- 0.0. Then the following rules apply: - If both operands have the same sign, the sign of the result is the same as the sign of the operands. So if X is +0.0, you'll get -0.0, the same as -X. - If the sum of two operands with opposite sign is exactly zero, the sign is positive in all rounding modes except round towards -Inf, in which case the sign is negative. So, if X is -0.0, you'll get +0.0, the same as -X, *unless* we're rounding towards -Inf. Thus, the answer to your question is "no". The case that differs is when X is -0.0 and the rounding mode is towards -Inf.""" Which I interpret to mean "yes", it's the same in all cases except when the rounding mode is tweaked to be -Inf, which is not the default. As this is the case, I've commited the following patch to the CFE: http://mail.cs.uiuc.edu/pipermail/llvm-commits/Week-of-Mon-20040202/011060.html Note that will still will eventually need "strict" vs "non-strict" floating point operators, particularly to support languages like Fortran and Java well. -Chris
The proposed patch does NOT fix the bug. Specifically, the test case still fails with argument "0.0". It looks like the front end is now getting it right: % llvm-gcc -S minus.c % fgrep 'sub double' minus.s %tmp.1 = sub double -0.0, %tmp.0 ; ty=double So far, so good. But: % llvm-gcc -c -Wa,-disable-opt minus.c % llvm-dis -o - minus.o | fgrep 'sub double' %tmp.1 = sub double 0x0, %tmp.0 ; <double> [#uses=1]
It works for me: $ llvmgcc test4.c -c -o - | lli - 0.0 WARNING: Cannot resolve fn '__main' using a dummy noop function instead! x = 0.000000 -x = -0.000000 ok $ llvmgcc test4.c -c -o - | llvm-dis | grep 'sub' %tmp.1 = sub double 0x8000000000000000, %x ; <double> [#uses=1] Make sure that your tree includes the patches for Bug 218, which is related to -0.0 handling. If this helps, please reclose the bug. -Chris
So sorry! I didn't realize I also had to install those other patches. D'oh... Yes, it works. Reclosed.