Verwenden Sie bitweises ODER 0, um eine Zahl zu bestimmen

192

Ein Kollege von mir stieß auf eine Methode, um Float-Zahlen mit einem bitweisen oder:

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

Wir haben darüber gesprochen und uns ein paar Dinge gefragt.

  • Wie funktioniert es? Unsere Theorie war, dass die Verwendung eines solchen Operators die Zahl in eine ganze Zahl umwandelt und so den Bruchteil entfernt
  • Hat es irgendwelche Vorteile gegenüber dem Tun Math.floor? Vielleicht ist es etwas schneller? (Wortspiel nicht beabsichtigt)
  • Hat es irgendwelche Nachteile? Vielleicht funktioniert es in einigen Fällen nicht? Klarheit ist offensichtlich, da wir es herausfinden mussten, und nun, ich schreibe diese Frage.

Vielen Dank.

Alex Turpin
quelle
6
Nachteil: Es funktioniert nur bis zu 2 ^ 31−1, was ungefähr 2 Milliarden (10 ^ 9) entspricht. Der maximale Zahlenwert liegt übrigens bei 10 ^ 308.
Šime Vidas
12
Beispiel: 3000000000.1 | 0ergibt -1294967296. Daher kann diese Methode nicht für Geldberechnungen angewendet werden (insbesondere in Fällen, in denen Sie mit 100 multiplizieren, um Dezimalzahlen zu vermeiden).
Šime Vidas
13
@ ŠimeVidas Floats sollten auch nicht für Geldberechnungen verwendet werden
George Reith
20
Es ist kein Bodenbelag, es schneidet ab (Rundung in Richtung 0).
Bartłomiej Zalewski
3
@sequence versuchen Sie, 0.1 + 0.2 == 0.3eine JavaScript-Konsole einzugeben . Wenn Ihre Sprache dies unterstützt, sollten Sie einen Dezimaltyp verwenden. Wenn nicht, speichern Sie stattdessen Cent.
Alex Turpin

Antworten:

160

Wie funktioniert es? Unsere Theorie war, dass die Verwendung eines solchen Operators die Zahl in eine ganze Zahl umwandelt und so den Bruchteil entfernt

Alle bitweisen Operationen mit Ausnahme der vorzeichenlosen Rechtsverschiebung >>>arbeiten mit vorzeichenbehafteten 32-Bit-Ganzzahlen. Wenn Sie also bitweise Operationen verwenden, wird ein Float in eine Ganzzahl konvertiert.

Hat es irgendwelche Vorteile gegenüber Math.floor? Vielleicht ist es etwas schneller? (Wortspiel nicht beabsichtigt)

http://jsperf.com/or-vs-floor/2 scheint etwas schneller zu sein

Hat es irgendwelche Nachteile? Vielleicht funktioniert es in einigen Fällen nicht? Klarheit ist offensichtlich, da wir es herausfinden mussten, und nun, ich schreibe diese Frage.

  • Wird jsLint nicht bestehen.
  • Nur vorzeichenbehaftete 32-Bit-Ganzzahlen
  • Seltsames Vergleichsverhalten : Math.floor(NaN) === NaN, while(NaN | 0) === 0
Joe
quelle
9
@harold in der Tat, weil es in der Tat nicht rund ist, nur abgeschnitten.
Alex Turpin
5
Ein weiterer möglicher Nachteil ist, dass Math.floor(NaN) === NaNwährend (NaN | 0) === 0. Dieser Unterschied kann in einigen Anwendungen wichtig sein.
Ted Hopp
4
Ihr jsperf liefert Leistungsinformationen für leere Schleifen auf Chrom aufgrund einer schleifeninvarianten Codebewegung. Ein etwas besserer Leistungstest wäre: jsperf.com/floor-performance/2
Sam Giles
4
Dies ist ein Standardteil von asm.js(wo ich zuerst davon erfahren habe). Es ist schneller, wenn auch aus keinem anderen Grund, da es keine Funktion für das MathObjekt aufruft , eine Funktion, die jederzeit wie in ersetzt werden kann Math.floor = function(...).
Gman
3
(value | 0) === valuekönnte verwendet werden, um zu überprüfen, ob ein Wert tatsächlich eine Ganzzahl und nur eine Ganzzahl ist (wie im verknüpften Elm-Quellcode @ dwayne-crooks). Und foo = foo | 0könnte verwendet werden, um einen beliebigen Wert in eine Ganzzahl zu zwingen (wobei 32-Bit-Zahlen abgeschnitten werden und alle Nicht-Zahlen 0 werden).
David Michael Gregg
36

Dies ist eine Kürzung im Gegensatz zu Bodenbelägen. Howards Antwort ist irgendwie richtig; Aber ich würde hinzufügen, dass Math.floordies genau das tut, was es in Bezug auf negative Zahlen tun soll. Mathematisch ist das ein Boden.

Im Fall , dass Sie oben beschrieben, war der Programmierer mehr daran interessiert, Abschneiden oder das Dezimalsystem Abhacken vollständig. Die von ihnen verwendete Syntax verdeckt jedoch die Tatsache, dass sie den Float in einen Int konvertieren.

Chad La Guardia
quelle
7
Dies ist die richtige Antwort, eine akzeptierte nicht. Fügen Sie hinzu, dass Math.floor(8589934591.1)das erwartete Ergebnis erzeugt wird,8589934591.1 | 0 NICHT .
Salman A
21

In ECMAScript 6, der das Äquivalent |0ist Math.trunc , sollte Art , sage ich:

Gibt den integralen Teil einer Zahl zurück, indem alle gebrochenen Ziffern entfernt werden. Es schneidet nur den Punkt und die Ziffern dahinter ab, egal ob das Argument eine positive oder eine negative Zahl ist.

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
zangw
quelle
6
Außer der Tatsache, dass Math.trunc()mit einer Zahl höher oder gleich 2 ^ 31 arbeiten und | 0nicht
Nolyurn
10

Ihr erster Punkt ist richtig. Die Zahl wird in eine Ganzzahl umgewandelt und somit werden alle Dezimalstellen entfernt. Bitte beachten Sie, dass Math.floorauf die nächste Ganzzahl in Richtung minus unendlich gerundet wird und daher bei Anwendung auf negative Zahlen ein anderes Ergebnis erzielt wird.

Howard
quelle
5

Javascript steht Numberfür 64-Bit-Floating-Zahlen mit doppelter Genauigkeit .

Math.floor arbeitet in diesem Sinne.

Bitweise Operationen funktionieren in vorzeichenbehafteten 32-Bit- Ganzzahlen. Ganzzahlen mit 32-Bit-Vorzeichen verwenden das erste Bit als negativen Bezeichner und die anderen 31 Bits sind die Zahl. Aus diesem Grund sind die zulässigen 32-Bit-Nummern mit minimaler und maximaler Signatur -2.147.483.648 bzw. 2147483647 (0x7FFFFFFFF).

Wenn Sie es also tun | 0, tun Sie es im Wesentlichen & 0xFFFFFFFF. Dies bedeutet, dass jede Zahl, die als 0x80000000 (2147483648) oder höher dargestellt wird, als negative Zahl zurückgegeben wird.

Beispielsweise:

 // 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

Ebenfalls. Bitweise Operationen "boden" nicht. Sie kürzen , was das gleiche ist wie zu sagen, sie runden am nächsten 0. Sobald Sie gehen um zu negativen Zahlen Math.floorRunde nach unten , während bitweise Start Rundung nach oben .

Wie ich bereits sagte, Math.floorist sicherer, weil es mit schwebenden 64-Bit-Zahlen arbeitet. Bitweise ist zwar schneller , aber auf den mit 32 Bit signierten Bereich beschränkt.

Zusammenfassen:

  • Bitweise funktioniert genauso, wenn Sie von arbeiten 0 to 2147483647 .
  • Bitweise ist 1 Zahl aus, wenn Sie von arbeiten -2147483647 to 0 .
  • Bitweise ist für Zahlen kleiner -2147483648und größer als völlig anders 2147483647.

Wenn Sie die Leistung wirklich optimieren und beides verwenden möchten:

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

Das Hinzufügen Math.truncfunktioniert wie bitweise Operationen. So können Sie Folgendes tun:

function trunc(n) {
    if (n > -0x80000000 && n < 0x80000000) {
      return n & 0xFFFFFFFF;
    }
    return Math.trunc(n);
}
ShortFuse
quelle
5
  • Die Spezifikationen besagen, dass es in eine Ganzzahl konvertiert wird:

    Sei lnum ToInt32 (lval).

  • Leistung: Dies wurde bereits bei jsperf getestet .

Hinweis: Toter Link zur Spezifikation entfernt

pimvdb
quelle