Domanda

Ho sentito che puoi spostare un numero a destra di 0,5 anziché usare Math.floor (). Ho deciso di controllare i suoi limiti per assicurarmi che fosse un sostituto adatto, quindi ho verificato i seguenti valori e ottenuto i seguenti risultati in Google Chrome:


2.5 >> .5 == 2;
2.9999 >> .5 == 2;
2.999999999999999 >> .5 == 2;  // 15 9s
2.9999999999999999 >> .5 == 3;  // 16 9s

Dopo un po 'di armeggiamento, ho scoperto che il valore più alto possibile di due che, se spostato a destra di 0,5, produrrebbe 2 è 2.9999999999999997779553950749686919152736663818359374999999¯ (con 9 ripetizioni) in Chrome e Firefox. Il numero è 2.9999999999999997779¯ in IE.

La mia domanda è: qual è il significato del numero .0000000000000007779553950749686919152736663818359374? È un numero molto strano e ha davvero suscitato la mia curiosità.

Ho cercato di trovare una risposta o almeno una sorta di modello, ma penso che il mio problema risieda nel fatto che non capisco davvero l'operazione bit a bit. Capisco l'idea in linea di principio, ma spostare una sequenza di bit di 0,5 non ha alcun senso per me. Qualsiasi aiuto è apprezzato.

Per la cronaca, la strana sequenza di cifre cambia con 2 ^ x. I valori più alti possibili dei seguenti numeri che si troncano ancora correttamente:

for 0: 0.9999999999999999444888487687421729788184165954589843749¯
for 1: 1.9999999999999999888977697537484345957636833190917968749¯
for 2-3: x+.99999999999999977795539507496869191527366638183593749¯
for 4-7: x+.9999999999999995559107901499373838305473327636718749¯
for 8-15: x+.999999999999999111821580299874767661094665527343749¯
...and so forth
È stato utile?

Soluzione

In realtà, stai semplicemente finendo per fare un floor () sul primo operando, senza che siano in corso operazioni in virgola mobile. Poiché le operazioni bit shift di shift sinistro e destro hanno senso solo con operandi interi, il motore JavaScript sta convertendo prima i due operandi in numeri interi:

2.999999 >> 0.5

diventa:

Math.floor(2.999999) >> Math.floor(0.5)

Che a sua volta è:

2 >> 0

Lo spostamento di 0 bit significa " non fare uno spostamento " e quindi si finisce con il primo operando, semplicemente troncato a un numero intero.

Il codice sorgente di SpiderMonkey ha:

switch (op) {
  case JSOP_LSH:
  case JSOP_RSH:
    if (!js_DoubleToECMAInt32(cx, d, &i)) // Same as Math.floor()
        return JS_FALSE;
    if (!js_DoubleToECMAInt32(cx, d2, &j)) // Same as Math.floor()
        return JS_FALSE;
    j &= 31;
    d = (op == JSOP_LSH) ? i << j : i >> j;
    break;

Stai vedendo un " arrotondamento per eccesso " con determinati numeri è dovuto al fatto che il motore JavaScript non è in grado di gestire cifre decimali oltre una certa precisione e quindi il numero finisce per essere arrotondato al numero intero successivo. Prova questo nel tuo browser:

alert(2.999999999999999);

Riceverai 2.999999999999999. Ora prova ad aggiungere un altro 9:

alert(2.9999999999999999);

Otterrai un 3.

Altri suggerimenti

Questa è forse la peggior idea che abbia mai visto. Il suo unico scopo possibile per esistere è vincere un contest di codice offuscato. Non c'è alcun significato per i numeri lunghi che hai pubblicato: sono un artefatto dell'implementazione in virgola mobile sottostante, filtrata attraverso dio, sa quanti strati intermedi. Lo spostamento di bit di un numero frazionario di byte è folle e sono sorpreso che non sollevi un'eccezione, ma questo è Javascript, sempre pronto a ridefinire "folle".

Se fossi in te, eviterei mai di usare questa "funzione". Il suo unico valore è come possibile causa principale per una condizione di errore insolita. Usa Math.floor () e abbi pietà del prossimo programmatore che manterrà il codice.


Confermando un paio di sospetti che avevo durante la lettura della domanda:

  • Spostando a destra qualsiasi numero frazionario x di qualsiasi numero frazionario y troncerà semplicemente x , dando lo stesso risultato di Math .floor () mentre confondi a fondo il lettore.
  • 2.999999999999999777955395074968691915 ... è semplicemente il numero più grande che può essere differenziato da " 3 " ;. Prova a valutarlo da solo - se aggiungi qualcosa ad esso, valuterà 3. Questo è un artefatto del browser e dell'implementazione in virgola mobile del sistema locale.

Se vuoi approfondire, leggi " Quello che ogni scienziato informatico dovrebbe sapere sull'aritmetica in virgola mobile " ;: http://docs.sun.com/source/806-3568/ncg_goldberg.html

Non credo che il tuo turno giusto sia rilevante. Sei semplicemente oltre la risoluzione di una costante in virgola mobile a doppia precisione.

In Chrome:

var x = 2.999999999999999777955395074968691915273666381835937499999;
var y = 2.9999999999999997779553950749686919152736663818359375;

document.write("x=" + x);
document.write(" y=" + y);

Stampa: x = 2.9999999999999996 y = 3

Prova questo javascript:   avviso (parseFloat (" 2,9999999999999997779553950749686919152736663818359374999999 "));

Quindi prova questo:   avviso (parseFloat (" 2,9999999999999997779553950749686919152736663818359375 "));

Quello che stai vedendo è una semplice imprecisione in virgola mobile. Per ulteriori informazioni a riguardo, vedere questo ad esempio: http://en.wikipedia.org/wiki / floating point # Accuracy_problems .

Il problema di base è che il più vicino a cui un valore in virgola mobile può arrivare a rappresentare il secondo numero è maggiore o uguale a 3, mentre la chiusura che un float può raggiungere al primo numero è strettamente inferiore a tre.

/ p>

Per quanto riguarda il motivo per cui il giusto spostamento di 0,5 fa qualcosa di sensato, sembra che 0,5 si stia convertendo in anticipo in un int (0). Quindi il float originale (2.999 ...) viene convertito in un int per troncamento, come al solito.

L'operatore di spostamento a destra funziona solo su numeri interi (entrambi i lati). Quindi, lo spostamento verso destra di 0,5 bit dovrebbe essere esattamente equivalente allo spostamento verso destra di 0 bit. Inoltre, il lato sinistro viene convertito in un numero intero prima dell'operazione di spostamento, che fa la stessa cosa di Math.floor ().

  

Sospetto che la conversione 2.9999999999999997779553950749686919152736663818359374999999   alla sua rappresentazione binaria sarebbe illuminante. Probabilmente è solo 1 bit diverso   dal vero 3.

Buona supposizione, ma niente sigaro. Poiché il numero FP a doppia precisione ha 53 bit, l'ultimo numero FP prima di 3 è effettivamente (esatto): 2.999999999999999555910790149937383830547332763671875

Ma perché lo è 2,9999999999999997779553950749686919152736663818359375

(e questo è esatto, non 49999 ...!)

che è più alto rispetto all'ultima unità visualizzabile? Arrotondamento. La routine di conversione (da stringa a numero) è semplicemente correttamente programmata per arrotondare l'ingresso al successivo numero in virgola mobile.

2,999999999999999555910790149937383830547332763671875

....... (valori tra, crescente) - > arrotondare per difetto

2,9999999999999997779553950749686919152736663818359375

....... (valori tra, crescente) - > arrotondare per eccesso a 3

3

L'input di conversione deve usare la massima precisione. Se il numero è esattamente la metà tra quei due numeri fp (che è 2.9999999999999997779553950749686919152736663818359375) l'arrotondamento dipende dalle bandiere posizionate. L'arrotondamento predefinito è arrotondato a pari, il che significa che il numero verrà arrotondato al numero pari successivo.

Ora

3 = 11. (binario)

2.999 ... = 10.11111111111 ...... (binario)

Tutti i bit sono impostati, il numero è sempre dispari. Ciò significa che il numero esatto della metà verrà arrotondato per eccesso, quindi stai ottenendo lo strano periodo ..... 49999 perché deve essere più piccolo della metà esatta per essere distinguibile da 3.

Sospetto che la conversione 2.9999999999999997779553950749686919152736663818359374999999 nella sua rappresentazione binaria sarebbe illuminante. Probabilmente è solo 1 bit diverso dal vero 3.

E per aggiungere alla risposta di John, le probabilità che questo sia più performante di Math.floor sono minuscole.

Non so se JavaScript utilizza numeri in virgola mobile o una sorta di libreria a precisione infinita, ma in entrambi i casi, si otterranno errori di arrotondamento su un'operazione come questa, anche se è abbastanza ben definita.

Va ??notato che il numero ".0000000000000007779553950749686919152736663818359374" è probabilmente il Epsilon , definito come " il numero più piccolo E tale che (1 + E ) > 1. "

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top