Est-ce que +0 et -0 sont identiques?

172

Lecture de la spécification ECMAScript 5.1 , +0et -0sont distingués.

Pourquoi alors +0 === -0évalue- truet-il?

Randomblue
la source
duplication possible de Différenciation +0 et -0
GolezTrol
6
Notez que dans ES2015, vous pouvez utiliser Object.ispour distinguer +0 et -0
Benjamin Gruenbaum
Citant David Flanagan de JS, le guide définitif : Un dépassement inférieur se produit lorsque le résultat d'une opération numérique est plus proche de zéro que le plus petit nombre représentable. Dans ce cas, JavaScript renvoie 0. En cas de dépassement inférieur à un nombre négatif, JavaScript renvoie une valeur spéciale appelée «zéro négatif».
RBT

Réponses:

194

JavaScript utilise la norme IEEE 754 pour représenter les nombres. De Wikipedia :

Le zéro signé est zéro avec un signe associé. En arithmétique ordinaire, −0 = +0 = 0. Cependant, en calcul, certaines représentations numériques permettent l'existence de deux zéros, souvent désignés par −0 (zéro négatif) et +0 (zéro positif) . Cela se produit dans certaines représentations de nombres signés pour les entiers et dans la plupart des représentations de nombres à virgule flottante. Le nombre 0 est généralement codé comme +0, mais peut être représenté par +0 ou −0.

La norme IEEE 754 pour l'arithmétique en virgule flottante (actuellement utilisée par la plupart des ordinateurs et langages de programmation qui prennent en charge les nombres à virgule flottante) nécessite à la fois +0 et −0. Les zéros peuvent être considérés comme une variante de la droite étendue des nombres réels telle que 1 / −0 = −∞ et 1 / + 0 = + ∞, la division par zéro n'est définie que pour ± 0 / ± 0 et ± ∞ / ± ∞ .

L'article contient de plus amples informations sur les différentes représentations.

C'est donc la raison pour laquelle, techniquement, les deux zéros doivent être distingués.

Cependant, +0 === -0évalue à vrai. Pourquoi donc (...) ?

Ce comportement est explicitement défini dans la section 11.9.6 , l' algorithme de comparaison d'égalité stricte (c'est moi qui souligne en partie):

La comparaison x === y, où xet ysont des valeurs, produit vrai ou faux . Une telle comparaison est effectuée comme suit:

(...)

  • Si Type (x) est Nombre, alors

    1. Si x est NaN, renvoie false.
    2. Si y est NaN, renvoie false.
    3. Si x est la même valeur numérique que y, renvoie true.
    4. Si x est +0 et y est −0, renvoie vrai.
    5. Si x est −0 et y est +0, renvoie vrai.
    6. Renvoie false.

(...)

(La même chose vaut pour +0 == -0btw.)

Il semble logiquement traiter +0et -0comme égal. Sinon, nous devrions en tenir compte dans notre code et moi, personnellement, je ne veux pas faire cela;)


Remarque:

ES2015 introduit une nouvelle méthode de comparaison, Object.is. Object.isfait une distinction explicite entre -0et +0:

Object.is(-0, +0); // false
Félix Kling
la source
15
En effet 1/0 === Infinity; // trueet 1/-0 === -Infinity; // true.
user113716
48
Donc nous avons 1 === 1et +0 === -0mais 1/+0 !== 1/-0. Comme c'est bizarre!
Randomblue
8
@Random: Je pense que c'est certainement mieux que +0 !== -0;) Cela pourrait vraiment créer des problèmes.
Felix Kling
@FelixKling, ou 0 !== +0/ 0 !== -0, ce qui créerait en effet aussi des problèmes!
Yanick Rochon le
5
En fait, ce comportement modélise le calcul des limites en mathématiques. Par exemple, la fonction 1 / x a une valeur infinie en 0, cependant, elle est séparée si nous nous rapprochons de 0 du côté positif du côté négatif; dans le premier, le résultat est + inf, dans le second, -inf.
Agoston Horvath
19

J'ajouterai ceci comme réponse car j'ai oublié le commentaire de @ user113716.

Vous pouvez tester -0 en faisant ceci:

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

isMinusZero(0); // false
isMinusZero(-0); // true
laktak
la source
6
Devrait probablement aussi vérifier == 0, le isMinusZero (-1e-323) ci-dessus renvoie true!
Chris
1
@Chris, la limite de l'exposant à double précision est e±308, votre nombre ne peut être représenté que sous forme dénormalisée et différentes implémentations ont des opinions différentes sur où les soutenir ou non. Le fait est que sur certaines machines dans certains modes à virgule flottante, votre nombre est représenté comme -0et sur d'autres comme un nombre dénormalisé 0.000000000000001e-308. Such floats, so fun
lownespase
Cela peut également fonctionner pour d'autres langues (j'ai testé pour C et cela fonctionne)
Mukul Kumar
12

Je viens de tomber sur un exemple où +0 et -0 se comportent en effet très différemment:

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

Attention: même en utilisant Math.round sur un nombre négatif comme -0,0001, il sera en fait -0 et peut bousiller certains calculs ultérieurs comme indiqué ci-dessus.

Un moyen rapide et sale de résoudre ce problème est de faire comme:

if (x==0) x=0;

ou juste:

x+=0;

Cela convertit le nombre en +0 au cas où il serait de -0.

Foxcode
la source
Merci. C'est tellement étrange comment l'ajout de zéro résoudrait le problème que j'ai rencontré. "Si tout le reste échoue, ajoutez zéro." Une leçon de vie.
Microsis
Je viens de rencontrer cela dans Math.atan (y / x) également, qui (peut-être étonnamment) peut gérer positivement ou négativement "y / x" infini, sauf que cela donne la mauvaise réponse dans le cas où x est -0. Le remplacement de "x" par "(x + 0)" corrige le problème.
Jacob C. dit Réintégrer Monica
5

dans le norme IEEE 754 utilisée pour représenter le type Number en JavaScript, le signe est représenté par un bit (un 1 indique un nombre négatif).

En conséquence, il existe à la fois une valeur négative et une valeur positive pour chaque nombre représentable, y compris 0.

C'est pourquoi les deux -0et +0existent.

Arnaud Le Blanc
la source
3
Le complément à deux utilise également un bit pour le signe, mais n'a qu'un seul zéro (positif).
Felix Kling
1
Oui, mais dans le complément à deux, le bit négatif fait également partie de la valeur, donc une fois que vous avez défini le bit négatif, ce n'est plus zéro.
Arnaud Le Blanc
3

Répondre au titre original Are +0 and -0 the same?:

brainslugs83(dans les commentaires de la réponse de Spudley) a souligné un cas important dans lequel +0 et -0 dans JS ne sont pas les mêmes - implémentés en fonction:

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

Ceci, autre que le standard, Math.signrenverra le signe correct de +0 et -0.

BM
la source
2

Il existe deux valeurs possibles (représentations binaires) pour 0. Ce n'est pas unique. Cela peut se produire en particulier dans les nombres à virgule flottante. C'est parce que les nombres à virgule flottante sont en fait stockés sous forme de formule.

Les entiers peuvent également être stockés de manière distincte. Vous pouvez avoir une valeur numérique avec un bit de signe supplémentaire, donc dans un espace de 16 bits, vous pouvez stocker une valeur entière de 15 bits et un bit de signe. Dans cette représentation, les valeurs 1000 (hexadécimal) et 0000 valent toutes deux 0, mais l'une d'elles est +0 et l'autre est -0.

Cela pourrait être évité en soustrayant 1 de la valeur entière afin qu'elle varie de -1 à -2 ^ 16, mais ce serait peu pratique.

Une approche plus courante consiste à stocker les entiers dans «deux compléments», mais apparemment, ECMAscript a choisi de ne pas le faire. Dans cette méthode, les nombres vont de 0000 à 7FFF positifs. Les nombres négatifs commencent de FFFF (-1) à 8000.

Bien sûr, les mêmes règles s'appliquent également aux nombres entiers plus grands, mais je ne veux pas que mon F s'use. ;)

GolezTrol
la source
4
Mais ne trouvez-vous pas cela +0 === -0un peu bizarre. Parce que maintenant nous avons 1 === 1et +0 === -0mais 1/+0 !== 1/-0...
Randomblue
2
Bien sûr, +0 est -0. Ce n'est rien à la fois. Mais il y a une énorme différence entre + infinity et -infinity, n'est-ce pas? Ces nombres d'inifinité peuvent même être la raison pour laquelle l'ECMA prend en charge à la fois +0 et -1.
GolezTrol
Vous n'expliquez pas pourquoi +0 === -0bien que les représentations à deux bits soient différentes.
Randomblue
1
+0 est -0 est 0, rien, nada, niente. Il est logique qu'ils soient identiques. Pourquoi le ciel est bleu? 4 + 3 est également identique à 1 + 6, bien que les représentations soient différentes. Ils ont des représentations différentes (et donc une valeur de bit différente), mais lorsqu'ils sont comparés, ils sont traités comme le même zéro, ce qu'ils sont.
GolezTrol
1
Ce ne sont pas les mêmes. Voir stackoverflow.com/questions/7223717/differentiating-0-and-0 pour des exemples montrant cela.
Randomblue
2

Nous pouvons utiliser Object.ispour distinguer +0 et -0, et une chose, NaN==NaN.

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

Object.is(NaN,NaN) //true
éponge
la source
1

Je le blâmerais sur la méthode Strict Equality Comparison ('==='). Regardez la section 4d entrez la description de l'image ici

voir 7.2.13 Comparaison d'égalité stricte sur la spécification

Bar Horing
la source
0

Wikipedia a un bon article pour expliquer ce phénomène: http://en.wikipedia.org/wiki/Signed_zero

En bref, +0 et -0 sont définis dans les spécifications de virgule flottante IEEE. Les deux sont techniquement distincts de 0 sans signe, qui est un entier, mais en pratique ils sont tous évalués à zéro, de sorte que la distinction peut être ignorée à toutes fins pratiques.

Spudley
la source
2
Ce n'est pas tout à fait correct - 1 / -0 == 1/0 donne la valeur false en javascript par exemple. Ils ne "évaluent" pas à un zéro magique non signé, car il n'existe pas de concept tel que "un zéro entier non signé" dans IEEE 754.
BrainSlugs83