Motivo: o JS processa a adição, subtração, multiplicação e divisão de decimais de acordo com 2. Com base no ARG1, a precisão do ARG2 é expandida ou inversamente expandida; portanto, a seguinte situação ocorrerá.
O problema de adicionar, subtrair, multiplicação e divisão de pontos decimais em JavaScript (JS) é um bug em JS como 0,3*1 = 0,299999999999, etc. Os seguintes listam quatro algoritmos JS que podem calcular perfeitamente a precisão correspondente.
função accdiv (arg1, arg2) {var t1 = 0, t2 = 0, r1, r2; tente {t1 = arg1.toString (). split (".") [1] .Length} catch (e) {} tente {t2 = arg2.toString (). split (".") [1] .Length} Catch (e) {} com (math) {r1 = número (número (arg1.toss. r2 = número (arg2.toString (). substituir (".", ")) retornar accmul (((r1/r2), pow (10, t2-t1)); }} // função múltipla accmul (arg1, arg2) {var m = 0, s1 = arg1.toString (), s2 = arg2.toString (); tente {m+= s1.split (".") [1] .Length} catch (e) {} tente {m+= s2.split (".") [1] .Length} Catch (e) {} Número de retorno (s1.replace (".", ")*)*FUNCLATE (s2.replace (" "" ". Accadd (arg1, arg2) {var r1, r2, m; tente {r1 = arg1.toString (). split (".") [1] .Length} Catch (e) {r1 = 0} tente {r2 = arg2.tostring (). split ("") [1] .length} catch (e) {r2 = 0} m = math.pow (10, 10, math.M. (arg1*m+arg2*m)/m} // função de subtração subtr (arg1, arg2) {var r1, r2, m, n; tente {r1 = arg1.toString (). split (".") [1] .Length} Catch (e) {r1 = 0} tente {r2 = arg2.tostring (). split (".") [1] .length} catch (e) {r2 = 0} m = math.pow (10, 10, math.m. n = (r1> = r2)? R1: r2; return ((arg1*m-arg2*m)/m) .tofixado (n); }Vamos analisar a perda de precisão digital em JavaScript.
1. Alguns problemas típicos da perda de precisão digital JS
1. Adicione dois números simples de ponto flutuante
0,1 + 0,2! = 0,3 // true
Firebug
Este não é realmente um problema de Firebug, você pode experimentá -lo com alerta (haha, brincadeira).
Confira os resultados do cálculo de Java
Dê uma olhada no Python
2. Operação inteira grande
99999999999999999999 == 10000000000000000001 //?
Firebug
Os números de 16 dígitos e 17 dígitos são realmente iguais, não é razoável.
Também gosto
var x = 9007199254740992x + 1 == x //?
Veja os resultados
As três visualizações foram subvertidas novamente.
3. Tofixed não vai arredondar (cromo)
1.335.Tofixed (2) // 1.33
Firebug
Houve preços inconsistentes no Chrome e em outros navegadores on -line, e é precisamente por causa da questão da compatibilidade com tofixada.
2. Razões para a precisão da perda de número JS
Alguns números não podem ser expressos finalmente por implementações binárias e limites de bits. Assim como alguns números irracionais não podem ser finalmente representados, como o PI 3.1415926 ..., 1.3333 ..., etc. O JS segue a especificação IEEE 754, adota armazenamento de dupla precisão e ocupa 64 bits. Como mostrado
significado
Números de ponto flutuante, p.
0,1 >> 0,0001 1001 1001 1001… (1001 loop infinito) 0,2 >> 0,0011 0011 0011 0011… (0011 loop infinito)
Nesse momento, você só pode contorná-lo imitando o sistema decimal, mas existem apenas dois sistemas binários: 0 e 1, para que ele se torne 0 e entre 1. Essa é a razão fundamental pela qual alguns números de ponto flutuante nos computadores são erros e precisão perdida.
A perda de precisão de números inteiros grandes é essencialmente o mesmo que os números de ponto flutuante. O bit Mantissa máximo é de 52 bits; portanto, o número inteiro máximo que pode ser representado com precisão no JS é Math.pow (2, 53), que é 9007199254740992.
Mais de 9007199254740992 pode perder a precisão
9007199254740992 >> 1000000000000000 ... 000 // TOTAL 53 09007199254740992 + 1 >> 100000000000000 ... 001 // no meio 52 0900719254740992 + 2 >>
Na verdade
9007199254740992 + 1 // perdeu 9007199254740992 + 2 // não perdeu 9007199254740992 + 3 // perdido 9007199254740992 + 4 // não perdido
Os resultados são mostrados na figura
Como mencionado acima, podemos saber que os números aparentemente finitos são infinitos na representação binária do computador. Devido ao limite de bits de armazenamento, há "escape" e a perda de precisão ocorre.
Para uma análise mais aprofundada, você pode ler este artigo (longo e fedorento): o que todo cientista da computação deve saber sobre aritmética de ponto flutuante
3. Solução
Para números inteiros, a probabilidade de problemas no front-end pode ser relativamente baixa. Afinal, poucas empresas precisam usar números inteiros super grande. Enquanto o resultado do cálculo não exceder o Math.Pow (2, 53), a precisão não será perdida.
Para decimais, ainda existem muitas chances de problemas com o front-end, especialmente em alguns sites de comércio eletrônico que envolvem dados como valores. Solução: coloque o decimal no número inteiro (múltiplo) e depois encolher de volta ao múltiplo original (dividindo múltiplo)
// 0,1 + 0,2 (0,1*10 + 0,2*10) / 10 == 0,3 // true
A seguir, é apresentado um objeto que escrevi para bloquear a perda de precisão de adição, subtração, multiplicação e divisão de decimais. Obviamente, o número inteiro convertido não pode exceder 9007199254740992.
/*** O flotobj contém quatro métodos: adição, subtração, multiplicação e divisão, o que pode garantir que o cálculo do número de ponto flutuante não perca a precisão ** Sabemos que os cálculos do número de ponto flutuante terão perda de precisão (ou erro de arredondamento). A causa raiz é que alguns números não podem ser expressos nos limites binários e implementando* o seguinte é a representação binária correspondente a decimal decimal* 0,1 >> 0,0001 1001 1001 1001 1001… (1001 loop infinito)* 0,2 >> 0,0011 0011 0011 0011… (0011 um infinito loop)*. Por exemplo, o JavaScript usa tipos numéricos de armazenamento de 64 bits; portanto, aqueles que os excedem serão descartados. A parte perdida é a parte perdida em precisão. ** ** Método *** Adicionar / subtrair / multiplicar / dividir ** ** Explicação *** 0.1 + 0,2 == 0,30000000000000000004 (0,0000000000000004 MAIS)* 0.2 + 0,4 == 0,6000000000000001 (0,699999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999990 (0,00000000000000002 MAIS) ** Flotobj.add (0,1, 0,2) >> 0,3*floatobj.multiPly (19,9, 100) >> 1990 **/var flotoBJ = function () {/** Determine se é um inteiro*/função isinteger (jj) {retornar Número do ponto em um número inteiro e retorne um número inteiro e múltiplo. Por exemplo, 3.14 >> 314, o múltiplo é 100* @param floatnum {number} decimal* @return {object}* {times: 100, num: 314}*/function toInteger (floatnum) {var ret = {times: 1, num: 0} se (isinteger (floatnum) {times: 1, 1, 0} se (isinteger (sloatnum) {times: 1, 1, 0} se (isintetger (sloatnum) {var = {times: 1, num: 0} if (isinteger (sloatnum)) {times: 1, num: 0} se (isinteger (slothnum) {times: 1, num: 0} if (isinteger (sloatger) {times: 1, num: 0} se (isinteger (isIrTURGERM) {times. floatnum + '' var dotpos = strfi.indexOf ('.') var len = strfi.substr (dotpos + 1) .LengthVar Times = math.pow (10, len) var intnum = parseInt (floatnum* times + 0.5)/ret.times = times. Operações de divisão para garantir que a precisão não esteja perdida* IDEIA: amplie o decimal em um número inteiro (multiplicador), execute operações aritméticas e depois encolhem para um decimal (dividido) ** @param a {número} número 1* @param b {número} número 2* @Param Digits {Number} Accuracy, número 1* @param B {Número} Número 2* Lugares*@param op {string} Tipo de operação, com adição, subtração, multiplicação e divisão (adicione/subtraia/multiplique/divida) **/operação de função (a, b, dígitos, op) {var o1 = ToInteger (a) var o2 = ToInteger (b) var n1 = o1.numVar n2 = O2.NUM AT O2 = TOINTEGER (B) var n1 = o1.numVar N2 = O2.NUM TO = T1> T2? T1: resultado t2var = nullswitch (op) {case 'add': if (t1 === t2) {// dois lugares decimais são o mesmo resultado = n1 + n2} else if (t1> t2) {// o1 de decimal maior que o2Result = n1 + n2 * (t1 / t2) (T2 / T1) + N2} RETORNO DE RETORNO / MAXCASE 'SUBRACTO': if (t1 === T2) {resultado = n1 - n2} else if (t1> t2) {resultado = n1 - n2 * (t1 / t2)} else {resultado = n1 * (t2 / t1) - 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) {Retorne operação (a, b, dígitos, 'multiply')} função divide (a, b, dígitos) {retorna operação (a, b, dígitos, 'divide')} // exportsreturn {add: add, subtraia: subtrair, multiplicar: multiplicar, divide: divide}}} ();A correção de Tofixed é a seguinte
// função de correção tofixada tofixed (num, s) {var times = math.pow (10, s) var des = num * times + 0.5des = parseint (des, 10) / times des + ''}