Does float have a negative zero? (-0f)
Asked Answered
A

10

50

IEEE floating point numbers have a bit assigned to indicate the sign, which means you can technically have different binary representations of zero (+0 and -0). Is there an arithmetic operation I could do for example in C which result in a negative zero floating point value?

This question is inspired by another which called into question whether you can safely compare 0.0f using ==, and I wondered further if there is are other ways to represent zero which would cause float1 == 0.0f to break for seemingly perfectly equal values.

[Edit] Please, do not comment about the safety of comparing floats for equality! I am not trying to add to that overflowing bucket of duplicate questions.

Abstemious answered 23/2, 2011 at 19:25 Comment(3)
Even more interesting is NaN. NaN compares not equal to all values, including itself!Clypeus
"Is there an arithmetic operation I could do for example in C which result in a negative zero floating point value?" - Possibly C is a bad example, since C doesn't guarantee that its floating-point types are IEEE floats. As far as the C standard is concerned there's no portable way to guarantee generating a negative zero, but there are operations for which the implementation is permitted to generate a negative zero. There's also a way for an implementation to advertise that its floats in fact are IEEE floats, in which case game on.Kristoforo
Please provide link to "another question whether you can safely compare 0.0f using =="Adamski
B
39

According to the standard, negative zero exists but it is equal to positive zero. For almost all purposes, the two behave the same way and many consider the existence of a negative to be an implementation detail. There are, however, some functions that behave quite differently, namely division and atan2:

#include <math.h>
#include <stdio.h>

int main() {
    double x = 0.0;
    double y = -0.0;
    printf("%.08f == %.08f: %d\n", x, y, x == y);
    printf("%.08f == %.08f: %d\n", 1 / x, 1 / y, 1 / x == 1 / y);
    printf("%.08f == %.08f: %d\n", atan2(x, y), atan2(y, y), atan2(x, y) == atan2(y, y));
}

The result from this code is:

0.00000000 == -0.00000000: 1
1.#INF0000 == -1.#INF0000: 0
3.14159265 == -3.14159265: 0

This would mean that code would correctly handle certain limits without a need for explicit handling. It's not certain that relying on this feature for values close to the limits is a good idea, since a simple calculation error can change the sign and make the value far from correct, but you can still take advantage of it if you avoid calculations that would change the sign.

Bulbil answered 23/2, 2011 at 19:45 Comment(1)
Can you cite a standard reference?Cupidity
M
17

Is there an arithmetic operation I could do for example in C which result in a negative zero floating point value?

Sure:

float negativeZero = -10.0e-30f * 10.0e-30f;

The mathematically precise result of the multiplication is not representable as a floating-point value, so it rounds to the closest representable value, which is -0.0f.

The semantics of negative zero are well defined by the IEEE-754 standard; the only real observable way in which its behavior differs from that of zero in arithmetic expression is that if you divide by it, you will get a different sign of infinity. For example:

1.f /  0.f --> +infinity
1.f / -0.f --> -infinity

Comparisons and addition and subtraction with -0.f give the same result as they would with +0.f (in the default rounding mode). Multiplication can preserve the sign of zero, but as noted, it generally isn't observable.

There are some math library functions whose behavior can vary depending on the sign of zero. For example:

copysignf(1.0f, 0.0f) -->  1.0f
copysignf(1.0f,-0.0f) --> -1.0f

This is more common in the complex functions:

csqrtf(-1.0f + 0.0f*i) --> 0.0f + 1.0f*i
csqrtf(-1.0f - 0.0f*i) --> 0.0f - 1.0f*i

In general, however, you shouldn't need to worry about negative zero.

Marshamarshal answered 23/2, 2011 at 19:50 Comment(1)
Wouldn't it be better if the expression float negativeZero = -10.0e-30f * 10.0e-30f; rounded of to 0.0f and instead of -0.0f. If both positive and negative zero are same why not eliminate this ambiguity and keep only positive zero ? Another simple way of getting negative zero-: float neg=-0.00000000000000000001; printf("%f",neg);Paulpaula
D
8

Yes zero can be signed but the standard requires positive and negative zero to test as equal

Driskill answered 23/2, 2011 at 19:28 Comment(0)
S
5

There are a couple of simple arithmetic operations that result in a negative zero answer (at least on the i386/x64/ARMv7/ARMv8 systems I tested it on) :

  • -1 * 0
  • 0 / -1

These caught me by surprise when I was writing an optimiser to simplify arithmetic expressions. Optimising "a = b * 0" to "a = 0" will result in the wrong answer (+0) if b happens to be negative (correct answer is -0).

Skedaddle answered 5/4, 2016 at 9:37 Comment(1)
Off-topic. But there's another reason you should not optimize a = b * 0 into a = 0: Assuming IEEE 754 arithmetic, if b = Infinity, then "Infinity × 0" results in NaN, not 0.Obey
C
2

Yes, float does have a negative zero, but no, you don't have to worry about this when comparing floating-point values.

Floating-point arithmetic is defined to work correctly on special cases.

Canicular answered 23/2, 2011 at 19:29 Comment(11)
Many compilers turn == on FP values into memory compare.Clypeus
@David: if they don't take account of negative zero in doing so, that should be reported as a bug to the compiler vendor.Marshamarshal
@Stephen depends on which language we are talking about. Some make no claims about standards, some languages aren't based on standards!Clypeus
@David: Sure, but those languages don't count. =PMarshamarshal
@Stephen Ha ha! They count for me - I write an FE code (very successful, used by all major oil cos. and their contractors) in just such a language! Do you pity me?!!Clypeus
@David: The fortran standard body generally takes the position that you do whatever is fastest (this viewpoint is not conducive to rigorous engineering, but that doesn't seem to have dissuaded many people from using it, sadly). On modern hardware, a floating-point comparison is usually as fast as or faster than a comparison on encodings, so a good modern fortran compiler should use the hardware FP comparisons, and not raise the issue you suggest (which isn't to say that they didn't do this in the past).Marshamarshal
@Stephen It's not Fortran! We wouldn't be successful if we'd used Fortran! The f2c header file says it best, "/* f2c.h -- Standard Fortran to C header file / /* barf [ba:rf] 2. "He suggested using FORTRAN, and everybody barfed." - From The Shogakukan DICTIONARY OF NEW ENGLISH (Second edition) */ " No, I don't care for Fortran, although John Backus is an all-time hero of mine.Clypeus
@David: I'm shocked to find someone doing FE modeling and not using fortran, so I admit my curiosity: what language/compiler are you using that doesn't respect IEEE-754?Marshamarshal
@Stephen Object Pascal, Delphi to be specific. This issue with compare against 0 is the only thing that I'm aware of that's not IEEE754 because of course, the FP values map directly to the hardware. It's never an issue for us because we only test for equality with 0 in an handful of places, and then only to take an optimised branch to save calculation and so it doesn't affect correctness. The reason we use Delphi is because we like our software to be usable! We loath Fortran with a passion that verges on fanatical!Clypeus
@David: Interesting. I still think that behavior is worth registering a complaint with your compiler vendor, especially if they manage to be IEEE-754 compliant in other regards. Is there some strange historical reason for Delphi taking that approach?Marshamarshal
@Stephen I don't know the reason behind it. I know that back in the day Borland never concentrated on FP codegen. The optimiser does nothing with FP code. If you go back far enough then Turbo Pascal ran on machines without co-processors (remember them!) I doubt raising it with them would have an effect. I can even get them to fix gross bugs like Tanh(x) failing for large abs(x) due to use of naive Sinh/Cosh formula! I mostly just fix their bugs by hooking out their RTL functions.Clypeus
B
1

Yes, floats have negative zero just like other IEEE floating point types such as double (on systems with IEEE floating point). There is an example here in Octave of how to create them; the same operations work in C. The == operator treats +0 and -0 the same, though, and so negative zeros do not break that type of comparison.

Booker answered 23/2, 2011 at 19:28 Comment(2)
is that the Octave == operator to which you refer?Clypeus
@David: The C ==, but Octave should do the same.Booker
G
1

You should exercise caution when doing equality comparisons using floats. Remember, you're trying to represent a decimal value in a binary system.

Is it safe to check floating point values for equality to 0?

If you must compare floating point values I would suggest you use some kind of tolerance that is acceptable to you float1 <= toleranceVal && float1 >= toleranceVal2 or multiply by some factor of ten and cast as an integer. if (!(int)(float1 * 10000)) { .. some stuff .. }

Gurglet answered 23/2, 2011 at 19:41 Comment(1)
decimal/binary is not the issue, its representability/precision which is not the same thingClypeus
C
1

-lm has the signbit() function available to indicate if a value is negative (including -0)

Cylix answered 4/2, 2013 at 0:4 Comment(0)
I
0

this float1 == 0.0f is never really a safe comparison.

if you have something like

float x = 0.0f;
for (int i = 0; i < 10; i++) x += 0.1f;
x -= 1.0f;
assert (x == 0.0f);

it will fail even though it is seemingly supposed to be 0.

Inspirational answered 23/2, 2011 at 19:29 Comment(1)
this answer is relevant to the question that inspired mine. Not mine though.Abstemious
L
0

Yes you can have a +0 and -0 and those are different bit patterns (should fail the equality test). You should never use == with float, certainly not IEEE float. < or > are fine. There are many other SO questions and discussions on this topic, so I wont get into it here.

Loewe answered 23/2, 2011 at 19:33 Comment(7)
+0 and -1? you didn't mean that I thinkClypeus
-1 Equality goes further than comparing bits. And, in fact, -0 and +0 are defined to be equal.Avalokitesvara
fair enough, a quick sample program confirms that gcc produces equality between -0 and +0. Doesnt mean you cant have a -0, the original question, and doesnt mean that it is a good idea to use an equal comparison with float.Loewe
do you have a reference where -0 and +0 are defined to be equal? thanks.Loewe
Although negative zero and positive zero are generally considered equal for comparison purposes, some programming language relational operators and similar constructs might or do treat them as distinct.Loewe
(except that negative zero and positive zero should be considered equal)Loewe
I dont know where my copy of the spec is, I see it referenced that they are defined as equal. As we all know though very few if any processors or systems (processor + compiler + os) actually meet the IEEE 754 standard, so I still recommend not putting a lot of faith in the equals sign for float even if there is one special case where it works. And to the original poster, yes + and - zero are defined in the standard as are + and - infinity. And from time to time your math will result in a negative zero. (+/- infinity are really easy to come by)Loewe

© 2022 - 2024 — McMap. All rights reserved.