JS数值计算精度

在JS里面,Number 类型包含整型和浮点型,是使用IEEE754格式来表示的。我们可能都知道在JS里面进行数值运算的时候,得到的结果可能和预期不符,可以看下面的例子:

var num1 = 0.1;
var num2 = 0.2;
alert(num1+num2 === '0.3');  // false  
// num1+num2=0.30000000000000004

那么在JS里面为什么会出现上面的问题呢?
其实对于浮点数的四则运算,几乎所有的编程语言都会有类似精度误差的问题,只不过在 C++/C#/Java 这些语言中已经封装好了方法来避免出现精度的问题。
而 JavaScript 是一门弱类型的语言,从设计思想上就没有对浮点数有个严格的数据类型,所以精度误差的问题就显得格外突出。下面就分析下为什么会有这个精度误差,以及怎样修复这个误差。
首先,我们要从计算机角度来思考 0.1 + 0.2 这个浮点运算问题。我们知道,计算机只能读懂二进制,而不是十进制,所以需要先把 0.1 和 0.2 转换成二进制:
0.1 —>0.1.toString(2) —> 0.0001100110011(无限循环…)

0.2 —》0.2.toString(2) —> 0.001100110011(无限循环…)

双精度浮点数的小数部分最多支持 52 位,所以两者相加之后得到这么一串0.0100110011001100110011001100110011001100110011001100,因浮点数小数位的限制而截断的二进制数字,这时候,再将二进制转换为十进制,就成了 0.30000000000000004。

原来如此,那怎么解决这个问题呢?我想要的结果就是 0.1 + 0.2 === 0.3 啊!!!有种最简单的解决方案,就是给出明确的精度要求,在返回值的过程中,计算机会自动四舍五入,如:

var num1 = 0.1; 
var num2 = 0.2; 
alert( parseFloat((num1 + num2).toFixed(2)) === 0.30 );

但四舍五入明显不是一劳永逸的办法。如果能有一个方法可以帮我们解决这些浮点数的精度问题,那该多好!所以,我们就自己写一个方法。试试下面这个方法:

var formatNum = function(f, digit) { 
    var m = Math.pow(10, digit);  // digit是参数的最大精度
    return parseInt(f * m, 10) / m; 
} 
var num1 = 0.1; 
var num2 = 0.2;
alert(formatNum(num1 + num2, 1) === 0.3);

这个方法是什么意思呢?
为了避免产生精度差异,我们先把需要计算的数字乘以 10 的 n 次幂,换算成计算机能够精确识别的整数,然后再除以 10 的 n 次幂。

【注意】
在实际的使用中,我们还可以使用 toPrecision 方法

var num1 = 0.1;
var num2 = 0.2;
parseFloat((num1+num2).toPrecision(12)) === 0.3

toPrecision 和 toFixed究竟有什么区别?
toPrecison也是指定参数的精度,不过它是从参数的整数位即第一位不为0的数字开始计算位数精度,而toFixed则是对参数的小数位开始计算位数精度。两者在进行最后一位数字的判定时都是采用四舍五入的方法。详细可参考:链接

你可能感兴趣的:(JS数值计算精度)