Razón: JS procesa la adición, resta, multiplicación y división de decimales según 2. Sobre la base de Arg1, la precisión de Arg2 se expande o se expande inversamente, por lo que ocurrirá la siguiente situación.
El problema de sumar, restar, multiplicar y división de puntos decimales en JavaScript (JS) es un error en JS, como 0.3*1 = 0.2999999999999, etc. Los siguientes enumeran cuatro algoritmos JS que pueden calcular perfectamente la precisión correspondiente.
función accDiv (arg1, arg2) {var t1 = 0, t2 = 0, r1, r2; Pruebe {t1 = arg1.ToString (). Split (".") [1] .Length} Catch (e) {} try {t2 = arg2.ToString (). Split (".") [1] .length} capt (e) {} con (matemáticas) {r1 = number (arg1.tostring (). reemplazar ("." "))))))))) r2 = número (arg2.ToString (). reemplazar (".", ")) return accMul ((r1/r2), pow (10, t2-t1)); }} // función múltiple accMul (arg1, arg2) {var m = 0, s1 = arg1.ToString (), s2 = arg2.ToString (); Pruebe {m+= s1.split (".") [1] .length} catch (e) {} try {m+= s2.split (".") [1] .length} catch (e) {} número de retorno (s1.replace (".", ")*Número (s2.replace (". "")) Accadd (arg1, arg2) {var r1, r2, m; Pruebe {r1 = arg1.ToString (). Split (".") [1] .Length} Catch (e) {r1 = 0} try {r2 = arg2.ToString (). Split (".") [1] .length} Catch (e) {r2 = 0} m = Math.Pow (10, math.max (r1, r2)) } // función de subtracción subtr (arg1, arg2) {var r1, r2, m, n; Pruebe {r1 = arg1.ToString (). Split (".") [1] .Length} Catch (e) {r1 = 0} try {r2 = arg2.ToString (). Split (".") [1] .length} Catch (e) {r2 = 0} m = Math.Pow (10, math.max (r1, r2)); n = (r1> = r2)? r1: r2; return ((arg1*m-arg2*m)/m) .tofixed (n); }Analicemos la pérdida de precisión digital en JavaScript.
1. Algunos problemas típicos de pérdida de precisión digital JS
1. Agregue dos números de punto flotante simples
0.1 + 0.2! = 0.3 // Verdadero
Firebug
Esto realmente no es un problema de Firebug, puede probarlo con alerta (jaja, broma).
Echa un vistazo a los resultados del cálculo de Java
Echa un vistazo a Python
2. Operación entera grande
999999999999999999 == 1000000000000000000001 //?
Firebug
Los números de 16 dígitos y 17 dígitos son realmente iguales, no es razonable.
También me gusta
var x = 9007199254740992x + 1 == x //?
Ver los resultados
Las tres vistas fueron subvertidas nuevamente.
3. Tofijo no redondeará (Chrome)
1.335.tofixed (2) // 1.33
Firebug
Ha habido precios inconsistentes en Chrome y otros navegadores en línea, y se debe precisamente al problema de compatibilidad tofijo.
2. Razones para la precisión de la pérdida de números JS
Algunos números no pueden expresarse en finitor por implementaciones binarias y límites de bits. Al igual que algunos números irracionales no pueden representarse finitamente, como PI 3.1415926 ..., 1.3333 ..., etc. JS sigue la especificación IEEE 754, adopta almacenamiento de doble precisión y ocupa 64 bits. Como se muestra
significado
Números de puntos flotantes, p.
0.1 >> 0.0001 1001 1001 1001 ... (1001 Infinite Loop) 0.2 >> 0.0011 0011 0011 0011 ... (0011 Infinite Loop)
En este momento, solo puede redondearlo imitando el sistema decimal, pero solo hay dos sistemas binarios: 0 y 1, por lo que se convierte en 0 y lo rodea por 1. Esta es la razón fundamental por la cual algunos números de punto flotante en las computadoras son errores y precisión perdida.
La pérdida de precisión de enteros grandes es esencialmente la misma que los números de puntos flotantes. El bit Mantissa máximo es de 52 bits, por lo que el entero máximo que se puede representar con precisión en JS es Math.Pow (2, 53), que es 9007199254740992.
Más de 9007199254740992 puede perder precisión
90071992547409992 >> 1000000000000000 ... 000 // Total 53 090071999254740992 + 1 >> 1000000000000000000 ... 001 // en el medio 52 09007199254740992 + 2 >> 100000000000000000000000000 ...
De hecho
9007199254740992 + 1 // perdido 9007199254740992 + 2 // no perdido 9007199254740992 + 3 // perdido 9007199254740992 + 4 // no perdido
Los resultados se muestran en la figura
Como se mencionó anteriormente, podemos saber que los números aparentemente finitos son infinitos en la representación binaria de la computadora. Debido al límite de bits de almacenamiento, hay "escapado" y la pérdida de precisión ocurre.
Para un análisis más detallado, puede leer este documento (largo y maloliente): lo que todo científico informático debe saber sobre la aritmética de punto flotante
3. Solución
Para enteros, la probabilidad de problemas en el front-end puede ser relativamente baja. Después de todo, pocas empresas necesitan usar enteros súper grandes. Mientras el resultado del cálculo no exceda las matemáticas. POW (2, 53), la precisión no se perderá.
Para los decimales, todavía hay muchas posibilidades de problemas con el front-end, especialmente en algunos sitios web de comercio electrónico que involucran datos como cantidades. Solución: Coloque el decimal en el entero (múltiples múltiples) y luego reduzca el múltiplo original (dividiendo múltiples)
// 0.1 + 0.2 (0.1*10 + 0.2*10) / 10 == 0.3 // Verdadero
El siguiente es un objeto que escribí para bloquear la pérdida de precisión de suma, resta, multiplicación y división de decimales. Por supuesto, el entero convertido no puede exceder el 9007199254740992.
/*** Floatobj contiene cuatro métodos: adición, sustracción, multiplicación y división, lo que puede garantizar que el cálculo del número de punto flotante no pierda precisión ** Sabemos que los cálculos de los números de punto flotante tendrán pérdida de precisión (o error de redondeo). La causa raíz es que algunos números no se pueden expresar en los límites binarios y de implementación de la bit* La siguiente es la representación binaria correspondiente a decimal decimal* 0.1 >> 0.0001 1001 1001 1001 1001… (1001 Infinite Loop)* 0.2 >> 0.0011 0011 0011 0011… (0011 bucle infinito)* El almacenamiento de cada tipo de datos en una computadora es una amplia finita. Por ejemplo, JavaScript utiliza tipos numéricos de almacenamiento de 64 bits, por lo que se descartarán los que los exceden. La parte que se pierde es la parte que se pierde en precisión. ** ** method *** add / subtract / multiply /divide** ** explanation *** 0.1 + 0.2 == 0.300000000000000004 (0.0000000000000004 more) * 0.2 + 0.4 == 0.60000000000000000001 (0.00000000000000001) * 19.9 * 100 == 1989.9999999999999998 (0.0000000000000000002 menos) ** floatobj.add (0.1, 0.2) >> 0.3*floatobj.multiply (19.9, 100) >> 1990 **/var floatobj = function () {/** Determinar si OBJ es un entero*/function isInteger (obj) {return math.floor (obj) Número de punto flotante en un entero y devuelve un entero y un múltiplo. Por ejemplo, 3.14 >> 314, el múltiplo es 100* @param floatnum {número} decimal* @return {object}* {tiempos: 100, num: 314}*/function toInTeger (floatnum) {var ret = {tiempos: 1, num: 0} if (isInteger (floatnum)) {ret.num = floatnumnumnumnumnumnumnumnum floatnum + '' var dotpos = strfi.indexof ('.') var len = strfi.substr (dotPos + 1 .lengthvar times = math.pow (10, len) var intnum = parseInt (floatnum* times + 0.5, 10) ret.times = timesret.num = intnumretretret}/** Métode de núcleo, subdirctation, Subtractation, subdircation, Subtractation, Multiplattation, Multiplation, Multiplation, Multiplation, Multiplattion y intnumretur Operaciones de división para garantizar que la precisión no se pierda* Idea: amplíe el decimal en un entero (multiplicador), realice operaciones aritméticas y luego se reduzca a un decimal (dividido) ** @param un {número} de operación número 1* @param B {número} Número de operación 2* @param digits {número} Accuración, el número de puntos decimales de decimales, tal como 2, como 2, se retira como 2, se retira como 2, como 2, se retira como 2, como 2, se retira como 2. lugares*@param op {string} Tipo de operación, con suma, subtracción, multiplicación y división (agregue/rect/multiply/divide) **/operación de función (a, b, dígitos, op) {var o1 = tointeger (a) var o2 = tointeger (b) var n1 = o1.numvar n2 = o2.numvar t1 = o1.Times t2 = o2.times t2 = o2.times = t1> t2? t1: t2var result = nullSwitch (op) {case 'add': if (t1 === t2) {// dos decimales son el mismo resultado = n1 + n2} else if (t1> t2) {// o1 decimal lugares mayores que o2result = n1 + n2 * (t1 / t2)}}} {// o1 decimal lugares más (t2 / t1) + n2} Resultado de retorno / maxcase 'restar': if (t1 === T2) {result = n1 - n2} else if (t1> t2) {result = n1 - n2 * (t1 / t2)} else {result = n1 * (t2 / t1) - n2} return durt / maxcase 'múltiply': resultado = (n1 n2) t2) return resultcase 'divide':result = (n1 / n2) * (t2 / t1)return result}}// Four interfaces of addition, subtraction, multiplication and division function add(a, b, digits) {return operation(a, b, digits, 'add')}function subtract(a, b, digits) {return operation(a, b, digits, 'subtract')}function multiply(a, b, dígitos) {Operación de retorno (a, b, dígitos, 'multiplicar')} función divide (a, b, dígitos) {operación de retorno (a, b, dígitos, 'dividir')} // exportsreturn {agregar: agregar, restar: restar, multiplicar: multiplicar, dividir: divide}} ();La solución de Tofixed es la siguiente
// Función de fijación tofixed tofixed (num, s) {var times = math.pow (10, s) var des = num * times + 0.5des = parseint (des, 10) / timesreturn des + ''}