Czy +0 i -0 to to samo?

171

Czytania przez opisie ECMAScript 5.1 , +0i-0 odznaczają.

Dlaczego więc +0 === -0ocenia się true?

Randomblue
źródło
możliwy duplikat Różniczkowania +0 i -0
GolezTrol
6
Zauważ, że w ES2015 możesz użyć Object.isdo rozróżnienia +0 i -0
Benjamin Gruenbaum
Cytując Davida Flanagana z JS, ostateczny przewodnik : Niedopełnienie występuje, gdy wynik operacji numerycznej jest bliższy zeru niż najmniejsza możliwa do przedstawienia liczba. W tym przypadku JavaScript zwraca 0. Jeśli niedomiar wynika z liczby ujemnej, JavaScript zwraca specjalną wartość znaną jako „ujemne zero”.
RBT

Odpowiedzi:

193

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 === -0zwraca 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, gdzie xi ysą 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

    1. Jeśli x jest NaN, zwraca false.
    2. Jeśli y jest NaN, zwraca false.
    3. Jeśli x jest tą samą wartością Number co y, zwraca true.
    4. Jeśli x wynosi +0, a y wynosi -0, zwraca prawdę.
    5. Jeśli x wynosi −0, a y wynosi +0, zwraca prawdę.
    6. Zwróć fałsz.

(...)

(To samo dotyczy przy +0 == -0okazji.)

Wydaje się logiczne, aby traktować +0i -0ró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.iswyraźnie rozróżnia -0i +0:

Object.is(-0, +0); // false
Felix Kling
źródło
15
Rzeczywiście 1/0 === Infinity; // truei 1/-0 === -Infinity; // true.
user113716
48
Więc mamy 1 === 1i +0 === -0ale 1/+0 !== 1/-0. Jakie dziwne!
Randomblue
8
@Random: Myślę, że to z pewnością lepsze niż +0 !== -0;) To może naprawdę stwarzać problemy.
Felix Kling
@FelixKling lub 0 !== +0/ 0 !== -0, co rzeczywiście stworzyłoby problemy!
Yanick Rochon
5
W rzeczywistości te modele zachowania ograniczają obliczenia w matematyce. Na przykład funkcja 1 / x ma wartość nieskończoności w 0, jednak jest oddzielana, jeśli zbliżamy się do 0 od strony dodatniej lub ujemnej; w pierwszym przypadku wynikiem jest + inf, w drugim -inf.
Agoston Horvath
19

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
laktak
źródło
6
Powinien prawdopodobnie również sprawdzić == 0, powyższe isMinusZero (-1e-323) zwraca prawdę!
Chris
1
@Chris, limit wykładnika podwójnej precyzji jest taki 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, -0a na innych jako zdenormalizowana liczba 0.000000000000001e-308. Takie
pływanie
Może to działać również w innych językach (testowałem na C i to działa)
Mukul Kumar
11

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.

Foxcode
źródło
Dzięki. Dziwne, jak dodanie zera rozwiązałoby problem, z którym się spotkałem. „Jeśli wszystko inne zawiedzie, dodaj zero”. Lekcja na całe życie.
Microsis
Właśnie spotkałem się z tym również w Math.atan (y / x), które (być może zaskakująco) może obsłużyć dodatnio lub ujemnie nieskończone "y / x", z wyjątkiem tego, że daje złą odpowiedź w przypadku, gdy x wynosi -0. Zastąpienie „x” przez „(x + 0)” rozwiązuje ten problem.
Jacob C. mówi Przywróć Monikę
5

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 -0i drugie +0istnieje.

Arnaud Le Blanc
źródło
3
Uzupełnienie do dwóch również używa trochę dla znaku, ale ma tylko jedno zero (dodatnie).
Felix Kling
1
Tak, ale w uzupełnieniu do dwóch bit ujemny jest również częścią wartości, więc po ustawieniu bitu ujemnego nie jest już zerem.
Arnaud Le Blanc
3

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.signzwróci poprawny znak +0 i -0.

BM
źródło
2

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. ;)

GolezTrol
źródło
4
Ale nie uważasz, że +0 === -0to trochę dziwne. Ponieważ teraz mamy 1 === 1i +0 === -0ale 1/+0 !== 1/-0...
Randomblue
2
Oczywiście +0 to -0. To jest nic. Ale jest ogromna różnica między + infinity a -infinity, prawda? Te liczby nieskończoności mogą nawet być powodem, dla którego ECMA obsługuje zarówno +0, jak i -1.
GolezTrol
Nie wyjaśniasz dlaczego, +0 === -0mimo że dwie reprezentacje bitów są różne.
Randomblue
1
+0 to -0 to 0, nic, nada, niente. To ma sens, że są takie same. Dlaczego niebo jest niebieskie? 4 + 3 to również to samo, co 1 + 6, chociaż reprezentacje są różne. Mają różne reprezentacje (a więc inną wartość bitową), ale podczas porównywania są traktowane jako to samo zero, którym są.
GolezTrol
1
Są to nie to samo. Zobacz stackoverflow.com/questions/7223717/differentiating-0-and-0, aby zobaczyć przykłady pokazujące to.
Randomblue
2

Możemy użyć Object.isdo odróżnienia +0 i -0, i jeszcze jedno, NaN==NaN.

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

Object.is(NaN,NaN) //true
terryc
źródło
1

Zrzuciłbym to na metodę ścisłego porównania równości („===”). Spójrz na sekcję 4d wprowadź opis obrazu tutaj

patrz 7.2.13 Ścisłe porównanie równości w specyfikacji

Bar Horing
źródło
0

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.

Spudley
źródło
2
To nie jest całkowicie poprawne - na przykład 1 / -0 == 1/0 oznacza fałsz w javascript. Nie „oceniają” do magicznego zera bez znaku, ponieważ w IEEE 754 nie ma takiej koncepcji, jak „liczba całkowita bez znaku”.
BrainSlugs83