+0 e -0 são iguais?

171

A leitura através da especificação ECMAScript 5,1 , +0e -0são distinguidos.

Por que, então, +0 === -0avalia a true?

Randomblue
fonte
possível duplicata de Diferenciando +0 e -0
GolezTrol
6
Note que no ES2015 você pode usar Object.ispara distinguir +0 e -0
Benjamin Gruenbaum
Citando David Flanagan do JS, o guia definitivo : O fluxo insuficiente ocorre quando o resultado de uma operação numérica está mais próximo de zero do que o menor número representável. Nesse caso, o JavaScript retornará 0. Se o estouro ocorrer a partir de um número negativo, o JavaScript retornará um valor especial conhecido como "zero negativo".
RBT

Respostas:

193

JavaScript usa o padrão IEEE 754 para representar números. Da Wikipedia :

Zero assinado é zero com um sinal associado. Na aritmética comum, −0 = +0 = 0. No entanto, na computação, algumas representações numéricas permitem a existência de dois zeros, geralmente denotados por −0 (zero negativo) e +0 (zero positivo) . Isso ocorre em algumas representações numéricas assinadas para números inteiros e na maioria das representações numéricas de ponto flutuante. O número 0 geralmente é codificado como +0, mas pode ser representado por +0 ou −0.

O padrão IEEE 754 para aritmética de ponto flutuante (atualmente usado pela maioria dos computadores e linguagens de programação que suportam números de ponto flutuante) requer +0 e -0. Os zeros podem ser considerados uma variante da linha de número real estendida, de modo que 1 / −0 = −∞ e 1 / + 0 = + ∞, a divisão por zero é indefinida apenas para ± 0 / ± 0 e ± ∞ / ± ∞ .

O artigo contém mais informações sobre as diferentes representações.

Portanto, essa é a razão pela qual, tecnicamente, os dois zeros devem ser distinguidos.

No entanto, +0 === -0avalia como verdadeiro. Por que é que (...) ?

Esse comportamento é definido explicitamente na seção 11.9.6 , o Algoritmo de comparação estrita de igualdade (ênfase parcialmente minha):

A comparação x === y, onde xe ysão valores, produz verdadeiro ou falso . Essa comparação é realizada da seguinte maneira:

(...)

  • Se o Tipo (x) for Número, então

    1. Se x for NaN, retorne false.
    2. Se y for NaN, retorne false.
    3. Se x é o mesmo valor numérico que y, retorne true.
    4. Se x é +0 e y é −0, retorne true.
    5. Se x é −0 e y é +0, retorne true.
    6. Retorna falso.

(...)

(O mesmo vale para +0 == -0btw.)

Parece logicamente tratar +0e -0igual. Caso contrário, teríamos que levar isso em conta em nosso código e eu, pessoalmente, não quero fazer isso;)


Nota:

O ES2015 apresenta um novo método de comparação Object.is,. Object.isdistingue explicitamente entre -0e +0:

Object.is(-0, +0); // false
Felix Kling
fonte
15
De fato 1/0 === Infinity; // truee 1/-0 === -Infinity; // true.
user113716
48
Então nós temos 1 === 1e +0 === -0mas 1/+0 !== 1/-0. Que estranho!
Randomblue 28/08
8
@ Random: Eu acho que é certamente melhor do que +0 !== -0;) Isso pode realmente criar problemas.
Felix Kling
@FelixKling, ou 0 !== +0/ 0 !== -0, o que também criaria problemas também!
Yanick Rochon
5
Na verdade, esses modelos de comportamento limitam o cálculo em matemática. Por exemplo, a função 1 / x tem um valor infinito em 0; no entanto, é separado se estivermos aproximando de 0 do lado positivo do negativo; no primeiro, o resultado é + inf, no último, -inf.
Agoston Horvath
19

Acrescentarei isso como resposta, porque ignorei o comentário de @ user113716.

Você pode testar -0 fazendo o seguinte:

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

isMinusZero(0); // false
isMinusZero(-0); // true
Laktak
fonte
6
Provavelmente também deve verificar == 0, o isMinusZero acima (-1e-323) retorna verdadeiro!
Chris
1
@ Chris, o limite do expoente de dupla precisão é que e±308seu número pode ser representado apenas na forma desnormalizada e diferentes implementações têm opiniões diferentes sobre onde apoiá-los ou não. O ponto é que, em algumas máquinas, em alguns modos de ponto flutuante, seu número é representado como -0em outras, como número desnormalizado 0.000000000000001e-308. Esses carros alegóricos, tão divertido
weaknespase
Isso pode funcionar para outras línguas também (eu testei para C e isso funciona)
Mukul Kumar
11

Acabei de encontrar um exemplo em que +0 e -0 se comportam de maneira muito diferente:

Math.atan2(0, 0);  //returns 0
Math.atan2(0, -0); //returns Pi

Cuidado: mesmo ao usar Math.round em um número negativo como -0.0001, ele será realmente -0 e poderá estragar alguns cálculos subsequentes, como mostrado acima.

A maneira rápida e suja de corrigir isso é fazer algo como:

if (x==0) x=0;

ou apenas:

x+=0;

Isso converte o número em +0, caso seja -0.

Foxcode
fonte
Obrigado. Tão estranho como adicionar zero resolveria o problema que encontrei. "Se tudo mais falhar, adicione zero." Uma lição para a vida.
Microsis 23/08/19
Eu também encontrei isso no Math.atan (y / x) também, que (talvez surpreendentemente) pode lidar com "y / x" infinitamente positivo ou negativo, exceto que fornece a resposta errada no caso em que x é -0. Substituir "x" por "(x + 0)" corrige.
Jacob C. disse Reinstate Monica
5

No padrão IEEE 754 usado para representar o tipo Number em JavaScript, o sinal é representado por um bit (1 indica um número negativo).

Como resultado, existe um valor negativo e um positivo para cada número representável, inclusive 0.

É por isso que ambos -0e +0existem.

Arnaud Le Blanc
fonte
3
O complemento de dois também usa um pouco para o sinal, mas tem apenas um zero (positivo).
Felix Kling 28/08
1
Sim, mas no complemento do Dois, o bit negativo também faz parte do valor; portanto, depois de definir o bit negativo, ele não é mais zero.
Arnaud Le Blanc
3

Respondendo ao título original Are +0 and -0 the same?:

brainslugs83(nos comentários da resposta de Spudley) apontou um caso importante em que +0 e -0 em JS não são os mesmos - implementados como função:

var sign = function(x) {
    return 1 / x === 1 / Math.abs(x);
}

Isso, além do padrão, Math.signretornará o sinal correto de +0 e -0.

BM
fonte
2

Existem dois valores possíveis (representações de bits) para 0. Isso não é exclusivo. Especialmente em números de ponto flutuante, isso pode ocorrer. Isso ocorre porque os números de ponto flutuante são realmente armazenados como um tipo de fórmula.

Os números inteiros também podem ser armazenados de maneiras separadas. Você pode ter um valor numérico com um bit de sinal adicional; portanto, em um espaço de 16 bits, você pode armazenar um valor inteiro de 15 bits e um bit de sinal. Nesta representação, o valor 1000 (hex) e 0000 são 0, mas um deles é +0 e o outro é -0.

Isso poderia ser evitado subtraindo 1 do valor inteiro, variando de -1 a -2 ^ 16, mas isso seria inconveniente.

Uma abordagem mais comum é armazenar números inteiros em 'dois complementos', mas aparentemente o ECMAscript optou por não. Neste método, os números variam de 0000 a 7FFF positivo. Os números negativos começam em FFFF (-1) a 8000.

Obviamente, as mesmas regras também se aplicam a números inteiros maiores, mas não quero que meu F se esgote. ;)

GolezTrol
fonte
4
Mas você não acha isso +0 === -0um pouco estranho. Porque agora nós temos 1 === 1e +0 === -0mas 1/+0 !== 1/-0...
Randomblue
2
Claro que +0 é -0. Os dois não são nada. Mas há uma enorme diferença entre + infinito e-infinito, existe? Esses números de inifinidade podem até ser a razão pela qual o ECMA suporta +0 e -1.
precisa saber é o seguinte
Você não explica por que, +0 === -0apesar das representações de dois bits serem diferentes.
Randomblue 28/08
1
+0 é -0 é 0, nada, nada, niente. Faz sentido que eles sejam iguais. Por que o céu é azul? 4 + 3 também é o mesmo que 1 + 6, embora as representações sejam diferentes. Eles têm representações diferentes (e, portanto, um valor de bit diferente), mas, quando comparados, são tratados com o mesmo zero, o que são.
precisa saber é o seguinte
1
Eles não são os mesmos. Veja stackoverflow.com/questions/7223717/differentiating-0-and-0 para exemplos mostrando isso.
Randomblue 29/08
2

Podemos usar Object.ispara distinguir +0 e -0 e mais uma coisa NaN==NaN,.

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

Object.is(NaN,NaN) //true
terryc
fonte
1

Eu culparia o método de comparação estrita da igualdade ('==='). Veja a seção 4d insira a descrição da imagem aqui

consulte 7.2.13 Comparação estrita de igualdade na especificação

Bar Horing
fonte
0

A Wikipedia tem um bom artigo para explicar esse fenômeno: http://en.wikipedia.org/wiki/Signed_zero

Em resumo, ambos +0 e -0 são definidos nas especificações de ponto flutuante IEEE. Ambos são tecnicamente distintos de 0 sem um sinal, que é um número inteiro, mas, na prática, todos são avaliados como zero, de modo que a distinção pode ser ignorada para todos os fins práticos.

Spudley
fonte
2
Isso não está totalmente correto - 1 / -0 == 1/0 é avaliado como falso em javascript, por exemplo. Eles não "avaliar" a um sem sinal de zero mágico, como não existe tal conceito como "um inteiro sem sinal zero" em IEEE 754.
BrainSlugs83