Behaviour of Negative Zero (-0.0) in Comparison with Positive Zero (+0.0)

What operations and functions on +0.0 and -0.0 give different arithmetic results?

There are a few standard operations and functions that form numerically different answers between f(+0.0) and f(-0.0).

Different rounding modes or other floating point implementations may give different results.

#include <math.h>

double inverse(double x) { return 1/x; }

double atan2m1(double y) { return atan2(y, -1.0); }

double sprintf_d(double x) {
char buf[20];
// sprintf(buf, "%+f", x); Changed to e
sprintf(buf, "%+e", x);
return buf[0]; // returns `+` or `-`
}

double copysign_1(double x) { return copysign(1.0, x); }

double signbit_d(double x) {
int sign = signbit(x); // my compile returns 0 or INT_MIN
return sign;
}

double pow_m1(double x) { return pow(x, -1.0); }

void zero_test(const char *name, double (*f)(double)) {
double fzp = (f)(+0.0);
double fzn = (f)(-0.0);
int differ = fzp != fzn;
if (fzp != fzp && fzn != fzn) differ = 0; // if both NAN
printf("%-15s f(+0):%-+15e %s f(-0):%-+15e\n",
name, fzp, differ ? "!=" : "==", fzn);
}

void zero_tests(void) {
zero_test("1/x", inverse);
zero_test("atan2(x,-1)", atan2m1);
zero_test("printf(\"%+e\")", sprintf_d);
zero_test("copysign(x,1)", copysign_1);
zero_test("signbit()", signbit_d);
zero_test("pow(x,-odd)", pow_m1);; // @Pascal Cuoq
zero_test("tgamma(x)", tgamma); // @vinc17 @Pascal Cuoq
}

Output:
1/x f(+0):+inf != f(-0):-inf
atan2(x,-1) f(+0):+3.141593e+00 != f(-0):-3.141593e+00
printf("%+e") f(+0):+4.300000e+01 != f(-0):+4.500000e+01
copysign(x,1) f(+0):+1.000000e+00 != f(-0):-1.000000e+00
signbit() f(+0):+0.000000e+00 != f(-0):-2.147484e+09
pow(x,-odd) f(+0):+inf != f(-0):-inf
tgamma(x) f(+0):+inf != f(-0):+inf

Notes:

tgamma(x) came up == on my gcc 4.8.2 machine, but correctly != on others.

rsqrt(), AKA 1/sqrt() is a maybe future C standard function. May/may not also work.

double zero = +0.0; memcpy(&zero, &x, sizeof x) can show x is a different bit pattern than +0.0 but x could still be a +0.0. I think some FP formats have many bit patterns that are +0.0 and -0.0. TBD.

This is a self-answer as provided by https://stackoverflow.com/help/self-answer.

Why does Java print a negative sign in front of 0 when 0 with no sign is equal and more commonly accepted as correct?

The modulo operator just takes the remainder once you divide a number by that.

Divide 0 by -1 and you get 0, so the result is 0.

Floating points and doubles do actually know the difference between -0 and +0 though, so when you take the remainder of a -0 you get -0 as that is still a valid number between 0 and 1 (or -1).

This is a quirk of the way floating point numbers work, and of the special properties of 0 as the same does not hold true for other numbers:

System.out.println((0.0 % -1)+","+(-0.0 % 1)+","+ (-0.0 % -1));

System.out.println((0 % -1)+","+(-0 % 1)+","+ (-0 % -1));

System.out.println((3 % -1)+","+(-3 % 1)+","+ (-3 % -1));

Displays:

0.0,-0.0,-0.0 
0,0,0
0,0,0

Since references were requested:

Floating points are defined in IEEE_754-1985:

http://en.wikipedia.org/wiki/IEEE_754-1985

There is a whole wikipedia page discussing Negative Zero:

http://en.wikipedia.org/wiki/Negative_zero

This also at least partly explains why the modulo works as:

According to the IEEE 754 standard, negative zero and positive zero should compare as equal with the usual (numerical) comparison operators, like the == operators of C and Java.

Since modulo produces a number >= to 0 and < than the given value then -0 already satisfies the >= requirement (since -0 == 0) and the operation can just end immediately.

negative zero in python

Check out −0 (number) in Wikipedia

Basically IEEE does actually define a negative zero.

And by this definition for all purposes:

-0.0 == +0.0 == 0

I agree with aaronasterling that -0.0 and +0.0 are different objects. Making them equal (equality operator) makes sure that subtle bugs are not introduced in the code.

Think of a * b == c * d

>>> a = 3.4
>>> b =4.4
>>> c = -0.0
>>> d = +0.0
>>> a*c
-0.0
>>> b*d
0.0
>>> a*c == b*d
True
>>>

[Edit: More info based on comments]

When I said for all practical purposes, I had chosen the word rather hastily. I meant standard equality comparison.

As the reference says, the IEEE standard defines comparison so that +0 = -0, rather than -0 < +0. Although it would be possible always to ignore the sign of zero, the IEEE standard does not do so. When a multiplication or division involves a signed zero, the usual sign rules apply in computing the sign of the answer.

Operations like divmod and atan2 exhibit this behavior. In fact, atan2 complies with the IEEE definition as does the underlying "C" lib.

>>> divmod(-0.0,100)
(-0.0, 0.0)
>>> divmod(+0.0,100)
(0.0, 0.0)

>>> math.atan2(0.0, 0.0) == math.atan2(-0.0, 0.0)
True
>>> math.atan2(0.0, -0.0) == math.atan2(-0.0, -0.0)
False

One way is to find out through the documentation, if the implementation complies with IEEE behavior . It also seems from the discussion that there are subtle platform variations too.

However this aspect (IEEE definition compliance) has not been respected everywhere. See the rejection of PEP 754 due to disinterest! I am not sure if this was picked up later.

See also What Every Computer Scientist Should Know About Floating-Point Arithmetic.

Java - comparison of positive and negative zeros

This behaviour is actually documented:

If d1 represents +0.0 while d2 represents -0.0, or vice versa, the equal test has the value false, even though +0.0==-0.0 has the value true. This definition allows hash tables to operate properly.

How can a primitive float value be -0.0? What does that mean?

Because Java uses the IEEE Standard for Floating-Point Arithmetic (IEEE 754) which defines -0.0 and when it should be used.

The smallest number representable has no 1 bit in the subnormal significand and is called the positive or negative zero as determined by the sign. It actually represents a rounding to zero of numbers in the range between zero and the smallest representable non-zero number of the same sign, which is why it has a sign, and why its reciprocal +Inf or -Inf also has a sign.

You can get around your specific problem by adding 0.0

e.g.

Double.toString(value + 0.0);

See: Java Floating-Point Number Intricacies

Operations Involving Negative Zero
...

(-0.0) + 0.0 -> 0.0

-

"-0.0" is produced when a floating-point operation results in a negative floating-point number so close to 0 that it cannot be represented normally.

How can negative zero be tested for in PHP?

@njuffa suggested if (1 / $x === -INF), which works but throws a division by zero warning.

@Manu-sh modified it to do the same thing without the warning by using the new "power of" operator: **

<?php
function is_negative_zero($x) {
return $x ** -1 === -INF;
}

echo is_negative_zero(0.0)? "Yes": "No";
echo PHP_EOL;

echo is_negative_zero(-0.0)? "Yes": "No";
echo PHP_EOL;

Output:

No
Yes

Any reason why 0.0/0.0 makes negative nan in C++?

As far as the C++ language is concerned, the behaviour of the program is undefined. So, from C++ perspective there is no "reason" for anything about the behaviour of the program.

According to the IEEE-754 floating point standard, assuming your hardware and language implementation conform to it (they probably do), result of such division is NaN. IEEE-754 does not specify the sign of NaN for any operation. So from IEEE-754's perspective, the answer is: It just happened to be so on your system for whatever reason the language / hardware implementation has chosen (or simply for no particular reason).

In conclusion: You should never rely on sign of NaN being anything in particular. And you should generally avoid division by zero.

Negative zero literal in golang

There is a registered issue.

And it happens to give a kind of solution :

a := math.Copysign(0, -1)

It's not so bad as it obviously refers to the standard copysign function defined by IEEE754.

But this means you need to import a package and this still looks much too heavy for the (admittedly minor and rare) need.



Related Topics



Leave a reply



Submit