Utilizzo bit per bit OR 0 per inserire un numero al piano


193

Un mio collega si è imbattuto in un metodo per trasferire i numeri float usando un bit a bit o:

var a = 13.6 | 0; //a == 13

Ne stavamo parlando e ci chiedevamo alcune cose.

  • Come funziona? La nostra teoria era che l'uso di un tale operatore lancia il numero su un numero intero, rimuovendo così la parte frazionaria
  • Ha qualche vantaggio rispetto al fare Math.floor? Forse è un po 'più veloce? (gioco di parole non previsto)
  • Ha degli svantaggi? Forse non funziona in alcuni casi? La chiarezza è ovvia, dal momento che abbiamo dovuto capirlo, e bene, sto scrivendo questa domanda.

Grazie.


6
Svantaggio: funziona solo fino a 2 ^ 31−1, ovvero circa 2 miliardi (10 ^ 9). Il valore Numero massimo è di circa 10 ^ 308 btw.
Šime Vidas,

12
Esempio: 3000000000.1 | 0restituisce -1294967296. Quindi questo metodo non può essere applicato per i calcoli del denaro (specialmente nei casi in cui si moltiplica per 100 per evitare i numeri decimali).
Šime Vidas,

13
@ ŠimeVidas Floats non dovrebbe essere usato anche nei calcoli del denaro
George Reith,

20
Non sta pavimentando, si sta troncando (arrotondando verso 0).
Bartłomiej Zalewski,

3
@sequence prova a digitare 0.1 + 0.2 == 0.3in una console JavaScript. Se la tua lingua lo supporta, dovresti usare un tipo decimale. In caso contrario, conservare invece i centesimi.
Alex Turpin,

Risposte:


161

Come funziona? La nostra teoria era che l'uso di un tale operatore lancia il numero su un numero intero, rimuovendo così la parte frazionaria

Tutte le operazioni bit a bit tranne lo spostamento a destra senza segno >>>, funzionano su numeri interi a 32 bit con segno. Quindi l'utilizzo di operazioni bit a bit convertirà un float in un numero intero.

Ha qualche vantaggio rispetto a Math.floor? Forse è un po 'più veloce? (gioco di parole non previsto)

http://jsperf.com/or-vs-floor/2 sembra leggermente più veloce

Ha degli svantaggi? Forse non funziona in alcuni casi? La chiarezza è ovvia, dal momento che abbiamo dovuto capirlo, e bene, sto scrivendo questa domanda.

  • Non passerà jsLint.
  • Solo numeri interi con segno a 32 bit
  • Strano comportamento comparativo:, Math.floor(NaN) === NaNmentre(NaN | 0) === 0

9
@harold davvero, perché in realtà non arrotonda, semplicemente si tronca.
Alex Turpin,

5
Un altro possibile svantaggio è che Math.floor(NaN) === NaN, mentre (NaN | 0) === 0. Questa differenza potrebbe essere importante in alcune applicazioni.
Ted Hopp,

4
Il tuo jsperf sta fornendo informazioni sulle prestazioni per loop vuoti su Chrome a causa del movimento del codice invariante del loop. Un test di perf leggermente migliore sarebbe: jsperf.com/floor-performance/2
Sam Giles

4
Questa è una parte standard di asm.js(dove l'ho imparato per la prima volta). È più veloce se non per nessun altro motivo perché non chiama una funzione Mathsull'oggetto, una funzione che può essere sostituita in qualsiasi momento come in Math.floor = function(...).
Gman,

3
(value | 0) === valuepotrebbe essere usato per verificare che un valore sia in realtà un numero intero e solo un numero intero (come nel codice sorgente Elm @ dwayne-truffatori collegati). E foo = foo | 0potrebbe essere usato per forzare qualsiasi valore in un numero intero (dove i numeri a 32 bit vengono troncati e tutti i non numeri diventano 0).
David Michael Gregg,

36

Questo è il troncamento rispetto alla pavimentazione. La risposta di Howard è in qualche modo corretta; Ma aggiungerei che Math.floorfa esattamente quello che dovrebbe fare rispetto ai numeri negativi. Matematicamente, questo è un pavimento.

Nel caso sopra descritto, il programmatore era più interessato al troncamento o al taglio del decimale completamente. Sebbene, la sintassi che hanno usato in qualche modo oscura il fatto che stanno convertendo il float in un int.


7
Questa è la risposta corretta, accettata non lo è. Aggiungilo a quello che Math.floor(8589934591.1)produce il risultato atteso, 8589934591.1 | 0 NON .
Salman A

21

In ECMAScript 6, l'equivalente di |0è Math.trunc , tipo di dovrei dire:

Restituisce la parte integrale di un numero rimuovendo le cifre frazionarie. Tronca semplicemente il punto e le cifre dietro di esso, indipendentemente dal fatto che l'argomento sia un numero positivo o un numero negativo.

Math.trunc(13.37)   // 13
Math.trunc(42.84)   // 42
Math.trunc(0.123)   //  0
Math.trunc(-0.123)  // -0
Math.trunc("-1.123")// -1
Math.trunc(NaN)     // NaN
Math.trunc("foo")   // NaN
Math.trunc()        // NaN

6
Tranne il fatto che Math.trunc()funziona con un numero maggiore o uguale a 2 ^ 31 e | 0non funziona
Nolyurn

10

Il tuo primo punto è corretto. Il numero viene castato su un numero intero e quindi le cifre decimali vengono rimosse. Nota che Math.floorarrotondano al numero intero successivo verso meno infinito e quindi danno un risultato diverso se applicato a numeri negativi.


5

Javascript rappresenta Numbercome doppia precisione numeri mobile a 64 bit .

Math.floor funziona con questo in mente.

Operazioni bit per bit lavorano in 32 bit firmato interi. Gli interi con segno a 32 bit utilizzano il primo bit come significante negativo e gli altri 31 bit sono il numero. Per questo motivo, il numero minimo e massimo consentito per i numeri con segno a 32 bit sono -2.147.483.648 e 2147483647 (0x7FFFFFFFF), rispettivamente.

Quindi, quando lo fai | 0, lo stai essenzialmente facendo & 0xFFFFFFFF. Ciò significa che qualsiasi numero rappresentato come 0x80000000 (2147483648) o superiore restituirà un numero negativo.

Per esempio:

 // Safe
 (2147483647.5918 & 0xFFFFFFFF) ===  2147483647
 (2147483647      & 0xFFFFFFFF) ===  2147483647
 (200.59082098    & 0xFFFFFFFF) ===  200
 (0X7FFFFFFF      & 0xFFFFFFFF) ===  0X7FFFFFFF

 // Unsafe
 (2147483648      & 0xFFFFFFFF) === -2147483648
 (-2147483649     & 0xFFFFFFFF) ===  2147483647
 (0x80000000      & 0xFFFFFFFF) === -2147483648
 (3000000000.5    & 0xFFFFFFFF) === -1294967296

Anche. Le operazioni bit a bit non "pavimentano". Essi troncano , che è lo stesso che dire, si completano più vicino 0. Una volta che si va in giro per i numeri negativi, Math.floorgiri verso il basso mentre bit a bit iniziano arrotondamento in su .

Come ho detto prima, Math.floorè più sicuro perché funziona con numeri fluttuanti a 64 bit. Bitwise è più veloce , sì, ma limitato a ambito firmato a 32 bit.

Riassumere:

  • Bitwise funziona allo stesso modo se lavori da 0 to 2147483647.
  • Bitwise ha 1 numero di sconto se lavori da -2147483647 to 0.
  • Bitwise è completamente diverso per i numeri minori di -2147483648e maggiori di 2147483647.

Se vuoi davvero modificare le prestazioni e utilizzare entrambi:

function floor(n) {
    if (n >= 0 && n < 0x80000000) {
      return n & 0xFFFFFFFF;
    }
    if (n > -0x80000000 && n < 0) {
      return (n - 1) & 0xFFFFFFFF;
    }
    return Math.floor(n);
}

Solo per aggiungere Math.truncfunziona come operazioni bit per bit. Quindi puoi farlo:

function trunc(n) {
    if (n > -0x80000000 && n < 0x80000000) {
      return n & 0xFFFFFFFF;
    }
    return Math.trunc(n);
}

5
  • Le specifiche dicono che è convertito in un numero intero:

    Sia lnum ToInt32 (lval).

  • Prestazioni: questo è già stato testato su jsperf .

nota: link morto alle specifiche rimosso

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.