Czytania przez opisie ECMAScript 5.1 , +0
i-0
odznaczają.
Dlaczego więc +0 === -0
ocenia się true
?
Object.is
do rozróżnienia +0 i -0
Czytania przez opisie ECMAScript 5.1 , +0
i-0
odznaczają.
Dlaczego więc +0 === -0
ocenia się true
?
Object.is
do rozróżnienia +0 i -0
Odpowiedzi:
JavaScript używa standardu IEEE 754 do reprezentowania liczb. Z Wikipedii :
Podpisane zero to zero ze skojarzonym znakiem. W zwykłej arytmetyce −0 = +0 = 0. Jednak w obliczeniach niektóre reprezentacje liczbowe dopuszczają istnienie dwóch zer, często oznaczanych przez -0 (zero ujemne) i +0 (zero dodatnie) . Dzieje się tak w niektórych reprezentacjach liczb ze znakiem dla liczb całkowitych oraz w większości reprezentacji liczb zmiennoprzecinkowych. Liczba 0 jest zwykle kodowana jako +0, ale może być reprezentowana przez +0 lub −0.
Standard IEEE 754 dla arytmetyki zmiennoprzecinkowej (obecnie używany przez większość komputerów i języków programowania obsługujących liczby zmiennoprzecinkowe) wymaga zarówno +0, jak i -0. Zera można uznać za wariant rozszerzonej osi liczb rzeczywistych, tak że 1 / −0 = −∞ i 1 / + 0 = + ∞, dzielenie przez zero jest niezdefiniowane tylko dla ± 0 / ± 0 i ± ∞ / ± ∞ .
Artykuł zawiera dalsze informacje na temat różnych reprezentacji.
Więc to jest powód, dla którego technicznie należy rozróżnić oba zera.
Jednak
+0 === -0
zwraca wartość true. Dlaczego (...) ?
To zachowanie jest wyraźnie zdefiniowane w sekcji 11.9.6 , Algorytm ścisłego porównywania równości (podkreślenie częściowo moje):
Porównanie
x === y
, gdziex
iy
są wartościami, daje prawdę lub fałsz . Takie porównanie wykonuje się w następujący sposób:(...)
Jeśli Type (x) to Number, to
- Jeśli x jest NaN, zwraca false.
- Jeśli y jest NaN, zwraca false.
- Jeśli x jest tą samą wartością Number co y, zwraca true.
- Jeśli x wynosi +0, a y wynosi -0, zwraca prawdę.
- Jeśli x wynosi −0, a y wynosi +0, zwraca prawdę.
- Zwróć fałsz.
(...)
(To samo dotyczy przy +0 == -0
okazji.)
Wydaje się logiczne, aby traktować +0
i -0
równorzędnie. W przeciwnym razie musielibyśmy wziąć to pod uwagę w naszym kodzie, a ja osobiście nie chcę tego robić;)
Uwaga:
ES2015 wprowadza nową metodę porównania Object.is
. Object.is
wyraźnie rozróżnia -0
i +0
:
Object.is(-0, +0); // false
1/0 === Infinity; // true
i 1/-0 === -Infinity; // true
.
1 === 1
i +0 === -0
ale 1/+0 !== 1/-0
. Jakie dziwne!
+0 !== -0
;) To może naprawdę stwarzać problemy.
0 !== +0
/ 0 !== -0
, co rzeczywiście stworzyłoby problemy!
Dodam to jako odpowiedź, ponieważ przeoczyłem komentarz @ user113716.
Możesz sprawdzić -0, wykonując następujące czynności:
function isMinusZero(value) {
return 1/value === -Infinity;
}
isMinusZero(0); // false
isMinusZero(-0); // true
e±308
, że twoja liczba może być reprezentowana tylko w postaci zdenormalizowanej, a różne implementacje mają różne opinie na temat tego, gdzie je w ogóle wspierać, czy nie. Chodzi o to, że na niektórych maszynach w niektórych trybach zmiennoprzecinkowych twoja liczba jest reprezentowana jako, -0
a na innych jako zdenormalizowana liczba 0.000000000000001e-308
. Takie
Właśnie natknąłem się na przykład, w którym +0 i -0 zachowują się rzeczywiście bardzo różnie:
Math.atan2(0, 0); //returns 0
Math.atan2(0, -0); //returns Pi
Uważaj: nawet gdy używasz Math.round na liczbie ujemnej, takiej jak -0,0001, w rzeczywistości będzie to -0 i może zepsuć niektóre kolejne obliczenia, jak pokazano powyżej.
Szybkim i nieprzyjemnym sposobem rozwiązania tego problemu jest wykonanie czegoś takiego:
if (x==0) x=0;
Lub tylko:
x+=0;
To konwertuje liczbę na +0 w przypadku, gdy było to -0.
W standardzie IEEE 754 używanym do reprezentowania typu Number w JavaScript znak jest reprezentowany przez bit (1 oznacza liczbę ujemną).
W rezultacie istnieje zarówno wartość ujemna, jak i dodatnia dla każdej możliwej do przedstawienia liczby, w tym 0
.
Dlatego jedno -0
i drugie +0
istnieje.
Odpowiadając na oryginalny tytuł Are +0 and -0 the same?
:
brainslugs83
(w komentarzach odpowiedzi autorstwa Spudley
) wskazał na ważny przypadek, w którym +0 i -0 w JS to nie to samo - zaimplementowane jako funkcja:
var sign = function(x) {
return 1 / x === 1 / Math.abs(x);
}
To, poza standardowym, Math.sign
zwróci poprawny znak +0 i -0.
Istnieją dwie możliwe wartości (reprezentacje bitów) dla 0. To nie jest unikalne. Może się to zdarzyć zwłaszcza w przypadku liczb zmiennoprzecinkowych. Dzieje się tak, ponieważ liczby zmiennoprzecinkowe są w rzeczywistości przechowywane jako rodzaj formuły.
Liczby całkowite można również przechowywać na różne sposoby. Możesz mieć wartość liczbową z dodatkowym bitem znaku, więc w 16-bitowej przestrzeni można przechowywać 15-bitową wartość całkowitą i bit znaku. W tej reprezentacji wartości 1000 (szesnastkowo) i 0000 to 0, ale jedna z nich to +0, a druga to -0.
Można tego uniknąć, odejmując 1 od wartości całkowitej, tak aby mieściła się w zakresie od -1 do -2 ^ 16, ale byłoby to niewygodne.
Bardziej powszechnym podejściem jest przechowywanie liczb całkowitych w „dwóch uzupełnieniach”, ale najwyraźniej ECMAscript zdecydował się tego nie robić. W tej metodzie numery mieszczą się w zakresie od 0000 do 7FFF dodatnich. Liczby ujemne zaczynają się od FFFF (-1) do 8000.
Oczywiście te same zasady dotyczą również większych liczb całkowitych, ale nie chcę, aby moje F się zużyło. ;)
+0 === -0
to trochę dziwne. Ponieważ teraz mamy 1 === 1
i +0 === -0
ale 1/+0 !== 1/-0
...
+0 === -0
mimo że dwie reprezentacje bitów są różne.
Zrzuciłbym to na metodę ścisłego porównania równości („===”). Spójrz na sekcję 4d
patrz 7.2.13 Ścisłe porównanie równości w specyfikacji
Wikipedia zawiera dobry artykuł wyjaśniający to zjawisko: http://en.wikipedia.org/wiki/Signed_zero
W skrócie, zarówno +0, jak i -0 są zdefiniowane w specyfikacjach zmiennoprzecinkowych IEEE. Oba są technicznie różne od 0 bez znaku, który jest liczbą całkowitą, ale w praktyce wszystkie dają zero, więc rozróżnienie można zignorować ze względów praktycznych.