+0 e -0 sono uguali?


172

Leggendo la specifica ECMAScript 5.1 , +0e -0si distinguono.

Perché allora +0 === -0valuta true?


possibile duplicato di Differenziamento +0 e -0
GolezTrol

6
Si noti che in ES2015 è possibile utilizzare Object.isper distinguere +0 e -0
Benjamin Gruenbaum il

Citando David Flanagan di JS la guida definitiva : Underflow si verifica quando il risultato di un'operazione numerica è più vicino a zero del numero rappresentabile più piccolo. In questo caso, JavaScript restituisce 0. Se il underflow si verifica da un numero negativo, JavaScript restituisce un valore speciale noto come "zero negativo".
RBT,

Risposte:


194

JavaScript utilizza lo standard IEEE 754 per rappresentare i numeri. Da Wikipedia :

Lo zero con segno è zero con un segno associato. Nell'aritmetica ordinaria, −0 = +0 = 0. Tuttavia, nel calcolo, alcune rappresentazioni numeriche consentono l'esistenza di due zeri, spesso indicati con −0 (zero negativo) e +0 (zero positivo) . Ciò si verifica in alcune rappresentazioni di numeri con segno per numeri interi e nella maggior parte delle rappresentazioni di numeri in virgola mobile. Il numero 0 è generalmente codificato come +0, ma può essere rappresentato da +0 o −0.

Lo standard IEEE 754 per l'aritmetica in virgola mobile (attualmente utilizzato dalla maggior parte dei computer e dei linguaggi di programmazione che supportano i numeri in virgola mobile) richiede sia +0 che −0. Gli zeri possono essere considerati una variante della riga del numero reale esteso in modo tale che 1 / −0 = −∞ e 1 / + 0 = + ∞, la divisione per zero non è definita solo per ± 0 / ± 0 e ± ∞ / ± ∞ .

L'articolo contiene ulteriori informazioni sulle diverse rappresentazioni.

Quindi questo è il motivo per cui, tecnicamente, entrambi gli zeri devono essere distinti.

Tuttavia, +0 === -0restituisce true. Perché (...) ?

Questo comportamento è esplicitamente definito nella sezione 11.9.6 , l' algoritmo di confronto sull'uguaglianza rigorosa (l'enfasi è in parte mia):

Il confronto x === y, dove xe ysono valori, produce vero o falso . Tale confronto viene eseguito come segue:

(...)

  • Se Tipo (x) è Numero, allora

    1. Se x è NaN, restituisce false.
    2. Se y è NaN, restituisce false.
    3. Se x ha lo stesso valore numerico di y, restituisce true.
    4. Se x è +0 e y è −0, restituisce true.
    5. Se x è −0 e y è +0, restituisce true.
    6. Restituisci falso.

(...)

(Lo stesso vale per +0 == -0btw.)

Sembra logicamente da trattare +0e -0uguale. Altrimenti dovremmo tenerne conto nel nostro codice e, personalmente, non voglio farlo;)


Nota:

ES2015 introduce un nuovo metodo di confronto, Object.is. Object.isdistingue esplicitamente tra -0e +0:

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

15
Anzi 1/0 === Infinity; // truee 1/-0 === -Infinity; // true.
user113716,

48
Quindi abbiamo 1 === 1e +0 === -0ma 1/+0 !== 1/-0. Che strano!
Randomblue,

8
@Random: penso che sia certamente meglio di +0 !== -0;) Questo potrebbe davvero creare problemi.
Felix Kling,

@FelixKling, o 0 !== +0/ 0 !== -0, che creerebbe davvero anche problemi!
Yanick Rochon,

5
In realtà, questi modelli comportamentali limitano il calcolo in matematica. Ad esempio la funzione 1 / x ha un valore infinito in 0, tuttavia è separata se ci stiamo avvicinando a 0 dal lato positivo del lato negativo; nel primo, il risultato è + inf, nel secondo, -inf.
Agoston Horvath,

19

Aggiungerò questo come risposta perché ho trascurato il commento di @utente113716.

Puoi provare per -0 in questo modo:

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

isMinusZero(0); // false
isMinusZero(-0); // true

6
Dovresti probabilmente controllare anche == 0, quanto sopra isMinusZero (-1e-323) restituisce vero!
Chris,

1
@Chris, il limite dell'esponente a doppia precisione è e±308, il tuo numero può essere rappresentato solo in forma denormalizzata e diverse implementazioni hanno opinioni diverse su dove supportarle o meno. Il punto è che su alcune macchine in alcune modalità a virgola mobile il tuo numero è rappresentato come -0e su altri come numero denormalizzato 0.000000000000001e-308. Tali carri allegorici, così divertenti
debolezza,

Questo potrebbe funzionare anche per altre lingue (ho provato per C e funziona)
Mukul Kumar

12

Ho appena trovato un esempio in cui +0 e -0 si comportano in modo molto diverso:

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

Fai attenzione: anche quando usi Math.round su un numero negativo come -0.0001, sarà effettivamente -0 e può rovinare alcuni calcoli successivi come mostrato sopra.

Un modo rapido e sporco per risolvere questo problema è fare smth come:

if (x==0) x=0;

o semplicemente:

x+=0;

Questo converte il numero in +0 nel caso in cui fosse -0.


Grazie. È strano che l'aggiunta di zero risolva il problema che ho riscontrato. "Se tutto il resto fallisce, aggiungi zero." Una lezione per la vita.
Microsis,

L'ho appena riscontrato anche in Math.atan (y / x), che (forse sorprendentemente) può gestire "y / x" in modo positivo o negativo infinito, tranne per il fatto che dà la risposta sbagliata nel caso in cui x sia -0. La sostituzione di "x" con "(x + 0)" lo risolve.
Jacob C. dice di reintegrare Monica il

5

Nello standard IEEE 754 utilizzato per rappresentare il tipo Numero in JavaScript, il segno è rappresentato da un bit (un 1 indica un numero negativo).

Di conseguenza, esiste un valore sia negativo che positivo per ciascun numero rappresentabile, incluso 0.

Questo è il motivo per cui sia -0e +0esistono.


3
Anche il complemento a due usa un po 'per il segno, ma ha solo uno zero (positivo).
Felix Kling,

1
Sì, ma nel complemento di Two anche il bit negativo fa parte del valore, quindi una volta impostato il bit negativo, non è più zero.
Arnaud Le Blanc,

3

Rispondere al titolo originale Are +0 and -0 the same?:

brainslugs83(nei commenti della risposta di Spudley) ha sottolineato un caso importante in cui +0 e -0 in JS non sono gli stessi - implementato come funzione:

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

Ciò, oltre allo standard, Math.signrestituirà il segno corretto di +0 e -0.


2

Esistono due valori possibili (rappresentazioni di bit) per 0. Questo non è univoco. Ciò può verificarsi soprattutto nei numeri in virgola mobile. Questo perché i numeri in virgola mobile sono effettivamente memorizzati come un tipo di formula.

I numeri interi possono essere memorizzati anche in modi separati. È possibile avere un valore numerico con un bit di segno aggiuntivo, quindi in uno spazio di 16 bit è possibile memorizzare un valore intero di 15 bit e un bit di segno. In questa rappresentazione, il valore 1000 (esadecimale) e 0000 sono entrambi 0, ma uno di essi è +0 e l'altro è -0.

Ciò potrebbe essere evitato sottraendo 1 dal valore intero in modo che varia da -1 a -2 ^ 16, ma ciò sarebbe scomodo.

Un approccio più comune è quello di memorizzare numeri interi in "due complementi", ma apparentemente ECMAscript ha scelto di non farlo. In questo metodo i numeri vanno da 0000 a 7FFF positivi. I numeri negativi iniziano da FFFF (-1) a 8000.

Naturalmente, le stesse regole si applicano anche a numeri interi più grandi, ma non voglio che la mia F si esaurisca. ;)


4
Ma non lo trovi +0 === -0un po 'strano. Perché ora abbiamo 1 === 1e +0 === -0ma1/+0 !== 1/-0 ...
Randomblue,

2
Ovviamente +0 è -0. Non è niente. Ma c'è un'enorme differenza tra + infinito e -infinito, vero? Quei numeri di inifinità possono anche essere il motivo per cui l'ECMA supporta sia +0 che -1.
GolezTrol

Non spieghi perché +0 === -0nonostante le due rappresentazioni dei bit siano diverse.
Randomblue,

1
+0 è -0 è 0, niente, nada, niente. Ha senso che siano uguali. Perchè il cielo è blu? Anche 4 + 3 è uguale a 1 + 6, sebbene le rappresentazioni siano diverse. Hanno rappresentazioni diverse (e quindi un valore di bit diverso), ma se confrontate vengono gestite come lo stesso zero, che sono.
GolezTrol

1
Essi sono non lo stesso. Vedi stackoverflow.com/questions/7223717/differentiated-0-and-0 per esempi che lo dimostrano.
Randomblue,

2

Possiamo usare Object.is per distinguere +0 e -0, e un'altra cosa, NaN==NaN.

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

Object.is(NaN,NaN) //true

1

Darei la colpa al metodo del confronto rigoroso sull'uguaglianza ('==='). Guarda la sezione 4d inserisci qui la descrizione dell'immagine

vedere 7.2.13 Confronto rigoroso di uguaglianza sulla specifica


0

Wikipedia ha un buon articolo per spiegare questo fenomeno: http://en.wikipedia.org/wiki/Signed_zero

In breve, sia +0 che -0 sono definiti nelle specifiche IEEE in virgola mobile. Entrambi sono tecnicamente distinti da 0 senza segno, che è un numero intero, ma in pratica valutano tutti a zero, quindi la distinzione può essere ignorata per tutti gli scopi pratici.


2
Questo non è del tutto corretto - 1 / -0 == 1/0 restituisce falso ad esempio in javascript. Non "valutano" uno zero magico senza segno, in quanto non esiste alcun concetto come "uno zero intero senza segno" in IEEE 754.
BrainSlugs83
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.