Was ist die beste Methode, um Gleitkommazahlen in JavaScript in eine Ganzzahl umzuwandeln?

StackOverflow https://stackoverflow.com/questions/131406

  •  02-07-2019
  •  | 
  •  

Frage

Es gibt verschiedene Methoden zum Konvertieren von Gleitkommazahlen in Ganzzahlen in JavaScript.Meine Frage ist, welche Methode die beste Leistung bietet, am kompatibelsten ist oder als die beste Vorgehensweise gilt.

Hier sind einige Methoden, die ich kenne:

var a = 2.5;
window.parseInt(a); // 2
Math.floor(a);      // 2
a | 0;              // 2

Ich bin mir sicher, dass es da draußen noch andere gibt.Vorschläge?

War es hilfreich?

Lösung

Entsprechend Diese Internetseite:

parseInt wird gelegentlich verwendet, um eine Gleitkommazahl in eine Ganzzahl umzuwandeln.Für diese Aufgabe ist es sehr schlecht geeignet, denn wenn sein Argument vom numerischen Typ ist, wird es zuerst in eine Zeichenfolge umgewandelt und dann als Zahl geparst ...

Zum Runden von Zahlen auf ganze Zahlen sind Math.round, Math.ceil und Math.floor vorzuziehen ...

Andere Tipps

Von "Javascript: The Good Parts" von Douglas Crockford:

Number.prototype.integer = function () {
    return Math[this < 0 ? 'ceil' : 'floor'](this);
}

Dadurch, dass Ihr ein Verfahren zu jedem Number-Objekt hinzufügen.

Dann können Sie es so verwenden:

var x = 1.2, y = -1.2;

x.integer(); // 1
y.integer(); // -1

(-10 / 3).integer(); // -3

Offenbar Doppel bitweise nicht ist der schnellste Weg, um Boden eine Zahl:

var x = 2.5;
console.log(~~x); // 2

Gebraucht hier um einen Artikel zu sein, immer ein 404 jetzt aber: http: // james.padolsey.com/javascript/double-bitwise-not/

Google hat es zwischengespeichert: http://74.125.155.132/search?q=cache:wpZnhsbJGt0J:james.padolsey.com/javascript/double-bitwise-not/+double+bitwise+not&cd=1&hl=en&ct=clnk&gl=us

Aber die Wayback Machine rettet den Tag! http: // web. archive.org/web/20100422040551/http://james.padolsey.com/javascript/double-bitwise-not/

Die Antwort ist bereits gegeben, aber nur klar zu sein.

Mit der Math-Bibliothek für diesen. rund, ceil oder Bodenfunktionen.

parseInt ist eine Zeichenfolge in einen int für die Umwandlung der nicht, was hier gebraucht wird

toFixed ist ein Schwimmer in eine Zeichenfolge für die Umwandlung auch nicht, was hier gebraucht wird

Da die Mathematische Funktionen werden keine Konvertierungen oder aus einem String tun wird es schneller als alle anderen Entscheidungen sein, die ohnehin falsch sind.

Sie können mit Nummer (a) .toFixed (0);

oder auch nur a.toFixed (0);

Edit:

Das ist auf 0 Orte Runden, etwas anders als Kürzen, und als jemand anderes vorgeschlagen, kehrt toFixed einen String, kein rohes integer. Nützlich für die Anzeige.

var num = 2.7;  // typeof num is "Number"
num.toFixed(0) == "3"

Der beste Weg kommt darauf an An:

  • Rundungsmodus:Was Art der Rundung (von Float zu Integer), die Sie erwarten/erfordern
    für positive und/oder negative Zahlen, die einen Bruchteil haben.
    Häufige Beispiele:
    float | trunc | floor |  ceil | near (half up)
    ------+-------+-------+-------+---------------
    +∞    |   +∞  |   +∞  |   +∞  |   +∞  
    +2.75 |   +2  |   +2  |   +3  |   +3
    +2.5  |   +2  |   +2  |   +3  |   +3
    +2.25 |   +2  |   +2  |   +3  |   +2
    +0    |   +0  |   +0  |   +0  |   +0
     NaN  |  NaN  |  NaN  |  NaN  |  NaN
    -0    |   -0  |   -0  |   -0  |   -0
    -2.25 |   -2  |   -3  |   -2  |   -2
    -2.5  |   -2  |   -3  |   -2  |   -2
    -2.75 |   -2  |   -3  |   -2  |   -3
    -∞    |   -∞  |   -∞  |   -∞  |   -∞  
    
    Für die Konvertierung von Float in Integer haben wir häufig erwarten „Verkürzung“
    (auch bekannt als „Rund gegen Null“ aka „von der Unendlichkeit wegrunden“).
    Tatsächlich wird dadurch lediglich der Bruchteil einer Gleitkommazahl „abgeschnitten“.
    Die meisten Techniken und (intern) integrierten Methoden verhalten sich auf diese Weise.
  • Eingang:wie Ihre (Gleitkomma-)Zahl dargestellt wird:
    • String
      Allgemein Basis/Basis:10 (dezimal)
    • Gleitkomma ('intern') Number
  • Ausgabe:was Sie mit dem resultierenden Wert machen möchten:
    • (Zwischen-)Ausgabe String (Standardbasis 10) (auf dem Bildschirm)
    • Führen Sie weitere Berechnungen zum resultierenden Wert durch
  • Reichweite:
    In welchem ​​Zahlenbereich erwarten Sie Eingabe-/Berechnungsergebnisse?
    und für welchen Bereich erwarten Sie eine entsprechende „richtige“ Ausgabe.

Nur nach Wenn diese Überlegungen beantwortet sind, können wir über geeignete Methode(n) und Geschwindigkeit nachdenken!


Gemäß ECMAScript 262-Spezifikation: alle Zahlen (Typ Number) in Javascript werden dargestellt/gespeichert in:
"IEEE 754 Double Precision Floating Point (binary64)" Format.
So sind ganze Zahlen Auch vertreten in der Dasselbe Gleitkommaformat (als Zahlen ohne Bruch).
Notiz:die meisten Implementierungen Tun Verwenden Sie effizientere (für Geschwindigkeit und Speichergröße) Integer-Typen im Inneren wenn möglich!

Da dieses Format 1 Vorzeichenbit, 11 Exponentenbits und die ersten 53 signifikanten Bits („Mantisse“) speichert, können wir Folgendes sagen: nur Number-Werte zwischen -252 Und +252 kann einen Bruch haben.
Mit anderen Worten: alle darstellbares Positives und Negatives Number-Werte zwischen 252 zu (fast) 2(211/2=1024) (an diesem Punkt ruft das Format es auf ein Tag Infinity) sind bereits ganze Zahlen (intern gerundet, da keine Bits mehr vorhanden sind, um die verbleibenden Nachkommastellen und/oder niedrigstwertigen ganzzahligen Ziffern darzustellen).

Und da ist noch das erste Problem:
Sie können den internen Rundungsmodus von nicht steuern Number-Ergebnisse für die integrierten Literal/String-zu-Float-Konvertierungen (Rundungsmodus:IEEE 754-2008 „Auf den nächsten Wert runden, auf Gerade binden“) und integrierte arithmetische Operationen (Rundungsmodus:IEEE 754-2008 „round-to-nearest“).
Zum Beispiel:
252+0.25 = 4503599627370496.25 wird gerundet und gespeichert als: 4503599627370496
252+0.50 = 4503599627370496.50 wird gerundet und gespeichert als: 4503599627370496
252+0.75 = 4503599627370496.75 wird gerundet und gespeichert als: 4503599627370497
252+1.25 = 4503599627370497.25 wird gerundet und gespeichert als: 4503599627370497
252+1.50 = 4503599627370497.50 wird gerundet und gespeichert als: 4503599627370498
252+1.75 = 4503599627370497.75 wird gerundet und gespeichert als: 4503599627370498
252+2.50 = 4503599627370498.50 wird gerundet und gespeichert als: 4503599627370498
252+3.50 = 4503599627370499.50 wird gerundet und gespeichert als: 4503599627370500

Um die Rundung zu kontrollieren Number benötigt einen Bruchteil (und mindestens ein Bit, um diesen darzustellen), andernfalls gibt ceil/floor/trunc/near die Ganzzahl zurück, die Sie eingegeben haben.

Um eine Zahl bis zur x signifikanten Dezimalstelle(n) korrekt zu begrenzen/zu kürzen, ist uns nur wichtig, ob der entsprechende niedrigste und höchste Dezimalbruchwert uns nach dem Runden immer noch einen binären Bruchwert ergibt (also nicht auf Obergrenze oder Boden gekürzt wird). die nächste Ganzzahl).
Wenn Sie beispielsweise eine „korrekte“ Rundung (für Decke/Boden/Trennung) auf bis zu 1 signifikante Dezimalstelle erwarten (x.1 to x.9), wir brauchen mindestens 3 Bits (nicht 4), um uns zu geben A binärer Bruchwert:
0.1 ist näher dran 1/(23=8)=0.125 als es ist 0 Und 0.9 ist näher dran 1-1/(23=8)=0.875 als es ist 1.

nur bis zu ±2(53-3=50) Haben alle darstellbaren Werte einen binären Bruch ungleich Null für nicht mehr als? Erste signifikante Dezimalbruchziffer (Werte). x.1 Zu x.9).
Für 2 Dezimalstellen ±2(53-6=47), für 3 Dezimalstellen ±2(53-9=44), für 4 Dezimalstellen ±2(53-13=40), für 5 Dezimalstellen ±2(53-16=37), für 6 Dezimalstellen ±2(53-19=34), für 7 Dezimalstellen ±2(53-23=30), für 8 Dezimalstellen ±2(53-26=27), für 9 Dezimalstellen ±2(53-29=24), für 10 Dezimalstellen ±2(53-33=20), für 11 Dezimalstellen ±2(53-36=17), usw..

A „Sichere Ganzzahl“ in Javascript ist eine Ganzzahl:

  • das kann sein genau dargestellt als IEEE-754-Zahl mit doppelter Genauigkeit, und
  • dessen IEEE-754-Darstellung kann nicht das Ergebnis der Rundung einer beliebigen anderen Ganzzahl sein, um sie an die IEEE-754-Darstellung anzupassen
    (wenngleich ±253 (als exakte Potenz von 2) kann genau dargestellt werden, das ist es nicht eine sichere Ganzzahl, weil es auch hätte sein können ±(253+1) bevor es gerundet wurde, um in das Maximum der 53 höchstwertigen Bits zu passen).

Dies definiert effektiv einen Teilmengenbereich von (sicher darstellbaren) ganzen Zahlen zwischen -253 Und +253:

  • aus: -(253 - 1) = -9007199254740991 (inklusive)
    (Eine Konstante, die als statische Eigenschaft bereitgestellt wird Number.MIN_SAFE_INTEGER seit ES6)
  • Zu: +(253 - 1) = +9007199254740991 (inklusive)
    (Eine Konstante, die als statische Eigenschaft bereitgestellt wird Number.MAX_SAFE_INTEGER seit ES6)
    Triviales Polyfill für diese beiden neuen ES6-Konstanten:

    Number.MIN_SAFE_INTEGER || (Number.MIN_SAFE_INTEGER=
      -(Number.MAX_SAFE_INTEGER=9007199254740991) //Math.pow(2,53)-1
    );
    


Seit ES6 gibt es auch eine kostenlose statische Methode Number.isSafeInteger() Hiermit wird geprüft, ob der übergebene Wert vom Typ ist Number und ist eine Ganzzahl innerhalb des sicheren Ganzzahlbereichs (gibt einen booleschen Wert zurück). true oder false).
Notiz:werde auch wiederkommen false für: NaN, Infinity und offensichtlich String (auch wenn es eine Zahl darstellt).
Polyfill Beispiel:

Number.isSafeInteger || (Number.isSafeInteger = function(value){
  return typeof value === 'number' && 
                value === Math.floor(value) &&
                value  <   9007199254740992 &&
                value  >  -9007199254740992;
});

ECMAScript 2015 / ES6 bietet eine neue statische Methode Math.trunc()
So kürzen Sie eine Gleitkommazahl auf eine Ganzzahl:

Gibt den ganzzahligen Teil der Zahl x zurück und entfernt alle Nachkommastellen.Wenn x bereits eine ganze Zahl ist, ist das Ergebnis x.

Oder einfacher ausgedrückt (MDN):

Im Gegensatz zu den anderen drei mathematischen Methoden: Math.floor(), Math.ceil() Und Math.round(), der Weg Math.trunc() funktioniert ganz einfach und unkompliziert:
Schneiden Sie einfach den Punkt und die Ziffern dahinter ab, unabhängig davon, ob das Argument eine positive oder eine negative Zahl ist.

Wir können es weiter erklären (und polyfillen) Math.trunc() als solche:

Math.trunc || (Math.trunc = function(n){
    return n < 0 ? Math.ceil(n) : Math.floor(n); 
});

Beachten Sie, dass die Nutzlast des oben genannten Polyfills dies kann möglicherweise von der Engine besser voroptimiert werden als:
Math[n < 0 ? 'ceil' : 'floor'](n);

Verwendung: Math.trunc(/* Number or String */)
Eingang:(Ganzzahl oder Gleitkomma) Number (wird aber gerne versuchen, einen String in eine Zahl umzuwandeln)
Ausgabe:(Ganze Zahl) Number (Ich werde aber gerne versuchen, Number in einem String-Kontext in String umzuwandeln.)
Reichweite: -2^52 Zu +2^52 (Darüber hinaus sollten wir schlicht und einfach mit „Rundungsfehlern“ (und irgendwann mit wissenschaftlicher/exponentieller Notation) rechnen, weil unsere Number Eingabe in IEEE 754 hat bereits gebrochene Genauigkeit verloren:da Zahlen zwischen ±2^52 Zu ±2^53 sind bereits innen abgerundet ganze Zahlen (z.B 4503599627370509.5 wird intern bereits dargestellt als 4503599627370510) und darüber hinaus ±2^53 die ganzen Zahlen verlieren auch an Präzision (Potenzen von 2)).


Umwandlung von Float in Ganzzahl durch Subtrahieren Rest (%) einer Teilung durch 1:

Beispiel: result = n-n%1 (oder n-=n%1)
Dies sollte auch der Fall sein kürzen schwimmt.Da der Restoperator einen höheren Wert hat Vorrang als Subtraktion erhalten wir effektiv: (n)-(n%1).
Bei positiven Zahlen ist leicht zu erkennen, dass dies den Wert unterschreitet: (2.5) - (0.5) = 2,
für negative Zahlen ist dies die Obergrenze des Wertes: (-2.5) - (-0.5) = -2 (Weil --=+ Also (-2.5) + (0.5) = -2).

Seit der Eingang Und Ausgabe Sind Number Wir sollen bekommen das der gleiche Nutzbereich und die gleiche Leistung im Vergleich zu ES6 Math.trunc() (oder es ist Polyfill).
Notiz:hart ich Furcht (nicht sicher) Es könnte Unterschiede geben:Da wir Arithmetik (die intern den Rundungsmodus „nearTiesEven“ (auch bekannt als Banker-Rundung) verwendet) für die ursprüngliche Zahl (die Gleitkommazahl) und eine zweite abgeleitete Zahl (den Bruch) durchführen, scheint dies möglicherweise zu zusammengesetzten digitalen_Darstellungs- und arithmetischen Rundungsfehlern zu führen Immerhin einen Schwimmer zurückgeben.


Konvertierung von Float in Integer durch (ab-)using bitweise Operationen:

Das funktioniert per im Inneren Erzwingen eines (Gleitkomma) Number Konvertierung (Abschneiden und Überlauf) in einen vorzeichenbehafteten 32-Bit-Ganzzahlwert (Zweierkomplement) durch Verwendung einer bitweisen Operation auf einen Number (und das Ergebnis wird zurück in einen (Gleitkomma) konvertiert Number das nur den ganzzahligen Wert enthält).

Wieder, Eingang Und Ausgabe Ist Number (und wieder stille Konvertierung von String-Eingabe zu Zahl und Zahlen-Ausgabe zu String).

Wichtigeres, schwieriges (und normalerweise vergessenes und nicht erklärtes):
abhängig von der bitweisen Operation und dem Vorzeichen der Zahl, Die nützlich Reichweite wird sein begrenzt zwischen:
-2^31 Zu +2^31 (wie ~~num oder num|0 oder num>>0) ODER 0 Zu +2^32 (num>>>0).

Dies sollte durch die folgende Nachschlagetabelle (enthaltend) weiter verdeutlicht werden: alle „kritische“ Beispiele):

              n             | n>>0 OR n<<0 OR   |    n>>>0    | n < 0 ? -(-n>>>0) : n>>>0
                            | n|0 OR n^0 OR ~~n |             |
                            | OR n&0xffffffff   |             |
----------------------------+-------------------+-------------+---------------------------
+4294967298.5 = (+2^32)+2.5 |             +2    |          +2 |          +2
+4294967297.5 = (+2^32)+1.5 |             +1    |          +1 |          +1
+4294967296.5 = (+2^32)+0.5 |              0    |           0 |           0
+4294967296   = (+2^32)     |              0    |           0 |           0
+4294967295.5 = (+2^32)-0.5 |             -1    | +4294967295 | +4294967295
+4294967294.5 = (+2^32)-1.5 |             -2    | +4294967294 | +4294967294
       etc...               |         etc...    |      etc... |      etc...
+2147483649.5 = (+2^31)+1.5 |    -2147483647    | +2147483649 | +2147483649
+2147483648.5 = (+2^31)+0.5 |    -2147483648    | +2147483648 | +2147483648
+2147483648   = (+2^31)     |    -2147483648    | +2147483648 | +2147483648
+2147483647.5 = (+2^31)-0.5 |    +2147483647    | +2147483647 | +2147483647
+2147483646.5 = (+2^31)-1.5 |    +2147483646    | +2147483646 | +2147483646
       etc...               |         etc...    |      etc... |      etc...
         +1.5               |             +1    |          +1 |          +1
         +0.5               |              0    |           0 |           0
          0                 |              0    |           0 |           0
         -0.5               |              0    |           0 |           0
         -1.5               |             -1    | +4294967295 |          -1
       etc...               |         etc...    |      etc... |      etc...
-2147483646.5 = (-2^31)+1.5 |    -2147483646    | +2147483650 | -2147483646
-2147483647.5 = (-2^31)+0.5 |    -2147483647    | +2147483649 | -2147483647
-2147483648   = (-2^31)     |    -2147483648    | +2147483648 | -2147483648
-2147483648.5 = (-2^31)-0.5 |    -2147483648    | +2147483648 | -2147483648
-2147483649.5 = (-2^31)-1.5 |    +2147483647    | +2147483647 | -2147483649
-2147483650.5 = (-2^31)-2.5 |    +2147483646    | +2147483646 | -2147483650
       etc...               |         etc...    |      etc... |      etc...
-4294967294.5 = (-2^32)+1.5 |             +2    |          +2 | -4294967294
-4294967295.5 = (-2^32)+0.5 |             +1    |          +1 | -4294967295
-4294967296   = (-2^32)     |              0    |           0 |           0
-4294967296.5 = (-2^32)-0.5 |              0    |           0 |           0
-4294967297.5 = (-2^32)-1.5 |             -1    | +4294967295 |          -1
-4294967298.5 = (-2^32)-2.5 |             -2    | +4294967294 |          -2

Anmerkung 1:Die letzte Spalte hat einen erweiterten Bereich 0 Zu -4294967295 verwenden (n < 0 ? -(-n>>>0) : n>>>0).
Anmerkung 2:bitweise führt einen eigenen Konvertierungsaufwand ein(S) (Schweregrad vs Math hängt von der tatsächlichen Implementierung ab, also bitweise könnte schneller sein (häufig bei älteren historischen Browsern)).


Offensichtlich, wenn Ihre Gleitkommazahl a wäre String zunächst,
parseInt(/*String*/, /*Radix*/) wäre eine geeignete Wahl, um es in eine Ganzzahl zu analysieren Number.
parseInt() Wille kürzen auch (für positive und negative Zahlen).
Der Reichweite ist wiederum auf IEEE 754-Gleitkomma mit doppelter Genauigkeit beschränkt, wie oben erläutert Math Methode(n).

Schließlich, wenn Sie eine haben String und erwarte ein String Als Ausgabe können Sie auch den Basispunkt und den Bruch zerschneiden (was Ihnen auch einen größeren genauen Kürzungsbereich im Vergleich zum IEEE 754-Gleitkomma mit doppelter Genauigkeit bietet (±2^52))!


EXTRA:
Anhand der obigen Informationen sollten Sie nun alles haben, was Sie wissen müssen.

Wenn Sie zum Beispiel möchten von Null weg runden (auch bekannt als rund in Richtung Unendlichkeit) Sie könnten das ändern Math.trunc() Polyfill, z Beispiel:

Math.intToInf || (Math.intToInf = function(n){
    return n < 0 ? Math.floor(n) : Math.ceil(n); 
});
var i = parseInt(n, 10);

Wenn Sie nicht über einen Radix-Werte wie '010' angeben wird als Oktal behandelt werden (und so wird das Ergebnis 8 nicht 10).

Mit Bit-Operatoren. Es ist vielleicht nicht der klarste Weg in einer ganzen Zahl umzuwandeln, aber es funktioniert auf jede Art von Datentyp.

Nehmen wir an Ihre Funktion übernimmt ein Argument value und die Funktion arbeitet in einer Weise, dass value immer eine ganze Zahl sein muss (und 0 wird akzeptiert). Dann eine der folgenden Bedingungen wird zuweisen value als Integer:

value = ~~(value)
value = value | 0;
value = value & 0xFF;   // one byte; use this if you want to limit the integer to
                        // a predefined number of bits/bytes

Der beste Teil ist, dass diese mit Strings arbeiten (was man von einem Texteingabe bekommen könnte, usw.), die Zahlen ~~("123.45") === 123 sind. Alle nicht numerischen Werte ergeben 0, dh

~~(undefined) === 0
~~(NaN) === 0
~~("ABC") === 0

Es funktioniert mit hexadezimalen Zahlen als Strings (mit einem 0x prefix)

~~("0xAF") === 175

Es gibt eine Art Zwang beteiligt sind, nehme ich an. Ich werde einige Performance-Tests durchführen, diese parseInt() und Math.floor() zu vergleichen, aber Ich mag mit den zusätzlichen Komfort ohne Errors geworfen und eine 0 für Nicht-Zahlen bekommen

Also habe ich einen Maßstab, an Chrome gemacht, wenn die Eingabe bereits eine Zahl ist, würde die schnellste ~~num und num|0, halbe Geschwindigkeit: Math.floor, und die langsamste wäre parseInt siehe hier

Benchmark-Ergebnis

Bearbeiten : es scheint, gibt es bereits eine andere Person, die Rundung Benchmark (zusätzliches Ergebnis) und zusätzliche Wege : num>>0 (so schnell wie |0) und num - num%1 (manchmal schnell)

erscheint die Frage, und zwar von einem Schwimmer zu fragen, in einen int zu konvertieren. Mein Verständnis ist, dass die Art und Weise, dies zu tun ist toFixed zu verwenden. So ...

var myFloat = 2.5;
var myInt = myFloat.toFixed(0);

Wer weiß, wenn Math.floor() mehr oder weniger performant als Number.toFixed() ist?

könnten Sie auch es auf diese Weise tun:

var string = '1';
var integer = a * 1;

parseInt () ist wahrscheinlich die beste. a | 0 nicht tut, was Sie wirklich wollen (es weist nur 0, wenn ein nicht definierter oder Nullwert ist, was bedeutet, ein leeres Objekt oder Array besteht den Test) und Math.floor funktioniert durch eine Art List (es ruft im Grunde parseInt ( im Hintergrund)).

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top