Number function is considering -0 as 0 in javascript [duplicate] - javascript

Reading through the ECMAScript 5.1 specification, +0 and -0 are distinguished.
Why then does +0 === -0 evaluate to true?

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
If x is NaN, return false.
If y is NaN, return false.
If x is the same Number value as y, return true.
If x is +0 and y is −0, return true.
If x is −0 and y is +0, return true.
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

I'll add this as an answer because I overlooked #user113716's comment.
You can test for -0 by doing this:
function isMinusZero(value) {
return 1/value === -Infinity;
}
isMinusZero(0); // false
isMinusZero(-0); // true

I just came across an example where +0 and -0 behave very differently indeed:
Math.atan2(0, 0); //returns 0
Math.atan2(0, -0); //returns Pi
Be careful: even when using Math.round on a negative number like -0.0001, it will actually be -0 and can screw up some subsequent calculations as shown above.
Quick and dirty way to fix this is to do smth like:
if (x==0) x=0;
or just:
x+=0;
This converts the number to +0 in case it was -0.

2021's answer
Are +0 and -0 the same?
Short answer: Depending on what comparison operator you use.
Long answer:
Basically, We've had 4 comparison types until now:
‘loose’ equality
console.log(+0 == -0); // true
‘strict’ equality
console.log(+0 === -0); // true
‘Same-value’ equality (ES2015's Object.is)
console.log(Object.is(+0, -0)); // false
‘Same-value-zero’ equality (ES2016)
console.log([+0].includes(-0)); // true
As a result, just Object.is(+0, -0) makes difference with the other ones.
const x = +0, y = -0; // true -> using ‘loose’ equality
console.log(x === y); // true -> using ‘strict’ equality
console.log([x].indexOf(y)); // 0 (true) -> using ‘strict’ equality
console.log(Object.is(x, y)); // false -> using ‘Same-value’ equality
console.log([x].includes(y)); // true -> using ‘Same-value-zero’ equality

In the IEEE 754 standard used to represent the Number type in JavaScript, the sign is represented by a bit (a 1 indicates a negative number).
As a result, there exists both a negative and a positive value for each representable number, including 0.
This is why both -0 and +0 exist.

Answering the original title Are +0 and -0 the same?:
brainslugs83 (in comments of answer by Spudley) pointed out an important case in which +0 and -0 in JS are not the same - implemented as function:
var sign = function(x) {
return 1 / x === 1 / Math.abs(x);
}
This will, other than the standard Math.sign return the correct sign of +0 and -0.

We can use Object.is to distinguish +0 and -0, and one more thing, NaN==NaN.
Object.is(+0,-0) //false
Object.is(NaN,NaN) //true

I'd blame it on the Strict Equality Comparison method ( '===' ).
Look at section 4d
see 7.2.13 Strict Equality Comparison on the specification

If you need sign function that supports -0 and +0:
var sign = x => 1/x > 0 ? +1 : -1;
It acts as Math.sign, except that sign(0) returns 1 and sign(-0) returns -1.

There are two possible values (bit representations) for 0. This is not unique. Especially in floating point numbers this can occur. That is because floating point numbers are actually stored as a kind of formula.
Integers can be stored in separate ways too. You can have a numeric value with an additional sign-bit, so in a 16 bit space, you can store a 15 bit integer value and a sign-bit. In this representation, the value 1000 (hex) and 0000 both are 0, but one of them is +0 and the other is -0.
This could be avoided by subtracting 1 from the integer value so it ranged from -1 to -2^16, but this would be inconvenient.
A more common approach is to store integers in 'two complements', but apparently ECMAscript has chosen not to. In this method numbers range from 0000 to 7FFF positive. Negative numbers start at FFFF (-1) to 8000.
Of course, the same rules apply to larger integers too, but I don't want my F to wear out. ;)

Wikipedia has a good article to explain this phenomenon: http://en.wikipedia.org/wiki/Signed_zero
In brief, it both +0 and -0 are defined in the IEEE floating point specifications. Both of them are technically distinct from 0 without a sign, which is an integer, but in practice they all evaluate to zero, so the distinction can be ignored for all practical purposes.

Related

Floating point numbers - no arithmetic operations and precision errors

Can I ever run into any floating number precision errors if I don't perform any arithmetic operations on the floats? The only operations I do with numbers in my program are limited to the following:
Getting numbers as strings from a web service and converting them to floats using parseFloat()
Comparing resulting floats using <= < == > >=
Example:
const input = ['1000.69', '1001.04' /*, ... */]
const x = parseFloat(input[0])
const y = parseFloat(input[1])
console.log(x < y)
console.log(x > y)
console.log(x == y)
As for parseFloat() implemetation, I'm using latest Node.js.
The source of floats is prices in USD as strings, always two decimals.
As long as the source of your floats is reliable, your checks are safe, yes.
I'd still round them to an acceptable decimal number after the parsing, just to be 100% safe.
As the MDN docs show in one of their examples
// these all return 3.14
parseFloat(3.14);
parseFloat('3.14');
parseFloat(' 3.14 ');
parseFloat('314e-2');
parseFloat('0.0314E+2');
parseFloat('3.14some non-digit characters');
parseFloat({ toString: function() { return "3.14" } });
//and of course
parseFloat('3.140000000') === 3.14
The parseFloat operation converts a string into it's number value. The spec says:
In this specification, the phrase “the Number value for x” where x represents an exact real mathematical quantity (which might even be an irrational number such as π) means a Number value chosen in the following manner. Consider the set of all finite values of the Number type, with -0 removed and with two additional values added to it that are not representable in the Number type, namely 2ℝ1024ℝ (which is +1ℝ × 2ℝ53ℝ × 2ℝ971ℝ) and -2ℝ1024ℝ (which is -1ℝ × 2ℝ53ℝ × 2ℝ971ℝ). Choose the member of this set that is closest in value to x.
That reads as if two same strings are always converted to the same closest number. Except for NaN, two same numbers are equal.
6.1.6.1.13 Number::equal ( x, y )
If x is NaN, return false.
If y is NaN, return false.
If x is the same Number value as y, return true.
If x is +0 and y is -0, return true.
If x is -0 and y is +0, return true.
Return false.
emphasis mine

-0 in JavaScript after using Math.round? What is -0?

I just wanted to generate random integer number in range <-1,1>, that is -1, 0 or 1. I think this code nails it:
Math.round(Math.random()*2-1)
But I'm getting one more value apart from -1, 0 and 1. I'm not sure what is that supposed to be:
Now I don't think this is implementation error, but if it is, I'm using Firefox 41 and same thing happened in Google Chrome 39. When I tried to log -0 in console it appeared as -0 but -0 converted to String appears as "0". What is it? Can I use it for something, like some cool programming hacks and workarounds? Can it cause some errors if I don't just ignore it?
Two Zeros
Because JavaScript’s numbers keep magnitude and sign separate, each nonnegative number has a negative, including 0.
The rationale for this is that whenever you represent a number digitally, it can become so small that it is indistinguishable from 0, because the encoding is not precise enough to represent the difference. Then a signed zero allows you to record “from which direction” you approached zero; that is, what sign the number had before it was considered zero.
from here
More technical stuff if you are interested.
The implementation of -0 and +0 is introduced in 1985 by IEEE as part of the IEEE 754 standard. The standard addressed many problems found in the diverse floating point implementations that made them difficult to use reliably and portably.
The standard defines
arithmetic formats: sets of binary and decimal floating-point data, which consist of finite numbers (including signed zeros and subnormal numbers), infinities, and special "not a number" values (NaNs)
interchange formats: encodings (bit strings) that may be used to exchange floating-point data in an efficient and compact form
rounding rules: properties to be satisfied when rounding numbers during arithmetic and conversions
operations: arithmetic and other operations on arithmetic formats
exception handling: indications of exceptional conditions (such as division by zero, overflow, etc.)
ECMAScript has two zero numbers: +0 and -0.
They are considered equal by most comparison algorithms:
+0 == -0 // true (Equality comparison)
+0 === -0 // true (StrictEquality comparison)
[+0].includes(-0) // true (SameValueZero comparison)
However, they are different number values:
Object.is(+0, -0) // false (SameValue comparison)
Usually, both zeros behave identically in mathematical calculations. However,
1 / +0 === +Infinity
1 / -0 === -Infinity
In this case, you get -0 because Math.round is defined as follows:
If x is less than 0 but greater than or equal to -0.5, the result is
−0.
In general, if you have an integer between −231 and 231−1, and you want to convert it to +0 if it's -0, and don't alter it otherwise, you can use bitwise OR:
-1 | 0 // -1
-0 | 0 // +0
+0 | 0 // +0
+1 | 0 // +1
However, in your case you could just take the -1 outside of Math.round:
Math.round(Math.random()*2) - 1
But note using Math.round will produce a non-uniform probability distribution. Consider using
Math.floor(Math.random()*3) - 1
The function Math.round works as specified in the standard:
If x is less than 0 but greater than or equal to -0.5, the result is −0.
Math.random returns number between 0 (zero) and 1 (one) exclusively, so you will have several occurances of that rule.

Why does rounding -0.1 return -0

While testing, I noticed something strange with Math.round().
When I was rounding negative numbers close to 0 (-0.1, -0.01, etc), the return value in my console would be -0 rather than 0. Even stranger, if I were to set that same value to an element's text, the element would display 0, instead of -0.
DEMO:
http://jsfiddle.net/dirtyd77/qcug9/
Can anyone explain why this occurs? Any help would be greatly appreciated!
Also, I am using Chrome Version 33.0.1750.117.
The kind of numbers JavaScript uses (IEEE-754 double-precision floating point) have the concept of both "positive" and "negative" zero.
The next highest integer to -0.1 is -0. "Highest" in this case means "toward positive infinity."
From the specification, §15.8.2.15 "Math.round":
If x is less than 0 but greater than or equal to -0.5, the result is −0.
-0 and +0 are both rendered as just 0 when converted to string. For instance:
console.log(-0) // 0 or -0 depending on what console you use
console.log(String(-0)) // 0 (always)
The IEEE double precision format allows for negative zero (distinct from positive 0). The values are considered almost equal (e.g. they're equal even if you compare with === and -0 is not considered "less than" 0) but for example 1/0 is infinity while 1/-0 is -infinity.
IMO You shouldn't try to read too much into this semantic.
JavaScript uses IEEE-754 to represent numbers and that spec considers 0 and -0 to be technically different. Presumably, negative zero is the integer nearest to those values, as defined by Math.round(x).
Note that zero is both loosely and strictly equal to negative zero (0==-0 and 0===-0).
You can workaround by doing Math.abs(Math.round(x)) if you don't want to see -0.

Why is IsNaN(x) different from x == NaN where x = NaN [duplicate]

This question already has answers here:
What is the rationale for all comparisons returning false for IEEE754 NaN values?
(12 answers)
Closed 10 years ago.
Why are these two different?
var x = NaN; //e.g. Number("e");
alert(isNaN(x)); //true (good)
alert(x == NaN); //false (bad)
Nothing is equal to NaN. Any comparison will always be false.
In both the strict and abstract comparison algorithms, if the types are the same, and either operand is NaN, the result will be false.
If Type(x) is Number, then
If x is NaN, return false.
If y is NaN, return false.
In the abstract algorithm, if the types are different, and a NaN is one of the operands, then the other operand will ultimately be coerced to a number, and will bring us back to the scenario above.
The equality and inequality predicates are non-signaling so x = x returning false can be used to test if x is a quiet NaN.
Source
This is the rule defined in IEEE 754 so full compliance with the specification requires this behavior.
The following operations return NaN
The divisions 0/0, ∞/∞, ∞/−∞, −∞/∞, and −∞/−∞
The multiplications 0×∞ and 0×−∞
The power 1^∞
The additions ∞ + (−∞), (−∞) + ∞ and equivalent subtractions.
Real operations with complex results:
The square root of a negative number
The logarithm of a negative number
The tangent of an odd multiple of 90 degrees (or π/2 radians)
The inverse sine or cosine of a number which is less than −1 or greater than +1.
The following operations return values for numeric operations. Hence typeof Nan is a number. NaN is an undefined number in mathematical terms. ∞ + (-∞) is not equal to ∞ + (-∞). But we get that NaN is typeof number because it results from a numeric operation.
From wiki:

Why is undefined == undefined but NaN != NaN? [duplicate]

This question already has answers here:
What is the rationale for all comparisons returning false for IEEE754 NaN values?
(12 answers)
Closed 8 years ago.
I am wondering why undefined == undefined but NaN != NaN.
Because that's how it is defined in both the Abstract Equality Comparison Algorithm, and the Strict Equality Comparison Algorithm.
If either operand to == or === is NaN, it returns false.
Abstract
If Type(x) is Number, then
If x is NaN, return false.
If y is NaN, return false.
If x is the same Number value as y, return true.
If x is +0 and y is −0, return true.
If x is −0 and y is +0, return true.
Return false.
EDIT: The motivation for the unequal comparison as noted by #CMS is compliance with the IEEE 754 standard.
From the Wikipedia link provided in the comment below:
...The normal comparison operations however treat NaNs as unordered and compare −0 and +0 as equal. The totalOrder predicate will order these cases, and it also distinguishes between different representations of NaNs and between the same decimal floating point number encoded in different ways.
Because Math.sqrt(-5) !== Math.sqrt(-6).
Not sure why it is like this, but in order to check if a certain statement or variable is a NaN, you should use the isNaN method
I would assume because the IEEE standard allows for more than one representation of NaN. Not all NaNs are equal to each other...
The reasoning is that the creators wanted x == x returning false to mean that x is NaN, so NaN == NaN has to return false to be consistent.

Categories