关于JS小数计算误差的解决办法

JavaScript05

关于JS小数计算误差的解决办法,第1张

//加法

Number.prototype.add = function (arg) {

    var r1 = 0, r2 = 0, m

    //获取小数的位数并取最大值

    try { r1 = this.toString().split(".")[1].length } catch (e) { }

    try { r2 = arg.toString().split(".")[1].length } catch (e) { }

    m = Math.pow(10, Math.max(r1, r2))

    return (this * m + arg * m) / m

}

//减法

Number.prototype.sub = function (arg) {

    return this.add(-arg)

}

//乘法

Number.prototype.mul = function (arg) {

    var m = 0, s1 = this.toString(), s2 = arg.toString()

    //小数位数相加

    try { m += s1.split(".")[1].length } catch (e) { }

    try { m += s2.split(".")[1].length } catch (e) { }

    return Number(s1.replace(".", "")) * Number(s2.replace(".", "")) / Math.pow(10, m)

}

//除法

Number.prototype.div = function (arg) {

    var t1 = 0, t2 = 0, r1, r2

    try { t1 = this.toString().split(".")[1].length } catch (e) { }

    try { t2 = arg.toString().split(".")[1].length } catch (e) { }

    //将小数点去掉转换为整数

    r1 = Number(this.toString().replace(".", ""))

    r2 = Number(arg.toString().replace(".", ""))

    return (r1 / r2) * Math.pow(10, t2 - t1)

}

很正常的,浮点数运算的误差。哪种语言都这样,只是误差大小不同而已。 \x0d\x0a 用解析字符串的方式移动小数点,转化为整数,完毕后,在把小数点复位。 \x0d\x0a 浮点数运算的时候,先转化为二进制,用二进制来算,结果再转回十进制 \x0d\x0a 例如 :求1038.1-1000 \x0d\x0a 1038.1=10000001110.0001100110011001100110011001100110011001100..... \x0d\x0a 1000= 1111101000 \x0d\x0a 1038.1转化为二进制是个无限循环小数,1100是循环节,只能取近似值,误差就是这里产生的 \x0d\x0a如果浏览器版本高,可以用toFixed() 方法可把 Number 四舍五入为指定小数位数的数字.\x0d\x0a后有固定的 num 位数字。如果必要,该数字会被舍入,也可以用 0 补足,以便它达到指定的长度。如果 num 大于 le+21,则该方法只调用 NumberObject.toString(),返回采用指数计数法表示的字符串。\x0d\x0a语法\x0d\x0aNumberObject.toFixed(num)\x0d\x0a返回值\x0d\x0a返回 NumberObject 的字符串表示,不采用指数计数法,小数点后有固定的 num 位数字。如果必要,该数字会被舍入,也可以用 0 补足,以便它达到指定的长度。如果 num 大于 le+21,则该方法只调用 NumberObject.toString(),返回采用指数计数法表示的字符串。\x0d\x0a\x0d\x0a抛出\x0d\x0a当 num 太小或太大时抛出异常 RangeError。0 ~ 20 之间的值不会引发该异常。有些实现支持更大范围或更小范围内的值。\x0d\x0a\x0d\x0a当调用该方法的对象不是 Number 时抛出 TypeError 异常。\x0d\x0a在本例中,我们将把数字舍入为仅有一位小数的数字:\x0d\x0a\x0d\x0aShow the number 13.37 with one decimal:\x0d\x0a\x0d\x0avar num = new Number(13.37)\x0d\x0adocument.write (num.toFixed(1))\x0d\x0a\x0d\x0a\x0d\x0a输出:\x0d\x0aShow the number 13.37 with one decimal:\x0d\x0a13.4

浮点数值的最高精度是17位小数,但在进行运算的时候其精确度却远远不如整数;整数在进行运算的时候都会转成10进制; 而java和JavaScript中计算小数运算时,都会先将十进制的小数换算到对应的二进制,一部分小数并不能完整的换算为二进制,这里就出现了第一次的误差。待小数都换算为二进制后,再进行二进制间的运算,得到二进制结果。然后再将二进制结果换算为十进制,这里通常会出现第二次的误差。

所以(0.1+0.2)!=03

解决这种问题,可以将小数变成整数进行运算,然后再将结果变为小数。

//乘法

function multiNum (a,b){

  var c = 0,

d = a.toString(),

e = b.toString()

try {

      c += d.split(".")[1].length

} catch (f) { }

  try {

      c += e.split(".")[1].length

} catch (f) { }

  return Number(d.replace(".","")) * Number(e.replace(".","")) / Math.pow(10,c)

}

//除法

function divide (a,b){

  var c,d,e = 0,

f = 0

try {

      e = a.toString().split(".")[1].length

} catch (g) { }

  try {

      f = b.toString().split(".")[1].length

} catch (g) { }

  return c = Number(a.toString().replace(".","")),d = Number(b.toString().replace(".","")),this.mul(c / d,Math.pow(10,f - e))

}

//加法

function addNum (a,b){

  var c,d,e

try {

      c = a.toString().split(".")[1].length

} catch (f) {

      c = 0

}

  try {

      d = b.toString().split(".")[1].length

} catch (f) {

      d = 0

}

  return e = Math.pow(10,Math.max(c,d)),(multiNum(a,e) + multiNum(b,e)) / e

}

//减法

function subNum (a,b) {

  var c,d,e

try {

      c = a.toString().split(".")[1].length

} catch (f) {

      c = 0

}

  try {

      d = b.toString().split(".")[1].length

} catch (f) {

      d = 0

}

  return e = Math.pow(10,Math.max(c,d)),(multiNum(a,e) - multiNum(b,e)) / e

}