一个js做相加运算的问题,但是相加之后出现问题。

JavaScript021

一个js做相加运算的问题,但是相加之后出现问题。,第1张

JavaScript小数在做四则运算时,精度会丢失

那么你需要写个函数来处理这个情况,比如下面几个有用的函数:

//说明:javascript的加法结果会有误差,在两个浮点数相加的时候会比较明显。这个函数返回较为精确的加法结果。

//调用:accAdd(arg1,arg2)

//返回值:arg1加上arg2的精确结果

function accAdd(arg1, arg2) {

var r1,

r2,

m

try {

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))

return (arg1 * m + arg2 * m) / m

}

//给Number类型增加一个add方法,调用起来更加方便。

Number.prototype.add = function(arg) {

return accAdd(arg, this)

}

//说明:javascript的加法结果会有误差,在两个浮点数相加的时候会比较明显。这个函数返回较为精确的加法结果。

//调用:accAdd(arg1,arg2)

//返回值:arg1加上arg2的精确结果

function accAdd(arg1, arg2) {

var r1,

r2,

m

try {

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))

return (arg1 * m + arg2 * m) / m

}

//给Number类型增加一个add方法,调用起来更加方便。

Number.prototype.add = function(arg) {

return accAdd(arg, this)

}

减法

Js代码

[code]

//说明:javascript的减法结果会有误差,在两个浮点数相加的时候会比较明显。这个函数返回较为精确的减法结果。

//调用:accSub(arg1,arg2)

//返回值:arg1减上arg2的精确结果

function accSub(arg1, arg2) {

return accAdd(arg1, -arg2)

}

//给Number类型增加一个sub方法,调用起来更加方便。

Number.prototype.sub = function(arg) {

return accSub(this, arg)

}

//说明:javascript的减法结果会有误差,在两个浮点数相加的时候会比较明显。这个函数返回较为精确的减法结果。

//调用:accSub(arg1,arg2)

//返回值:arg1减上arg2的精确结果

function accSub(arg1, arg2) {

return accAdd(arg1, -arg2)

}

很正常的,浮点数运算的误差。哪种语言都这样,只是误差大小不同而已。 \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

最简单的就是先把0.1和0.2换成别的数字(因为在js中只有这两个数相加有bug,例如:1.1+1.2不会有问题),所以先让0.1和0.2分别乘以10,求和之后再除以10 ,则不会有问题:

可能很多小伙伴都忘记了js的Number对象有一个保留小数位数的方法:toFixed();传入一个需要保留的位数就OK:

因为toFixed方法返回的是一个字符串,所以别忘了把字符串转回浮点数

计算机内部如何表示数

我们都知道,计算机用位来储存及处理数据。每一个二进制数(二进制串)都一一对应一个十进制数。

这里以十进制数13来展示“按位计数法”如何表示整数:

十进制值进制 按位格式描述

13 10 13 1x10^1 + 3x10^0 = 10 + 3

13 2 11011x2^3 + 1x2^2 + 0x2^1 + 1x2^0 = 8 + 4 + 0 + 1

十进制值进制 按位格式描述

0.625 10 0.625 6x10^-1 + 2x10^-2 + 5x10^-3 = 0.6 + 0.02 + 0.005

0.625 2 0.101 1x2^-1 + 0 x2^-2 + 1x2^-3 = 1/2 + 0 + 1/8

十进制整数转二进制方法:除2取余;十进制小数转二进制方法:乘2除整

十进制0.1转换成二进制,乘2取整过程:

0.1 * 2 = 0.2 # 0

0.2 * 2 = 0.4 # 0

0.4 * 2 = 0.8 # 0

0.8 * 2 = 1.6 # 1

0.6 * 2 = 1.2 # 1

0.2 * 2 = 0.4 # 0

从上面可以看出,0.1的二进制格式是:0.0001100011....。这是一个二进制无限循环小数,但计算机内存有限,我们不能用储存所有的小数位数。那么在精度与内存间如何取舍呢?

有误差的两个数,其计算的结果,当然就很可能与我们期望的不一样了。注意前面的这句话中的“很可能”这三个字?为啥是很可能昵?

0.1 + 0.1 为什么等于0.2

答案是:两个有舍入误差的值在求和时,相互抵消了,但这种“负负得正,相互抵消”不一定是可靠的,当这两个数字是用不同长度数位来表示的浮点数时,舍入误差可能不会相互抵消。

又如,对于 0.1 + 0.3 ,结果其实并不是0.4,但0.4是最接近真实结果的数,比其它任何浮点数都更接近。许多语言也就直接显示结果为0.4了,而不展示一个浮点数的真实结果了。

另外要注意,二进制能精确地表示位数有限且分母是2的倍数的小数,比如0.5,0.5在计算机内部就没有舍入误差。所以0.5 + 0.5 === 1

计算机这样胡乱舍入,能满足所有的计算需求吗

我们看两个现实的场景:

对于一个修建铁路的工程师而言,10米宽,还是10.0001米宽并没有什么不同。铁路工程师就不需要这么高0.x这样的精度

对于芯片设计师,0.0001米就会是一个巨大不同,他也永远不用处理超过0.1米距离

不同行业,要求的精度不是线性的,我们允许(对结果无关紧要的)误差存在。10.0001与10.001在铁路工程师看来都是合格的。

虽然允许误差存在,但程序员在使用浮点数进行计算或逻辑处理时,不注意,就可能出问题。记住,永远不要直接比较两个浮点的大小: