+0 и -0 одинаковы?

172

Читая спецификацию ECMAScript 5.1 , +0и -0различаются.

Почему тогда +0 === -0оценивать true?

Randomblue
источник
возможный дубликат дифференцирования +0 и -0
GolezTrol
6
Обратите внимание, что в ES2015 вы можете использовать, Object.isчтобы различать +0 и -0
Бенджамин Грюнбаум
Цитируя Дэвида Фланагана из JS, окончательное руководство : недостаточный уровень возникает, когда результат числовой операции ближе к нулю, чем наименьшее представимое число. В этом случае JavaScript возвращает 0. Если недостаточное значение возникает из-за отрицательного числа, JavaScript возвращает специальное значение, известное как «отрицательный ноль».
RBT

Ответы:

194

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являются значениями, дает истину или ложь . Такое сравнение выполняется следующим образом:

(...)

  • Если тип (х) является число, то

    1. Если x равен NaN, вернуть false.
    2. Если y равен NaN, вернуть false.
    3. Если x - это то же значение Number, что и y, вернуть true.
    4. Если x равен +0, а y равен −0, вернуть true.
    5. Если x равен −0, а y равен +0, вернуть true.
    6. Вернуть ложь.

(...)

(То же самое относится и к +0 == -0кстати.)

Вроде бы логично относиться +0и -0как к равным. В противном случае мы должны были бы принять это во внимание в нашем коде, и я лично не хочу этого делать;)


Примечание:

ES2015 вводит новый метод сравнения Object.is. Object.isчетко различает -0и +0:

Object.is(-0, +0); // false
Феликс Клинг
источник
15
Так 1/0 === Infinity; // trueи есть 1/-0 === -Infinity; // true.
user113716
48
Так у нас 1 === 1и есть +0 === -0но 1/+0 !== 1/-0. Как странно!
Randomblue
8
@ Случайно: Я думаю, что это, безусловно, лучше, чем +0 !== -0;) Это может действительно создать проблемы.
Феликс Клинг
@FelixKling, или 0 !== +0/ 0 !== -0, что тоже может создать проблемы!
Яник Рошон
5
На самом деле, эти модели поведения ограничивают вычисления в математике. Например, функция 1 / x имеет значение бесконечности в 0, однако она отделяется, если мы приближаемся к 0 с положительной или отрицательной стороны; в первом случае результат + inf, во втором -inf.
Агостон Хорват
19

Я добавлю это как ответ, потому что я пропустил комментарий @ user113716.

Вы можете проверить на -0, выполнив это:

function isMinusZero(value) {
  return 1/value === -Infinity;
}

isMinusZero(0); // false
isMinusZero(-0); // true
laktak
источник
6
Вероятно, следует также проверить == 0, приведенное выше isMinusZero (-1e-323) возвращает true!
Крис
1
@ Крис, предел показателя двойной точности есть e±308, ваш номер может быть представлен только в денормализованной форме, и разные реализации имеют разные мнения о том, где их поддерживать или нет. Дело в том, что на некоторых машинах в некоторых режимах с плавающей запятой ваш номер представлен как, -0а на других - как денормализованный номер 0.000000000000001e-308. Такие поплавки, так весело
слабое время
Это может работать и для других языков (я проверял на C, и это работает)
Мукул Кумар
12

Я только что натолкнулся на пример, где +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.

Foxcode
источник
Спасибо. Так странно, как добавление нуля решило бы проблему, с которой я столкнулся. «Если ничего не помогает, добавьте ноль». Урок на всю жизнь.
Microsis
Я только что столкнулся с этим в Math.atan (y / x), который (возможно, удивительно) может обрабатывать бесконечно "положительный или отрицательный" y / x ", за исключением того, что он дает неправильный ответ в случае, когда x равен -0. Замена "x" на "(x + 0)" исправляет это.
Джейкоб С. говорит восстановить Монику
5

В стандарте IEEE 754, используемом для представления типа Number в JavaScript, знак представлен битом (1 означает отрицательное число).

В результате существует как отрицательное, так и положительное значение для каждого представимого числа, в том числе 0.

Именно поэтому -0и +0существуют.

Арно Ле Блан
источник
3
Дополнение два также использует бит для знака, но имеет только один ноль (положительный).
Феликс Клинг
1
Да, но в дополнении Two отрицательный бит также является частью значения, поэтому после установки отрицательного бита он больше не равен нулю.
Арно Ле Блан
3

Отвечая на оригинальное название 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.

BM
источник
2

Есть два возможных значения (битовые представления) для 0. Это не уникально. Особенно в числах с плавающей точкой это может произойти. Это потому, что числа с плавающей запятой на самом деле хранятся в виде формулы.

Целые числа также могут храниться разными способами. Вы можете иметь числовое значение с дополнительным битом знака, поэтому в 16-битном пространстве вы можете хранить 15-битное целочисленное значение и бит знака. В этом представлении значения 1000 (hex) и 0000 оба равны 0, но одно из них равно +0, а другое - -0.

Этого можно избежать, вычитая 1 из целочисленного значения, чтобы оно варьировалось от -1 до -2 ^ 16, но это было бы неудобно.

Более распространенный подход - хранить целые числа в «двух дополнениях», но, очевидно, ECMAscript предпочел не делать этого. В этом методе числа в диапазоне от 0000 до 7FFF положительны. Отрицательные числа начинаются с FFFF (-1) до 8000.

Конечно, те же правила применимы и к большим целым числам, но я не хочу, чтобы мой F изнашивался. ;)

GolezTrol
источник
4
Но разве ты не находишь +0 === -0это немного странным? Потому что теперь у нас есть 1 === 1и +0 === -0но 1/+0 !== 1/-0...
Randomblue
2
Конечно, +0 - это -0. Это оба ничего. Но есть огромная разница между + бесконечность и -infinity, не так ли? Эти числа бесконечности могут быть даже причиной того, что ECMA поддерживает +0 и -1.
GolezTrol
Вы не объясняете, почему, +0 === -0несмотря на различие двухбитных представлений.
Randomblue
1
+0 это -0 это 0, ничего, нада, ниенте. Имеет смысл, что они одинаковы. Почему небо синее? 4 + 3 также совпадает с 1 + 6, хотя представления различны. Они имеют разные представления (и, следовательно, другое битовое значение), но при сравнении они обрабатываются как один и тот же ноль, которым они являются.
GolezTrol
1
Они не одинаковы. См. Stackoverflow.com/questions/7223717/differentiating-0-and-0 для примеров, показывающих это.
Randomblue
2

Мы можем использовать , Object.isчтобы различать +0 и -0, и еще одну вещь, NaN==NaN.

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

Object.is(NaN,NaN) //true
terryc
источник
1

Я бы свалил вину на метод сравнения строгого равенства ('==='). Посмотрите на раздел 4d введите описание изображения здесь

см. 7.2.13 Сравнение строгого равенства в спецификации

Бар Horing
источник
0

В Википедии есть хорошая статья, чтобы объяснить это явление: http://en.wikipedia.org/wiki/Signed_zero

Вкратце, это и +0, и -0 определены в спецификациях IEEE с плавающей запятой. Они оба технически отличаются от 0 без знака, который является целым числом, но на практике все они оцениваются как ноль, поэтому различие можно игнорировать для всех практических целей.

Spudley
источник
2
Это не совсем правильно - например, 1 / -0 == 1/0 оценивается как ложное в javascript. Они не "оценивают" магический ноль без знака, так как в IEEE 754 нет такого понятия, как "целое число без знака".
BrainSlugs83