Pergunta

Ouvi dizer que você poderia shift direita um número por 0,5 em vez de usar Math.floor (). Eu decidi verificar seus limites para se certificar de que era um substituto adequado, então eu verifiquei os seguintes valores e obteve os seguintes resultados no Google Chrome:


2.5 >> .5 == 2;
2.9999 >> .5 == 2;
2.999999999999999 >> .5 == 2;  // 15 9s
2.9999999999999999 >> .5 == 3;  // 16 9s

Após alguns mexer, descobri que o valor mais elevado possível de dois que, quando deslocado para a direita por 0,5-, iria produzir 2 é 2.9999999999999997779553950749686919152736663818359374999999¯ (com o 9 repetindo) em cromo e Firefox. O número é 2.9999999999999997779¯ no IE.

A minha pergunta é: qual é o significado do número .0000000000000007779553950749686919152736663818359374? É um número muito estranho e realmente despertou minha curiosidade.

Eu tenho tentado encontrar uma resposta ou pelo menos algum tipo de padrão, mas acho que minhas mentiras de problema no fato de que eu realmente não entendo a operação bit a bit. Eu entendo a idéia em princípio, mas mudando a seqüência de bits por .5 não faz qualquer sentido para mim. Qualquer ajuda é apreciada.

Para o registro, a seqüência de dígitos estranho muda com 2 ^ x. Os maiores valores possíveis dos seguintes números que ainda truncar corretamente:

for 0: 0.9999999999999999444888487687421729788184165954589843749¯
for 1: 1.9999999999999999888977697537484345957636833190917968749¯
for 2-3: x+.99999999999999977795539507496869191527366638183593749¯
for 4-7: x+.9999999999999995559107901499373838305473327636718749¯
for 8-15: x+.999999999999999111821580299874767661094665527343749¯
...and so forth
Foi útil?

Solução

Na verdade, você está simplesmente acabar fazendo um piso () no primeiro operando, sem quaisquer operações de ponto flutuante acontecendo. Desde o desvio à esquerda e as operações de mudança de bit a bit direito só fazem sentido com operandos inteiros, o motor JavaScript é converter os dois operandos inteiros primeiro:

2.999999 >> 0.5

Torna-se:

Math.floor(2.999999) >> Math.floor(0.5)

Que por sua vez é:

2 >> 0

Shifting por 0 significa que os bits "não faça uma mudança" e, portanto, você acaba com o primeiro operando, simplesmente truncado para um inteiro.

O código fonte SpiderMonkey tem:

switch (op) {
  case JSOP_LSH:
  case JSOP_RSH:
    if (!js_DoubleToECMAInt32(cx, d, &i)) // Same as Math.floor()
        return JS_FALSE;
    if (!js_DoubleToECMAInt32(cx, d2, &j)) // Same as Math.floor()
        return JS_FALSE;
    j &= 31;
    d = (op == JSOP_LSH) ? i << j : i >> j;
    break;

Seu vendo um "arredondamento" com determinados números é devido ao fato do motor JavaScript não pode lidar com dígitos decimais além de uma certa precisão e, portanto, seu número acaba sendo arredondado para o número inteiro seguinte. Tente isto em seu navegador:

alert(2.999999999999999);

Você obterá 2,999999999999999. Agora tente adicionar mais um 9:

alert(2.9999999999999999);

Você receberá um 3.

Outras dicas

Este é possivelmente o único pior idéia que eu já vi. Sua única finalidade possível de existir é para ganhar um concurso de código obfusticated. Não há nenhum significado para os números longos que você postou - eles são um artefato da implementação de ponto flutuante subjacente, filtrada através de deus sabe quantas camadas intermediárias. Bit de mudança por um número fracionário de bytes é insano e eu estou surpreso que ele não gerar uma exceção -. Mas isso é Javascript, sempre dispostos a redefinir "insano"

Se eu fosse você, eu iria evitar sempre usando este "recurso". Seu único valor é como uma possível causa raiz para uma condição de erro incomum. Use Math.floor() e ter pena de um a próxima programador que irá manter o código.


Confirmando algumas suspeitas que tive ao ler a pergunta:

  • Right-shifting qualquer número x fracionada por qualquer número y fracionária vai x simplesmente truncado, dando o mesmo resultado que Math.floor() enquanto confundindo completamente o leitor.
  • 2,999999999999999777955395074968691915 ... é simplesmente o maior número que pode ser diferenciada de "3". Tente avaliá-la por si só -. Se você acrescentar nada para ele, ele vai avaliar a 3. Este é um artefato do navegador e implementação de ponto flutuante do sistema local

Se você quiser ir mais fundo, leia "O que cada cientista computador deve saber sobre Floating-Point Arithmetic": http://docs.sun.com/source/806-3568/ncg_goldberg.html

Eu não acho que o seu deslocamento para a direita é relevante. Você está simplesmente além da resolução de um constante ponto de precisão dupla flutuante.

No Chrome:

var x = 2.999999999999999777955395074968691915273666381835937499999;
var y = 2.9999999999999997779553950749686919152736663818359375;

document.write("x=" + x);
document.write(" y=" + y);

Imprime: x = 2,9999999999999996 y = 3

Tente este javascript out: alert (parseFloat ( "2,9999999999999997779553950749686919152736663818359374999999"));

Em seguida, tente o seguinte: alert (parseFloat ( "2,9999999999999997779553950749686919152736663818359375"));

O que você está vendo é simples imprecisão de ponto flutuante. Para mais informações sobre isso, consulte este por exemplo: http://en.wikipedia.org/wiki / Floating_point # Accuracy_problems .

A questão básica é que o mais próximo que um valor de ponto flutuante pode chegar a representar o segundo número é maior ou igual a 3, enquanto que o fecha que a uma bóia pode chegar ao primeiro número é estritamente inferior a três.

Quanto ao porquê de deslocamento direita por 0,5 faz nada sã em tudo, parece que 0,5 é apenas em si ficando convertido para um int (0) antemão. Em seguida, o flutuador original (2.999 ...) está sendo convertido para um int por truncamento, como de costume.

O operador direito turno só funciona em números inteiros (ambos os lados). Então, mudando junto .5 pedaços deve ser exatamente equivalente ao deslocamento direita por 0 bits. E, do lado esquerdo é convertido para um inteiro antes da operação de deslocamento, que faz a mesma coisa como Math.floor ().

Eu suspeito que a conversão 2,9999999999999997779553950749686919152736663818359374999999 a ele de representação binária seria esclarecedor. É provavelmente apenas 1 bit diferente da verdadeira 3.

bom palpite, mas nenhum charuto. Como o número de precisão dupla FP tem 53 pedaços, o último número FP antes de 3 é na verdade (Exata): 2,999999999999999555910790149937383830547332763671875

Mas por que é 2,9999999999999997779553950749686919152736663818359375

(e este é exata, não 49999 ...!)

que é maior do que a última unidade displayable? Arredondamento. A rotina de conversão (String para o número) simplesmente está correctamente programado para completar a entrada do próximo número de ponto flutuante.

2,999999999999999555910790149937383830547332763671875

....... (valores entre, aumentando) -> rodada para baixo

2,9999999999999997779553950749686919152736663818359375

....... (valores entre, aumentando) ->-se rodada a 3

3

A entrada de conversão deve usar precisão total. Se o número é exatamente a metade entre estes dois números fp (que é 2,9999999999999997779553950749686919152736663818359375) o arredondamento depende das bandeiras setado. O arredondamento padrão é rodada até mesmo, o que significa que o número será arredondado para o próximo número par.

Agora

3 = 11. (binário)

2.999 ... = 10,11111111111 ...... (binário)

Todos os bits são definidos, o número é sempre estranho. Isso significa que o número exato metade será arredondado para cima, então você está recebendo o estranho ..... 49999 período porque ele deve ser menor do que a metade exata para ser distinguível de 3.

Eu suspeito que a conversão 2,9999999999999997779553950749686919152736663818359374999999 a sua representação binária seria esclarecedor. É provavelmente apenas 1 pouco diferente da verdadeira 3.

E para adicionar a resposta de John, as chances de isso ser mais eficaz do que Math.floor são muito pequenas.

Eu não sei se JavaScript usa números de ponto flutuante ou algum tipo de biblioteca infinita de precisão, mas de qualquer forma, você está indo para obter erros de arredondamento em uma operação como esta - mesmo que seja muito bem definido.

Note-se que o número" .0000000000000007779553950749686919152736663818359374" é possivelmente a Epsilon , definido como "o menor número E tal que (1 + E)> 1."

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