Pregunta

Escuché que podrías cambiar un número a la derecha en .5 en lugar de usar Math.floor (). Decidí verificar sus límites para asegurarme de que fuera un reemplazo adecuado, así que verifiqué los siguientes valores y obtuve los siguientes resultados en Google Chrome:


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

Después de algunos problemas, descubrí que el valor más alto posible de dos que, cuando se cambia a la derecha por .5, arrojaría 2 es 2.9999999999999997779553950749686919152736663818359374999999 (con la repetición de 9) en Chrome y Firefox. El número es 2.999999999999999997779 en IE.

Mi pregunta es: ¿cuál es el significado del número .0000000000000007779553950749686919152736663818359374? Es un número muy extraño y realmente despertó mi curiosidad.

He estado tratando de encontrar una respuesta o al menos algún tipo de patrón, pero creo que mi problema radica en el hecho de que realmente no entiendo la operación bit a bit. Entiendo la idea en principio, pero cambiar una secuencia de bits por .5 no tiene ningún sentido para mí. Cualquier ayuda es apreciada.

Para el registro, la secuencia de dígitos extraños cambia con 2 ^ x. Los valores más altos posibles de los siguientes números que aún se truncan correctamente:

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
¿Fue útil?

Solución

En realidad, simplemente terminas haciendo un floor () en el primer operando, sin que se realicen operaciones de punto flotante. Dado que las operaciones de desplazamiento a la izquierda y desplazamiento a la derecha solo tienen sentido con operandos enteros, el motor de JavaScript convierte primero los dos operandos en enteros:

2.999999 >> 0.5

Se convierte en:

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

que a su vez es:

2 >> 0

Cambiar por 0 bits significa "no hacer un cambio" y, por lo tanto, terminas con el primer operando, simplemente truncado a un entero.

El código fuente de SpiderMonkey tiene:

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;

Estás viendo un "redondeo" con ciertos números se debe al hecho de que el motor de JavaScript no puede manejar dígitos decimales más allá de una cierta precisión y, por lo tanto, su número termina siendo redondeado al siguiente entero. Pruebe esto en su navegador:

alert(2.999999999999999);

Obtendrá 2.999999999999999. Ahora intente agregar uno más 9:

alert(2.9999999999999999);

Obtendrás un 3.

Otros consejos

Esta es posiblemente la peor idea que he visto. Su único propósito posible para existir es ganar un concurso de código ofuscado. No hay importancia para los números largos que publicaste: son un artefacto de la implementación de punto flotante subyacente, filtrada a través de Dios sabe cuántas capas intermedias. El desplazamiento de bits por un número fraccional de bytes es una locura y me sorprende que no genere una excepción, pero eso es Javascript, siempre dispuesto a redefinir "loco".

Si fuera tú, evitaría usar esta función " " ;. Su único valor es como posible causa raíz de una condición de error inusual. Usa Math.floor () y ten piedad del próximo programador que mantendrá el código.


Confirmando un par de sospechas que tuve al leer la pregunta:

  • Desplazar a la derecha cualquier número fraccionario x por cualquier número fraccionario y simplemente truncará x , dando el mismo resultado que Math .floor () mientras confunde completamente al lector.
  • 2.999999999999999777955395074968691915 ... es simplemente el número más grande que se puede diferenciar de " 3 " ;. Intente evaluarlo por sí mismo: si le agrega algo, evaluará a 3. Este es un artefacto del navegador y la implementación de punto flotante del sistema local.

Si desea profundizar, lea "Lo que todo informático debe saber sobre la aritmética de coma flotante": http://docs.sun.com/source/806-3568/ncg_goldberg.html

No creo que tu cambio a la derecha sea relevante. Simplemente está más allá de la resolución de una constante de coma flotante de doble precisión.

En Chrome:

var x = 2.999999999999999777955395074968691915273666381835937499999;
var y = 2.9999999999999997779553950749686919152736663818359375;

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

Imprime: x = 2.9999999999999996 y = 3

Pruebe este javascript:   alerta (parseFloat (" 2.9999999999999997779553950749686919152736663818359374999999 "));

Entonces prueba esto:   alerta (parseFloat (" 2.9999999999999997779553950749686919152736663818359375 "));

Lo que estás viendo es simple inexactitud de punto flotante. Para obtener más información al respecto, vea esto por ejemplo: http://en.wikipedia.org/wiki / Floating_point # Accuracy_problems .

El problema básico es que lo más cerca que un valor de punto flotante puede llegar a representar el segundo número es mayor o igual a 3, mientras que los cierres que un flotador puede alcanzar al primer número son estrictamente menores que tres.

En cuanto a por qué el desplazamiento a la derecha por 0.5 hace algo sensato, parece que 0.5 se está convirtiendo a un int (0) de antemano. Luego, el flotador original (2.999 ...) se convierte a un int por truncamiento, como de costumbre.

El operador de cambio a la derecha solo opera en números enteros (ambos lados). Por lo tanto, desplazarse hacia la derecha en .5 bits debe ser exactamente equivalente a desplazarse hacia la derecha en 0 bits. Y, el lado izquierdo se convierte en un número entero antes de la operación de cambio, que hace lo mismo que Math.floor ().

  

Sospecho que convertir 2.9999999999999997779553950749686919152736663818359374999999   Para su representación binaria sería esclarecedor. Probablemente sea solo 1 bit diferente   de verdadero 3.

Buena suposición, pero sin cigarro. Como el número FP de doble precisión tiene 53 bits, el último número FP antes del 3 es en realidad (exacto): 2.999999999999999555910790149937383830547332763671875

Pero por qué es 2.9999999999999997779553950749686919152736663818359375

(y esto es exacto, no 49999 ...!)

¿cuál es más alto que la última unidad visualizable? Redondeo La rutina de conversión (Cadena a número) simplemente está programada correctamente para redondear la entrada al siguiente número de punto flotante.

2.999999999999999555910790149937383830547332763671875

....... (valores entre, aumentando) - > redondear hacia abajo

2.9999999999999997779553950749686919152736663818359375

....... (valores entre, aumentando) - > redondear hasta 3

3

La entrada de conversión debe usar precisión completa. Si el número es exactamente la mitad entre esos dos números fp (que es 2.9999999999999997779553950749686919152736663818359375) el redondeo depende de las banderas establecidas. El redondeo predeterminado es redondeado a par, lo que significa que el número se redondeará al siguiente número par.

Ahora

3 = 11. (binario)

2.999 ... = 10.11111111111 ...... (binario)

Todos los bits están configurados, el número siempre es impar. Eso significa que el número medio exacto se redondeará, por lo que está obteniendo el extraño período ..... 49999 porque debe ser más pequeño que la mitad exacta para distinguirse de 3.

Sospecho que convertir 2.9999999999999997779553950749686919152736663818359374999999 a su representación binaria sería esclarecedor. Probablemente sea solo 1 bit diferente de la verdadera 3.

Y para agregar a la respuesta de John, las probabilidades de que esto sea más eficaz que Math.floor son muy pequeñas.

No sé si JavaScript usa números de punto flotante o algún tipo de biblioteca de precisión infinita, pero de cualquier manera, obtendrá errores de redondeo en una operación como esta, incluso si está bastante bien definida.

Se debe tener en cuenta que el número " .0000000000000007779553950749686919152736663818359374 " es muy posiblemente el Epsilon , definido como " el número más pequeño E tal que (1 + E ) > 1. "

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top