Qual é o melhor método para converter ponto flutuante para um inteiro em JavaScript?

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

  •  02-07-2019
  •  | 
  •  

Pergunta

Existem vários métodos diferentes para a conversão de números de ponto flutuante para inteiros em JavaScript. A minha pergunta é o que o método dá o melhor desempenho, é mais compatível, ou é considerada a melhor prática?

Aqui estão alguns métodos que eu conheço:

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

Eu tenho certeza que existem outras pessoas lá fora. Sugestões?

Foi útil?

Solução

De acordo com a este site :

parselnt ocasionalmente é usada como um meio de transformar um número de ponto flutuante em um número inteiro. É muito doente adequado para essa tarefa porque se seu argumento é do tipo numérico que vai primeiro ser convertidos em uma seqüência e, em seguida, analisado como um número ...

Para arredondar números para inteiros uma das Math.round, Math.ceil e Math.floor são preferíveis ...

Outras dicas

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

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

Fazendo isso você está adicionando um método para cada objeto Number.

Em seguida, você pode usá-lo assim:

var x = 1.2, y = -1.2;

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

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

Aparentemente double bit a bit-não é o caminho mais rápido para andar um número:

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

Usado para ser um artigo aqui, ficando a 404 agora, porém: http: // james.padolsey.com/javascript/double-bitwise-not/

Google tem em cache: 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

Mas o Wayback Machine salva o dia! http: // web. archive.org/web/20100422040551/http://james.padolsey.com/javascript/double-bitwise-not/

A resposta já foi dada, mas só para ficar claro.

Use a biblioteca de matemática para isso. redondo, ceil ou de chão funções.

parseInt é para converter uma string para um int que não é o que é necessário aqui

toFixed é para converter um float para uma cadeia também não o que é necessário aqui

Uma vez que as funções matemáticas não vai fazer qualquer conversão para ou de uma corda que vai ser mais rápido do que qualquer uma das outras opções que são de qualquer maneira errada.

Você pode usar Number (a) .toFixed (0);

Ou mesmo apenas a.toFixed (0);

Editar:

Isso é o arredondamento para 0 lugares, um pouco diferentes do que truncando e, como alguém sugeriu, toFixed retorna uma string, não um inteiro cru. Útil para fins de exibição.

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

A forma 'melhor' depende em:

  • arredondamento modo: o que tipo de arredondamento (do flutuador para inteiro) que você espera / exigem
    para números positivos e / ou negativos que têm uma parte fracionária.
    Os exemplos mais comuns:
    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
    -∞    |   -∞  |   -∞  |   -∞  |   -∞  
    
    Para float para conversões de inteiros nós comumente esperar "truncagem"
    (Aka "round para zero" aka "round longe do infinito" ).
    Eficazmente esta apenas 'corta' a parte fracionária de um número de ponto flutuante.
    A maioria das técnicas e (internamente) built-in métodos se comportam desta forma.
  • Entrada: como o seu número (ponto flutuante) é representado:
    • String
      Comumente raiz / base: 10 (decimal)
    • ponto flutuante ( 'interna') Number
  • saída: o que você quer fazer com o valor resultante:
    • (intermediário) String saída (default radix 10) (na tela)
    • realizar cálculos adicionais no valor resultante
  • intervalo:
    em que faixa numérica que você espera de entrada / cálculo-resultados
    e para que vão fazer você espera correspondente saída 'correta'.

Apenas após estas considerações são respondidas podemos pensar sobre o método apropriado (s) e velocidade!


Por ECMAScript 262 spec: todas números (tipo Number) em javascript são representados / armazenados em:
" IEEE 754 de precisão dupla de ponto flutuante (binary64) " formato.
Então inteiros são também representado no mesma formato de ponto flutuante (como números sem uma fração).
Nota: a maioria das implementações fazer uso mais eficiente (para a velocidade e memória-size) inteiro de tipos internamente quando possível!

Como este formato armazena 1 bit de sinal, 11 bits de expoente e os primeiros 53 bits significativos ( "mantissa"), podemos dizer que: somente Number valores entre -252 e +252 pode ter uma fração.
Em outras palavras: todas positivos representável e negativos Number valores entre 252 para (quase) 2(211/2=1024) (altura em que o formato de chama-lo um dia Infinity) já são inteiros (arredondado internamente, já que não existem pedaços deixados para representar os restantes e ou dígitos inteiros fracionários / menos significativos).

E não é a primeira 'pegadinha':
Você não pode controlar o arredondamento de modo interno de Number-resultados para o built-in Literal / String flutuar conversões (modo de arredondamento: IEEE 754-2008 "round a mais próxima, laços com mesmo") e built-in operações aritméticas ( -arredondamento modo:. IEEE 754-2008 "round-a-mais próximo")
Por exemplo:
252+0.25 = 4503599627370496.25 é arredondado e armazenado como: 4503599627370496
252+0.50 = 4503599627370496.50 é arredondado e armazenado como: 4503599627370496
252+0.75 = 4503599627370496.75 é arredondado e armazenado como: 4503599627370497
252+1.25 = 4503599627370497.25 é arredondado e armazenado como: 4503599627370497
252+1.50 = 4503599627370497.50 é arredondado e armazenado como: 4503599627370498
252+1.75 = 4503599627370497.75 é arredondado e armazenado como: 4503599627370498
252+2.50 = 4503599627370498.50 é arredondado e armazenado como: 4503599627370498
252+3.50 = 4503599627370499.50 é arredondado e armazenado como: 4503599627370500

Para controlar arredondamento seu Number precisa de uma parte fracionária (e pelo menos um pouco para representar isso), caso contrário, ceil / chão / trunc / perto retorna o inteiro você alimentou a ele.

Para corretamente ceil / chão / trunc um número até x significativa fracionada decimal dígitos (s), só se importam se o menor e maior decimal valor fracionário correspondente ainda vai nos dar umavalor fracionário binário após o arredondamento (para não ser forradas ou arredondada para o número inteiro seguinte).
Assim, por exemplo, se você espera 'correta' arredondamento (para ceil / chão / trunc) até 1 significativa dígito decimal fracionário (x.1 to x.9), precisamos de , pelo menos 3 bits (não 4) para dar nós a valor fracionário binário:
0.1 está mais perto de 1/(23=8)=0.125 do que está a 0 e 0.9 está mais perto de 1-1/(23=8)=0.875 do que está a 1.

única até ±2(53-3=50) vai todos os valores representáveis ??tem uma fração binária diferente de zero para não mais do que o início decimal significativa dígito fracionário (valores x.1 para x.9). < br> Para 2 casas decimais ±2(53-6=47), durante 3 decimais ±2(53-9=44), durante 4 decimais ±2(53-13=40), durante 5 casas decimais ±2(53-16=37), durante 6 decimais ±2(53-19=34), durante 7 decimais ±2(53-23=30), durante 8 decimais ±2(53-26=27), para 9 decimais ±2(53-29=24), para 10 casas decimais ±2(53-33=20), para 11 casas decimais ±2(53-36=17), etc ..

A "Safe Integer" em JavaScript é um inteiro:

  • que pode ser exatamente representado como um número de precisão dupla IEEE-754, e
  • cujo IEEE-754 representação não pode ser o resultado de arredondamento qualquer outro inteiro para ajustar a IEEE-754 representação
    (embora ±253 (como uma potência exata de 2) pode exatamente ser representado, é não um inteiro seguro porque ele também poderia ter sido ±(253+1) antes que fosse arredondado para caber no máximo de 53 bits mais significativos).

Este define efectivamente uma gama subconjunto de números inteiros (com segurança representáveis) entre -253 e +253:

  • De: -(253 - 1) = -9007199254740991 (inclusive)
    (Uma constante fornecido como propriedade estática Number.MIN_SAFE_INTEGER desde ES6 )
  • para: +(253 - 1) = +9007199254740991 (inclusive)
    (Uma constante fornecido como propriedade estática Number.MAX_SAFE_INTEGER desde ES6 )
    Trivial polyfill para estes 2 novos constantes ES6:

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


Desde ES6 há também um método estático cortesia Number.isSafeInteger() que testa se o valor passado é do tipo Number e é um número inteiro dentro do intervalo inteiro segura (retornando um true boolean ou false).
Nota: também retornará false para:. NaN, Infinity e obviamente String (mesmo que representa um número)
Polyfill exemplo :

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

ECMAScript 2015 / ES6 fornece uma nova estática método Math.trunc()
para truncar um float para um inteiro:

Retorna a parte integrante do número x, removendo quaisquer dígitos fracionários. Se x é já um número inteiro, o resultado é x.

Ou colocar mais simples ( MDN ):

Ao contrário de outros três métodos de matemática: Math.floor(), Math.ceil() e Math.round(), a maneira Math.trunc() funciona é muito simples e direta:
apenas truncar o ponto e os dígitos por trás dele, não importa se o argumento é um número positivo ou um número negativo.

cum ainda explicar (e polyfill) Math.trunc() como tal:

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

Note, a carga útil do polyfill acima podem potencialmente ser melhor pré-otimizado pelo motor em comparação com:
Math[n < 0 ? 'ceil' : 'floor'](n);

Uso : Math.trunc(/* Number or String */)
Input : (Integer ou Floating Point) Number (mas terá todo o prazer tentar converter uma string para um número)
saída : (Integer) Number (mas terá todo o prazer tentar converter Número de Cordas em uma string de contexto)
Gama : -2^52 para +2^52 (além deste devemos esperar 'arredondamento erros' (e em algum momento científica / exponencial notação) pura e simplesmente porque a nossa entrada Number em IEEE 754 já perdeu precisão fracionária: desde Números entre ±2^52 para ±2^53 já são internamente arredondada inteiros (por exemplo 4503599627370509.5 é internamente já representados como 4503599627370510) e além ±2^53 os inteiros também precisão solta (potências de 2)).


nofollow Float à conversão inteiro subtraindo o Restante (%) de um devision por 1:

Exemplo: result = n-n%1 (ou n-=n%1)
Isso também deve truncar flutua. Uma vez que o operador de resto tem um maior precedência de subtração nós efetivamente se:. (n)-(n%1)
Para números positivos, é fácil ver que este piso o valor: (2.5) - (0.5) = 2,
Para números negativos este ceils o valor:. (-2.5) - (-0.5) = -2 (porque --=+ tão (-2.5) + (0.5) = -2)

Uma vez que o de entrada e saída são Number nós deve se mesmo intervalo útil e de saída em comparação com ES6 Math.trunc() (ou de polyfill).
Nota: difícil I temer (não tenho certeza) que pode haver diferenças: porque estamos fazendo aritmética (que usa internamente modo de arredondamento "nearTiesEven" (aka arredondamento do banqueiro)) sobre o número original (o float) e uma segunda derivada Number (fração), este parece convidar compondo erros digital_representation e arredondamento aritmética, portanto, potencialmente retornar um float afinal ..


flutuador para conversão por inteiro (ab-) usando operações bit a bit :

Isso funciona, internamente forçando um (ponto flutuante) conversão Number (truncamento e overflow) para um valor inteiro de 32 bits assinado (complemento de dois) usando uma operação bit a bit em um Number (eo resultado está de volta convertido a um Number (ponto flutuante), que detém apenas o valor inteiro).

Novamente, entrada e saída é Number (e conversão novamente silenciosa de corda-de entrada para o número e Número-saída para String).

O mais importante difícil (e geralmente esquecido e não explicado):
dependendo operação bit a bit e o sinal do número , o útil gama serão limitado entre:
-2^31 para +2^31 (como ~~num ou num|0 ou num>>0) ou 0 para +2^32 (num>>>0).

Isto deve ser clarificado pela pesquisa-tabela a seguir (contendo todas 'críticoexemplos al'):

              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

Nota 1:. A última coluna tem 0 faixa estendida para -4294967295 usando (n < 0 ? -(-n>>>0) : n>>>0)
Nota 2: bit a bit apresenta sua própria conversão overhead ( s ) (gravidade vs Math depende da implementação real, de modo bit a bit poderia ser mais rápido (muitas vezes em antigos navegadores históricos)).


Obviamente, se o seu número de 'ponto flutuante' foi um String para começar,
parseInt(/*String*/, /*Radix*/) seria uma escolha adequada para analisá-lo em um Number inteiro.
parseInt() irá truncar , bem como (para números positivos e negativos).
O gama é novamente limitada a IEEE ponto flutuante de precisão dupla 754 como explicado acima para o método (s) Math.

Finalmente, se você tem um String e esperar uma String como saída você poderia também cortar o ponto de raiz e fração (que também lhe dá uma gama de truncamento preciso maior em comparação com IEEE 754 de precisão dupla de ponto flutuante (±2^52))


EXTRA:
A partir da informação acima você deve agora ter tudo o que você precisa saber.

Se por exemplo você gostaria rodada longe de zero (aka rodada para o infinito ) você pode modificar o polyfill Math.trunc(), para exemplo :

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

Se você não especificar um valor Radix como '010' será tratado como octal (e assim o resultado será 8 não 10).

Usando operadores bit a bit. Pode não ser a maneira mais clara de converter para um inteiro, mas funciona em qualquer tipo de tipo de dados.

Suponha que a sua função tem um value argumento, e a função funciona de tal maneira a que value deve ser sempre um inteiro (e 0 é aceito). Então qualquer um dos seguintes irá atribuir value como um inteiro:

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

A melhor parte é que isso funciona com strings (o que você pode começar a partir de uma entrada de texto, etc.) que são números ~~("123.45") === 123. Quaisquer valores não numéricos resultar em 0, ou seja,

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

Ele faz o trabalho com números hexadecimais como strings (com um prefixo 0x)

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

Há algum tipo de coerção envolvido, eu suponho. Vou fazer alguns testes de desempenho para comparar estes para parseInt() e Math.floor(), mas eu gosto de ter a comodidade de não Errors sendo jogado e conseguir um 0 para os não-números

Então eu fiz um ponto de referência, em Chrome quando a entrada já é um número, o mais rápido seria ~~num e num|0, metade da velocidade: Math.floor, eo mais lento seria parseInt ver aqui

resultado do benchmark

Editar : parece que há já uma outra pessoa que fez arredondamento de referência (mais resultado) e adicionais maneiras : num>>0 (tão rápido quanto |0) e num - num%1 (às vezes rápido)

A questão parece estar perguntando especificamente sobre a conversão de um float para um int. O meu entendimento é que a maneira de fazer isso é usar toFixed. Então ...

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

Alguém sabe se Math.floor() é mais ou menos eficaz do que Number.toFixed()?

Você também pode fazê-lo desta maneira:

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

parseInt () é provavelmente o melhor. a | 0 não faz o que você realmente quer (ele só atribui 0 se a é um valor indefinido ou nulo, o que significa um objeto vazio ou matriz passa no teste), e Math.floor funciona por algum tipo truques (que basicamente chama parseInt ( ) no fundo).

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top