sirve - tolocalestring() javascript number
Number.prototype.toFixed: increĆblemente correcto en Internet Explorer (3)
Considera lo siguiente:
var x = 2.175;
console.log(x.toFixed(2)); // 2.17
¿Qué? No, no es sorpresa aquí. Eso es bastante obvio, ver: el literal número 2.175
realidad está almacenado en la memoria (según las reglas IEEE-754) como un valor que es solo un poco más pequeño que 2.175. Y eso es fácil de probar:
console.log(x.toFixed(20)); // 2.17499999999999982236
Así es como funciona en las últimas versiones de Firefox, Chrome y Opera en la configuración de Windows de 32 bits. Pero esa no es la pregunta.
La verdadera pregunta es cómo Internet Explorer 6 (!) Realmente logra hacerlo derecho como lo hacen los humanos:
var x = 2.175;
console.log(x.toFixed(2)); // 2.18
console.log(x.toFixed(20)); // 2.17500000000000000000
De acuerdo, sobredramaticé: en realidad todos los Explorers de Internet en los que probé esto (IE8-11, ¡e incluso MS Edge!) Se comportan de la misma manera. Aún así, ¿WAT?
ACTUALIZACIÓN: Se vuelve extraño:
x=1.0;while((x-=0.1) > 0) console.log(x.toFixed(20));
IE Chrome
0.90000000000000000000 0.90000000000000002220
0.80000000000000000000 0.80000000000000004441
0.70000000000000010000 0.70000000000000006661
0.60000000000000010000 0.60000000000000008882
0.50000000000000010000 0.50000000000000011102
0.40000000000000013000 0.40000000000000013323
0.30000000000000015000 0.30000000000000015543
0.20000000000000015000 0.20000000000000014988
0.10000000000000014000 0.10000000000000014433
0.00000000000000013878 0.00000000000000013878
¿Por qué la diferencia, en todas menos en la última? ¿Y por qué no hay diferencia en el último? Es muy similar para x=0.1; while(x-=0.01)...
x=0.1; while(x-=0.01)...
, por cierto: hasta que nos toFixed
a cero, toFixed
in IE aparentemente intenta cortar algunas esquinas.
Descargo de responsabilidad: sí sé que las matemáticas de coma flotante son un poco defectuosas. Lo que no entiendo es cuál es la diferencia entre IE y el resto del mundo del navegador.
Agradezco la contribución de Eric, pero, con el debido respeto, no responde la pregunta. Admito que era demasiado irónico con esas frases "correctas" y "increíblemente correctas"; pero sí, entiendo que el comportamiento de IE es una desviación en realidad.
De todas formas. Todavía estaba buscando una explicación de por qué el IE se comporta de manera diferente, y finalmente obtuve algo que parecía una pista ... irónicamente, en el rastreador de Mozilla, en esta larga discusión . Citar:
OUTPUT IN MOZILLA:
a = 0.827 ==> a.toFixed(17) = 0.82699999999999996
b = 1.827 ==> b.toFixed(17) = 1.82699999999999996
OUTPUT IN IE6:
a = 0.827 ==> a.toFixed(17) = 0.82700000000000000
b = 1.827 ==> b.toFixed(17) = 1.82700000000000000
La diferencia observada en IE y Mozilla es la siguiente. IE está almacenando ''a'' como una cadena y Mozilla está almacenando ''a'' como un valor. La especificación no establece el formato de almacenamiento. Por lo tanto, cuando IE hace
a.toFixed
, comienza con una representación de cadena exacta, mientras que Mozilla sufre las conversiones de ida y vuelta.
Sería genial tener una especie de confirmación oficial al respecto, pero al menos eso explica todo lo que he visto hasta ahora. En particular,
console.log( 0.3.toFixed(20) ); // 0.30000000000000000000
console.log( 0.2.toFixed(20) ); // 0.20000000000000000000
console.log( (0.3 - 0.2).toFixed(20) ); // "0.09999999999999998000"
El comportamiento informado se desvía de los requisitos de la especificación ECMA .
Según la cláusula 8.5, el tipo Number
tiene los valores binarios IEEE-754 de 64 bits, excepto que solo hay un NaN. Entonces 2.175 no puede ser representado exactamente; lo más cercano que puede obtener es 2.17499999999999982236431605997495353221893310546875.
Según 15.7.4.5, toFixed(20)
utiliza un algoritmo que se reduce a:
- "Sea n un entero cuyo valor matemático exacto de n ÷ 10 f - x es lo más cercano posible a cero. Si hay dos de tales n , elija el n más grande ".
- En lo anterior, f es 20 (el número de dígitos solicitados), y x es el operando, que debe ser 2.17499999999999982236431605997495353221893310546875.
- Esto da como resultado la selección de 217499999999999982236 para n .
- Luego se formatea n , produciendo "2.17499999999999982236".
En primer lugar, los puntos flotantes no se pueden representar "con precisión " en números binarios. Habrá una elevación / depresión, o el valor será un poco más alto o un poco más bajo . Cuánto está elevado / deprimido depende de cómo se realice la conversión. No hay exactamente un " valor correcto " incluso para una salida de cadena de ECMAScript a toFixed()
.
Pero los estándares de ECMA hacen las cosas más interesantes al establecer estándares. Lo cual es algo bueno en mi opinión. Es como " Si todos vamos a cometer errores de todos modos, hagamos el mismo " .
Entonces, la pregunta ahora sería cómo y por qué IE se desvía de los Estándares. Examinemos los siguientes casos de prueba.
Los candidatos son IE 10.0.9200.16688 y Chrome 30.0.1599.69, que se ejecutan en x64 Windows 8 Pro.
Case Code IE (10) Chrome (30)
--------------------------------------------------------------------------------
A (0.06).toFixed(20) 0.60000000000000000000 0.05999999999999999778
B (0.05+0.01).toFixed(20) 0.06000000000000000500 0.06000000000000000472
Entonces, independientemente de que sea IE o Chrome, vemos que (0.06)
no es exactamente igual a (0.05+0.01)
. ¿Porqué es eso? Es porque (0.06) tiene una representación muy cercana pero no igual a (0.06), también lo hace (0.05) y (0.01). Cuando realizamos una operación, como una adición, los errores muy menos significativos pueden resumirse para convertirse en un error de magnitud ligeramente diferente .
Ahora, la diferencia en el valor representado en diferentes navegadores se puede ver afectada por dos motivos:
- El algoritmo de conversión utilizado.
- Cuando la conversión se lleva a cabo.
Ahora no sabemos qué usa algo IE ya que no puedo ver su origen. Pero los casos de prueba anteriores demuestran claramente otra cosa, IE y Chrome manejan la conversión " no solo de manera diferente " sino también " en una ocasión diferente ".
En JavaScript, cuando creamos un número (instancia aka de una clase Number
con o sin la new
palabra clave), en realidad proporcionamos un literal
. El literal siempre es una cadena incluso si denota un número [1] . El navegador analiza el literal y crea el objeto y asigna el valor representado.
Ahora, aquí es donde las cosas tienden a ir de diferentes maneras. IE mantiene la conversión hasta que se necesite. Eso significa que hasta que se lleve a cabo una operación, IE mantiene el número como literal (o como un formato intermedio). Pero Chrome lo convierte de inmediato en el formato operativo.
Una vez finalizada la operación, IE no vuelve al formato literal o al formato intermedio, ya que no tiene sentido y puede causar una ligera pérdida de precisión.
Espero que eso aclare algo.
[1] El valor representado en el código siempre es literal
s. Si los cita, se llaman String Literal
s.