Читая спецификацию ECMAScript 5.1 , +0
и -0
различаются.
Почему тогда +0 === -0
оценивать true
?
Object.is
чтобы различать +0 и -0
Читая спецификацию ECMAScript 5.1 , +0
и -0
различаются.
Почему тогда +0 === -0
оценивать true
?
Object.is
чтобы различать +0 и -0
Ответы:
JavaScript использует стандарт IEEE 754 для представления чисел. Из Википедии :
Подписанный ноль - ноль со связанным знаком. В обычной арифметике -0 = +0 = 0. Однако в вычислениях некоторые числовые представления допускают существование двух нулей, часто обозначаемых как -0 (отрицательный ноль) и +0 (положительный ноль) . Это происходит в некоторых представлениях чисел со знаком для целых чисел и в большинстве представлений чисел с плавающей точкой. Число 0 обычно кодируется как +0, но может быть представлено либо +0, либо -0.
Стандарт IEEE 754 для арифметики с плавающей запятой (в настоящее время используется большинством компьютеров и языков программирования, поддерживающих числа с плавающей запятой) требует +0 и -0. Нули можно рассматривать как вариант расширенной строки действительных чисел, такой что 1 / −0 = −∞ и 1 / + 0 = + ∞, деление на ноль не определено только для ± 0 / ± 0 и ± ∞ / ± ∞ ,
Статья содержит дополнительную информацию о различных представлениях.
Это и есть причина, по которой технически следует различать оба нуля.
Тем не менее,
+0 === -0
оценивается как правда. Это почему (...) ?
Это поведение явно определено в разделе 11.9.6 , Алгоритм сравнения строгого равенства (выделение частично мое):
Сравнение
x === y
, гдеx
иy
являются значениями, дает истину или ложь . Такое сравнение выполняется следующим образом:(...)
Если тип (х) является число, то
- Если x равен NaN, вернуть false.
- Если y равен NaN, вернуть false.
- Если x - это то же значение Number, что и y, вернуть true.
- Если x равен +0, а y равен −0, вернуть true.
- Если x равен −0, а y равен +0, вернуть true.
- Вернуть ложь.
(...)
(То же самое относится и к +0 == -0
кстати.)
Вроде бы логично относиться +0
и -0
как к равным. В противном случае мы должны были бы принять это во внимание в нашем коде, и я лично не хочу этого делать;)
Примечание:
ES2015 вводит новый метод сравнения Object.is
. Object.is
четко различает -0
и +0
:
Object.is(-0, +0); // false
1/0 === Infinity; // true
и есть 1/-0 === -Infinity; // true
.
1 === 1
и есть +0 === -0
но 1/+0 !== 1/-0
. Как странно!
+0 !== -0
;) Это может действительно создать проблемы.
0 !== +0
/ 0 !== -0
, что тоже может создать проблемы!
Я добавлю это как ответ, потому что я пропустил комментарий @ user113716.
Вы можете проверить на -0, выполнив это:
function isMinusZero(value) {
return 1/value === -Infinity;
}
isMinusZero(0); // false
isMinusZero(-0); // true
e±308
, ваш номер может быть представлен только в денормализованной форме, и разные реализации имеют разные мнения о том, где их поддерживать или нет. Дело в том, что на некоторых машинах в некоторых режимах с плавающей запятой ваш номер представлен как, -0
а на других - как денормализованный номер 0.000000000000001e-308
. Такие поплавки, так весело
Я только что натолкнулся на пример, где +0 и -0 ведут себя совершенно по-разному:
Math.atan2(0, 0); //returns 0
Math.atan2(0, -0); //returns Pi
Будьте осторожны: даже при использовании Math.round с отрицательным числом, таким как -0,0001, оно будет на самом деле -0 и может привести к некоторым последующим вычислениям, как показано выше.
Быстрый и грязный способ исправить это сделать что-то вроде:
if (x==0) x=0;
или просто:
x+=0;
Это преобразует число в +0, если это было -0.
В стандарте IEEE 754, используемом для представления типа Number в JavaScript, знак представлен битом (1 означает отрицательное число).
В результате существует как отрицательное, так и положительное значение для каждого представимого числа, в том числе 0
.
Именно поэтому -0
и +0
существуют.
Отвечая на оригинальное название Are +0 and -0 the same?
:
brainslugs83
(в комментариях ответа от Spudley
) указал на важный случай, когда +0 и -0 в JS не совпадают - реализованы как функция:
var sign = function(x) {
return 1 / x === 1 / Math.abs(x);
}
Это, кроме стандартных, Math.sign
вернет правильный знак +0 и -0.
Есть два возможных значения (битовые представления) для 0. Это не уникально. Особенно в числах с плавающей точкой это может произойти. Это потому, что числа с плавающей запятой на самом деле хранятся в виде формулы.
Целые числа также могут храниться разными способами. Вы можете иметь числовое значение с дополнительным битом знака, поэтому в 16-битном пространстве вы можете хранить 15-битное целочисленное значение и бит знака. В этом представлении значения 1000 (hex) и 0000 оба равны 0, но одно из них равно +0, а другое - -0.
Этого можно избежать, вычитая 1 из целочисленного значения, чтобы оно варьировалось от -1 до -2 ^ 16, но это было бы неудобно.
Более распространенный подход - хранить целые числа в «двух дополнениях», но, очевидно, ECMAscript предпочел не делать этого. В этом методе числа в диапазоне от 0000 до 7FFF положительны. Отрицательные числа начинаются с FFFF (-1) до 8000.
Конечно, те же правила применимы и к большим целым числам, но я не хочу, чтобы мой F изнашивался. ;)
+0 === -0
это немного странным? Потому что теперь у нас есть 1 === 1
и +0 === -0
но 1/+0 !== 1/-0
...
+0 === -0
несмотря на различие двухбитных представлений.
Я бы свалил вину на метод сравнения строгого равенства ('==='). Посмотрите на раздел 4d
см. 7.2.13 Сравнение строгого равенства в спецификации
В Википедии есть хорошая статья, чтобы объяснить это явление: http://en.wikipedia.org/wiki/Signed_zero
Вкратце, это и +0, и -0 определены в спецификациях IEEE с плавающей запятой. Они оба технически отличаются от 0 без знака, который является целым числом, но на практике все они оцениваются как ноль, поэтому различие можно игнорировать для всех практических целей.