Are +0 and -0 the Same

Are +0 and -0 the same?

JavaScript uses IEEE 754 standard to represent numbers. From Wikipedia:

Signed zero is zero with an associated sign. In ordinary arithmetic, −0 = +0 = 0. However, in computing, some number representations allow for the existence of two zeros, often denoted by −0 (negative zero) and +0 (positive zero). This occurs in some signed number representations for integers, and in most floating point number representations. The number 0 is usually encoded as +0, but can be represented by either +0 or −0.

The IEEE 754 standard for floating point arithmetic (presently used by most computers and programming languages that support floating point numbers) requires both +0 and −0. The zeroes can be considered as a variant of the extended real number line such that 1/−0 = −∞ and 1/+0 = +∞, division by zero is only undefined for ±0/±0 and ±∞/±∞.

The article contains further information about the different representations.

So this is the reason why, technically, both zeros have to be distinguished.

However, +0 === -0 evaluates to true. Why is that (...) ?

This behaviour is explicitly defined in section 11.9.6, the Strict Equality Comparison Algorithm (emphasis partly mine):

The comparison x === y, where x and y are values, produces true or false. Such a comparison is performed as follows:

(...)

  • If Type(x) is Number, then

    1. If x is NaN, return false.
    2. If y is NaN, return false.
    3. If x is the same Number value as y, return true.
    4. If x is +0 and y is −0, return true.
    5. If x is −0 and y is +0, return true.
    6. Return false.

(...)

(The same holds for +0 == -0 btw.)

It seems logically to treat +0 and -0 as equal. Otherwise we would have to take this into account in our code and I, personally, don't want to do that ;)


Note:

ES2015 introduces a new comparison method, Object.is. Object.is explicitly distinguishes between -0 and +0:

Object.is(-0, +0); // false

Differences between 0, -0 and +0

"Branch Cuts for Complex Elementary Functions or Much Ado About Nothing's Sign Bit " addresses the reasons for signed zeros. That kind of analysis informed IEEE-754 which is the basis for most modern instruction sets
and programming languages' floating point number behavior.

In brief, many common numeric functions can be continuous with signed zeroes at places where they cannot be with an unsigned zero leading to fewer NaN values and fewer special cases. Division is one such function.



Then, the question is: why is -0 treated separately than 0 and +0?

Just to be clear, there are only two zero values. -0 and +0. The tokens (0) can be substituted for the tokens (+0) wherever they occur without changing semantics.



The interesting fact is that 0 is equal with -0

0 === -0
true

This behavior is mandated by IEEE-754.

To test whether two numeric values are the "same":

function same(x, y) {
if (x === y) {
if (x !== 0) {
return true; // Non-zero values.
} else {
return (1/x === 1/y); // Test signed-ness of zeroes.
}
} else {
return x !== x && y !== y; // Treat NaNs the same
}
}

Differences between 0, -0 and +0

"Branch Cuts for Complex Elementary Functions or Much Ado About Nothing's Sign Bit " addresses the reasons for signed zeros. That kind of analysis informed IEEE-754 which is the basis for most modern instruction sets
and programming languages' floating point number behavior.

In brief, many common numeric functions can be continuous with signed zeroes at places where they cannot be with an unsigned zero leading to fewer NaN values and fewer special cases. Division is one such function.



Then, the question is: why is -0 treated separately than 0 and +0?

Just to be clear, there are only two zero values. -0 and +0. The tokens (0) can be substituted for the tokens (+0) wherever they occur without changing semantics.



The interesting fact is that 0 is equal with -0

0 === -0
true

This behavior is mandated by IEEE-754.

To test whether two numeric values are the "same":

function same(x, y) {
if (x === y) {
if (x !== 0) {
return true; // Non-zero values.
} else {
return (1/x === 1/y); // Test signed-ness of zeroes.
}
} else {
return x !== x && y !== y; // Treat NaNs the same
}
}

Differentiating +0 and -0

In ECMAScript 6 Object.is behaves like === except that it distinguishes positive and negative zeroes, and Object.is(NaN, NaN) evaluates to true. (See here for a writeup.)

Chrome 24 supports Object.is.

Is there a difference between 0 and -0 in Javascript

Interesting! It seems their values are equal--neither is larger than the other, but they are distinct objects with several side effects (including division by 0 or -0 as per Roisin's answer).

Other interesting quirks observed:

const a = 0;
const b = -0;

a == b; // true
a === b; // true

a < b; // false
b < a; // false

Object.is(a, b); // false
Object.is(a, -b); // true

b.toString(); // "0" <-- loses the negative sign

a + b; // 0
b - a; // -0
a * b; // -0

What is the difference between -0 and 0?

No, +0 and -0 are not used in the same way in every computation. For example:

3·(+0) = +0
+0/-3 = -0

I suggest you to read What Every Computer Scientist Should Know About Floating-point arithmetic by David Goldberg, that sheds a light on why +0 and -0 are needed in floating point arithmetic and in which way they differ.

Examples on how +0 ad -0 differ (and why that can be useful when dealing with complex values) can be found in Kahan, W. 1987. Branch Cuts for Complex Elementary Functions, in "The State of the Art in Numerical Analysis" (I wasn't able to find a pdf of this article, you may find one at your local university library).

Is it possible to differentiate between 0 and -0?

It depends on the machine you're targeting.

On a machine that uses a 2's complement representation for integers there's no difference at bit-level between 0 and -0 (they have the same representation)

If your machine used one's complement, you definitely could

0000 0000   -> signed   0 
1111 1111 -> signed −0

Obviously we're talking about using native support, x86 series processors have native support for the two's complement representation of signed numbers. Using other representations is definitely possible but would probably be less efficient and require more instructions.

(As JerryCoffin also noted: even if one's complement has been considered mostly for historical reasons, signed magnitude representations are still fairly common and do have a separate representation for negative and positive zero)

+0 and -0 shows different behavior for int and float data

Ints and floats are pretty different beasts in Java. Ints are encoded as two's complement, which has a single 0 value. Floats use IEEE 754 (the 32-bit variant for floats, and 64-bit for doubles). IEEE 754 is somewhat complex, but for purpose of this answer, you just need to know that it has three sections, the first of which is a sign bit. That means for any float, there's a positive and negative variant¹. That includes 0, so floats actually have two "zero" values, +0 and -0.

As an aside, the two's complement that ints use is not the only way to encode integers in computer science. There are other methods, like ones' complement, but they have quirks — like having both a +0 and -0 as distinct values. ;-)

When you compare float primitives (and doubles), Java treats +0 and -0 as equal. But when you box them, Java treats them separately, as described in Float#equals. This lets the equals method be consistent with their hashCode implementation (as well as compareTo), which just uses the bits of the float (including that signed value) and shoves them as-is into an int.

They could have picked some other option for equals/hashCode/compareTo, but they didn't. I'm not sure what the design considerations there were. But in at least one regard, Float#equals was always going to diverge from the float primitive's ==: In primitives, NaN != NaN, but for all objects, o.equals(o) must also be true. That means that if you had Float f = Float.NaN, then f.equals(f) even though f.floatValue() != f.floatValue().


¹ NaN (not-a-number) values have a sign bit, but it doesn't have any meaning other than for ordering, and Java ignores it (even for ordering).

What do positive and negative mean in ECMAScript? +0 and -0

The terms "positive number" and "negative number" are indeed defined in the ECMAScript spec:

8.5 The Number Type

The Number type has exactly 18437736874454810627 (that is,
264−253+3) values [...]

The 9007199254740990 (that is, 253−2) distinct
“Not-a-Number” values of the IEEE Standard are represented in
ECMAScript as a single special NaN value [...]

There are two other special values, called positive Infinity and
negative Infinity [...]

The other 18437736874454810624 (that is,
264−253) values are called the finite numbers.
Half of these are positive numbers and half are negative
numbers
; for every finite positive Number value there is a
corresponding negative value having the same magnitude.

Therefore,

  • Since +0 and -0 are two of those finite numbers, each must either be positive or negative.
  • Since each finite positive number must have a negative counterpart, either +0 is positive and -0 is negative, or the opposite.
  • It would be too trollish if the positive zero was a negative number, and negative zero was a positive one. So we can (probably) assume that +0 is positive and -0 is negative.

However, according to the following, neither +0 nor -0 can be negative:

5.2 Algorithm Conventions

The mathematical function abs(x) yields the absolute value of x,
which is −x if x is negative (less than zero) and otherwise is
x itself.

In fact, in most cases the spec seems to differentiate the case when a variable is positive or negative from the case when it's zero. For example,

5.2 Algorithm Conventions

The mathematical function sign(x) yields 1 if x is positive
and −1 if
x is negative. The sign function is not used in this standard for cases when x is zero.

Therefore, the spec is contradictory.

Why is 0 === -0 true in JavaScript?

In fact, 0 and -0 are not the same even at the bit level. However, there is a special case implemented for +/-0 so they compare as equal.

The === operator compares by value when applied to primitive numbers.



Related Topics



Leave a reply



Submit