¿Cuál es el mejor método para convertir el punto flotante en un integer en JavaScript?

Existen varios methods diferentes para convertir numbers de coma flotante a integers en JavaScript. Mi pregunta es ¿qué método ofrece el mejor performance, es más compatible o se considera la mejor práctica?

Aquí hay algunos methods que conozco:

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

Estoy seguro de que hay otros por ahí. Sugerencias?

De acuerdo con este website :

parseInt se utiliza ocasionalmente como un medio para convertir un número de coma flotante en un número integer. Es muy inadecuado para esa tarea porque si su argumento es de tipo numérico, primero se convertirá en una cadena y luego se analizará como un número …

Para networkingondear numbers a numbers integers, uno de Math.round, Math.ceil y Math.floor son preferibles …

De "Javascript: The Good Parts" de Douglas Crockford:

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

Al hacerlo, estás agregando un método a cada object Number.

Entonces puedes usarlo así:

 var x = 1.2, y = -1.2; x.integer(); // 1 y.integer(); // -1 (-10 / 3).integer(); // -3 

Al parecer, el doble bit a bit no es la forma más rápida de poner un número en el piso:

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

Solía ​​ser un artículo aquí, obteniendo un 404 ahora: http://james.padolsey.com/javascript/double-bitwise-not/

Google lo tiene en caching: 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

¡Pero la máquina de Wayback salva el día! http://web.archive.org/web/20100422040551/http://james.padolsey.com/javascript/double-bitwise-not/

La respuesta ya ha sido dada, pero solo para ser claro.

Usa la biblioteca de Matemáticas para esto. funciones de ronda, ceil o piso.

parseInt es para convertir una cadena a un int que no es lo que se necesita aquí

toFixed es para convertir un flotador en una cadena que tampoco es lo que se necesita aquí

Dado que las funciones de Matemáticas no harán ninguna conversión ao desde una cadena, será más rápido que cualquiera de las otras opciones que de todos modos son incorrectas.

Puede usar el Número (a) .aFija (0);

O incluso simplemente a.toFixed (0);

Editar:

Eso está networkingondeando a 0 lugares, ligeramente diferente que truncar, y como alguien más sugirió, toFixed devuelve una cadena, no un integer sin procesar. Útil para propósitos de exhibición.

 var num = 2.7; // typeof num is "Number" num.toFixed(0) == "3" 
 var i = parseInt(n, 10); 

Si no especifica una raíz, los valores como '010' se tratarán como octales (y el resultado será 8 no 10 ).

Usando operadores bit a bit. Puede que no sea la forma más clara de convertir a un número integer, pero funciona en cualquier tipo de tipo de datos.

Supongamos que su function toma un value argumento, y la function funciona de tal manera que el value siempre debe ser un número integer (y se acepta 0). A continuación, cualquiera de los siguientes asignará value como un integer:

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

La mejor parte es que esto funciona con cadenas (lo que puede get de una input de text, etc.) que son numbers ~~("123.45") === 123 . Cualquier valor no numérico da como resultado 0 , es decir,

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

Funciona con numbers hexadecimales como cadenas (con un prefijo 0x )

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

Supongo que hay algún tipo de coerción involucrado. Haré algunas testings de performance para compararlas con parseInt() y Math.floor() , pero me gusta tener la conveniencia adicional de no lanzar Errors y get un 0 para no-numbers.

Así que hice un punto de reference, en Chrome cuando la input ya es un número, el más rápido sería ~~num y num|0 , la mitad de la velocidad: Math.floor , y el más lento sería parseInt ver aquí

resultado de referencia

EDITAR : parece que ya hay otra persona que hizo el punto de reference de networkingondeo (más resultado) y forms adicionales: num>>0 (tan rápido como |0 ) y num - num%1 (a veces rápido)

La 'mejor' manera depende de:

  • modo de networkingondeo: qué tipo de networkingondeo (del flotador a integer) espera / requiere
    para numbers positivos y / o negativos que tienen una parte fraccionaria.
    Ejemplos comunes:
      flotar |  trunc |  piso |  ceil |  cerca (medio arriba)
     ------ + ------- + ------- + ------- + ---------------
     + ∞ |  + ∞ |  + ∞ |  + ∞ |  + ∞  
     +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 |  Yaya
     -0 |  -0 |  -0 |  -0 |  -0
     -2.25 |  -2 |  -3 |  -2 |  -2
     -2.5 |  -2 |  -3 |  -2 |  -2
     -2.75 |  -2 |  -3 |  -2 |  -3
     -∞ |  -∞ |  -∞ |  -∞ |  -∞  
    

    Para las conversiones de flotante a integer, comúnmente esperamos un "truncamiento"
    (también conocido como "networkingondear hacia cero" o "networkingondear lejos del infinito" ).
    Efectivamente, esto simplemente 'corta' la parte fraccionaria de un número de punto flotante.
    La mayoría de las técnicas y los methods internos (internos) se comportan de esta manera.

  • input: cómo se representa su número (punto flotante):
    • String
      Comúnmente base / base: 10 (decimal)
    • punto flotante ('interno') Number
  • salida: lo que quiere hacer con el valor resultante:
    • String salida (intermedia) (base pnetworkingeterminada 10) (en la pantalla)
    • realizar más cálculos sobre el valor resultante
  • distancia:
    en qué range numérico esperas input / cálculo-resultados
    y para qué range espera salida correspondiente 'correcta'.

¡Solo después de que se hayan respondido estas consideraciones, podemos pensar en los methods apropiados y la velocidad!


Según la especificación ECMAScript 262: todos los numbers (tipo de Number ) en javascript se representan / almacenan en:
Formato " Doble punto flotante de precisión IEEE 754 (binary64) ".
Por lo tanto, los integers también se representan en el mismo formatting de coma flotante (como numbers sin una fracción).
Nota: ¡la mayoría de las implementaciones utilizan internamente types de integers más eficientes (para velocidad y tamaño de memory) cuando es posible!

Como este formatting almacena 1 bit de signo, 11 bits de exponente y los primeros 53 bits significativos ("mantisa"), podemos decir que: solo los valores Number entre -2 52 y +2 52 pueden tener una fracción.
En otras palabras: todos los valores Number positivos y negativos representables entre 2 52 y (casi) 2 (2 11 /2=1024) (en cuyo punto el formatting lo llama un día Infinity ) ya son integers (networkingondeados internamente, ya que no quedan bits para representar los dígitos integers fraccionarios y / o less significativos restantes).

Y está el primer 'gotcha':
No puede controlar el modo de networkingondeo interno de Number -resultados para las conversiones incorporadas de Literal / Cadena a flotación (modo de networkingondeo: IEEE 754-2008 "networkingondear al más cercano, vincula a par") y operaciones aritméticas incorporadas ( rounding-mode: IEEE 754-2008 "networkingondo a más cercano").
Por ejemplo:
2 52 +0.25 = 4503599627370496.25 se networkingondea y se almacena como: 4503599627370496
2 52 +0.50 = 4503599627370496.50 se networkingondea y almacena como: 4503599627370496
2 52 +0.75 = 4503599627370496.75 se networkingondea y se almacena como: 4503599627370497
2 52 +1.25 = 4503599627370497.25 se networkingondea y se almacena como: 4503599627370497
2 52 +1.50 = 4503599627370497.50 se networkingondea y se almacena como: 4503599627370498
2 52 +1.75 = 4503599627370497.75 se networkingondea y se almacena como: 4503599627370498
2 52 +2.50 = 4503599627370498.50 se networkingondea y almacena como: 4503599627370498
2 52 +3.50 = 4503599627370499.50 se networkingondea y almacena como: 4503599627370500

Para controlar el networkingondeo, su Number necesita una parte fraccionaria (y al less un bit para representar eso); de lo contrario, ceil / floor / trunc / near devuelve el integer que introdujo en él.

Para ceil / floor / trunc correctamente un Número hasta x dígito (s) decimal (es) decimal (es) significativo (s), solo nos importa si el valor decimal inferior más alto y más alto nos dará un valor fraccionario binary después del networkingondeo (para que no quede bloqueado o el siguiente integer).
Entonces, por ejemplo, si espera un networkingondeo "correcto" (para ceil / piso / trunc) de hasta 1 dígito decimal fraccionario significativo ( x.1 to x.9 ), necesitamos al less 3 bits (no 4) para darnos un valor fraccionario binary:
0.1 está más cerca de 1/(2 3 =8)=0.125 que de 0 y 0.9 está más cerca de 1-1/(2 3 =8)=0.875 que de 1 .

solo hasta ±2 (53-3=50) todos los valores representables tienen una fracción binaria distinta de cero para no más que el primer dígito decimal decimal significativo (valores x.1 a x.9 ).
Para 2 decimales ±2 (53-6=47) , para 3 decimales ±2 (53-9=44) , para 4 decimales ±2 (53-13=40) , para 5 decimales ±2 (53-16=37) , para 6 decimales ±2 (53-19=34) , para 7 decimales ±2 (53-23=30) , para 8 decimales ±2 (53-26=27) , para 9 decimales ±2 (53-29=24) , por 10 decimales ±2 (53-33=20) , por 11 decimales ±2 (53-36=17) , etc.

Un "Entero seguro" en javascript es un número integer:

  • que se puede representar exactamente como un número de doble precisión IEEE-754, y
  • cuya representación IEEE-754 no puede ser el resultado de networkingondear cualquier otro integer para ajustarse a la representación IEEE-754
    (aunque ±2 53 (como potencia exacta de 2) puede representarse exactamente, no es un integer seguro porque también podría haber sido ±(2 53 +1) antes de networkingondearse para ajustarse al máximo de 53 más bits significativos).

Esto define efectivamente un range de subsets de integers (representables de forma segura) entre -2 53 y +2 53 :

  • desde: -(2 53 - 1) = -9007199254740991 (inclusive)
    (una constante proporcionada como propiedad estática Number.MIN_SAFE_INTEGER desde ES6)
  • a: +(2 53 - 1) = +9007199254740991 (inclusive)
    (una constante proporcionada como propiedad estática Number.MAX_SAFE_INTEGER desde ES6)
    Polyfill trivial para estas 2 nuevas constantes ES6:

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

Desde ES6 también hay un método estático complementario Number.isSafeInteger() que testing si el valor pasado es de tipo Number y es un integer dentro del range integer seguro (devolviendo un boolean true o false ).
Nota: también devolverá false para: NaN , Infinity y obviamente String (incluso si representa un número).
Ejemplo de Polyfill:

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

ECMAScript 2015 / ES6 proporciona un nuevo método estático Math.trunc()
para truncar un flotante en un integer:

Devuelve la parte integral del número x, eliminando cualquier dígito fraccionario. Si x ya es un número integer, el resultado es x.

O ponlo más simple ( MDN ):

A diferencia de otros tres methods matemáticos: Math.floor() , Math.ceil() y Math.round() , la forma en que Math.trunc() funciona es muy simple y directa:
simplemente trunque el punto y los dígitos detrás de él, sin importar si el argumento es un número positivo o negativo.

Podemos explicar (y Math.trunc() ) Math.trunc() como tal:

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

Tenga en count que la carga útil del polyfill anterior puede ser mejor pre-optimizada por el motor en comparación con:
Math[n < 0 ? 'ceil' : 'floor'](n);

Uso : Math.trunc(/* Number or String */)
Entrada : (número integer o punto flotante) Number (pero intentaremos convertir una cadena a un número felizmente)
Salida : Number (integer) (pero intentará felizmente convertir Número a cadena en un context de cadena)
Rango : -2^52 a +2^52 (más allá de esto deberíamos esperar 'errores de networkingondeo' (y en algún punto notación científica / exponencial) simple y simplemente porque nuestra input de Number en IEEE 754 ya perdió precisión fraccional: desde Números entre ±2^52 a ±2^53 ya son integers networkingondeados internamente (por ejemplo, 4503599627370509.5 ya está internamente representado como 4503599627370510 ) y más allá de ±2^53 los integers también pierden precisión (potencias de 2)).


Conversión de flotante a integer restando el Resto ( % ) de una devision por 1 :

Ejemplo: result = nn%1 (o n-=n%1 )
Esto también debería truncar las carrozas. Como el operador Resto tiene una precedencia más alta que la Resta, obtenemos efectivamente: (n)-(n%1) .
Para Números positivos es fácil ver que esto nivela el valor: (2.5) - (0.5) = 2 ,
para Números negativos, esto cela el valor: (-2.5) - (-0.5) = -2 (porque --=+ tan (-2.5) + (0.5) = -2 ).

Dado que la input y la salida son Number , deberíamos get el mismo range útil y la misma producción en comparación con ES6 Math.trunc() (o su Math.trunc() ).
Nota: difícil, me temo (no estoy seguro), puede haber diferencias: porque estamos haciendo aritmética (que internamente usa el modo de networkingondeo "nearTiesEven" (también conocido como Banker's Rounding)) en el Número original (el flotante) y un segundo Número derivado (la fracción ) esto parece invitar a la composition digital de la representación y los errores de networkingondeo aritmético, por lo tanto, potencialmente devolver un flotador después de todo ..


Conversión de flotante a integer mediante (ab-) usando operaciones bit a bit :

Esto funciona forzando internamente una conversión de Number (punto flotante) (truncamiento y desbordamiento) a un valor integer de 32 bits con signo (complemento a dos) mediante el uso de una operación bit a bit en un Number (y el resultado se convierte a a (punto flotante) Number que contiene solo el valor integer).

Nuevamente, la input y la salida son Number (y nuevamente conversión silenciosa de Entrada de cadena a Número y Salida de número a Cadena).

Más importante resistente (y generalmente olvidado y no explicado):
dependiendo del funcionamiento a nivel de bit y el signo del número , el range útil estará limitado entre:
-2^31 a +2^31 (como ~~num o num|0 o num>>0 ) O 0 a +2^32 ( num>>>0 ).

Esto debería aclararse con la siguiente tabla de búsqueda (que contiene todos los ejemplos "críticos"):

               n |  n >> 0 OR n << 0 OR |  n >>> 0 |  n <0?  - (- n >>> 0): n >>> 0
                             |  n | 0 OR n ^ 0 OR ~~ n |  |
                             |  O 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

Nota 1: la última columna tiene un range extendido de 0 a -4294967295 usando (n < 0 ? -(-n>>>0) : n>>>0) .
Nota 2: bitwise introduce su propia conversión: sobrecarga ( s ) (la gravedad frente a Math depende de la implementación real, por lo que el bitwise podría ser más rápido (a menudo en browseres históricos más antiguos)).


Obviamente, si su número de 'coma flotante' era una String para empezar,
parseInt(/*String*/, /*Radix*/) sería una opción apropiada para analizarlo en un número integer.
parseInt() se truncará también (para numbers positivos y negativos).
El range está nuevamente limitado al punto flotante de doble precisión IEEE 754 como se explicó anteriormente para el (los) método (s) Math (s).

Finalmente, si tiene un String y espera un String como resultado, también puede cortar el punto y la fracción del radix (que también le proporciona un range de truncamiento más grande en comparación con el punto flotante de doble precisión IEEE 754 ( ±2^52 )).


EXTRA:
De la información anterior, ahora debería tener todo lo que necesita saber.

Si, por ejemplo, desea networkingondear desde cero (también conocido como networkingondeado hacia el infinito ) puede modificar el polyfill Math.trunc() , por ejemplo :

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

La pregunta parece estar preguntando específicamente sobre la conversión de un flotante a un int. toFixed entendido, la forma de hacerlo es utilizar para toFixed . Asi que…

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

¿Alguien sabe si Math.floor() tiene más o less performance que Number.toFixed() ?

también podrías hacerlo de esta manera:

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

parseInt () es probablemente el mejor. a | 0 a | 0 no hace lo que realmente quiere (simplemente asigna 0 si a es un valor indefinido o nulo, lo que significa que un object vacío o una matriz pasa la testing), y Math.floor funciona mediante algún truco tipo (básicamente llama a parseInt ( ) en el background).