+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.