0.1 + 0.2 === 0.3 ?
实践
在日常生活中,如果被问到 0.1 + 0.2 等于 0.3 吗,你可以非常肯定回答是的。但是在 JS 中,就未必了。打开编辑器,试输入以下代码:
console.log(0.1 + 0.2 === 0.3) // false
运行结果是 false。
究其原因
javascript是弱类型语言,在计算的时候是通过转换成二进制然后进行计算的,而不是我们想象中的十进制的计算,而十进制转换成二进制时就会产生精度丢失的问题。所以 0.1 + 0.2 在 javascript 运算中到底等于多少呢?
console.log(0.1 + 0.2) // 0.30000000000000004
可以说是约等于 0.3,但并不完全等于0.3,原因就是精度丢失。
在实际开发中可能遇到的问题
既然我们知道JS在浮点运算时可能会遇到精度丢失的问题,那么在实际开发中我们要如何避免。
假设这样一个场景,一件商品是 0.1 元,另一件商品是0.2元,那么在计算总价的时候如何处理?
const a = 0.1
const b = 0.2
const total = a + b
console.log(total) // 0.30000000000000004
很明显这么写页面显示的即位0.30000000000000004,是不正确的,这样的数据传给后台也是不合理的。想要规避这个问题,我们究其原因既可解决。原因是浮点计算转换成二进制精度丢失,那么我们可以在整数的基础上进行计算。
const a = 0.1
const b = 0.2
const total = (a * 100 + b * 100) / 100
console.log(total) // 0.3
在计算金额的时候我们通常是精确到分的,那么只要乘以100,即可转换为整数,进行运算后,再 除以100进行还原!
终极解决方案
在实际开发中,我们未必就是处理金额问题,加入涉及到一些工业问题,或者需要较高精度的计算,难道我需要乘以更高位数使其变成整数吗?又或者JS不适合做高精度的计算吗?要回答这个问题,我们要考虑JS是否有一个浮点计算的终极解决方案。
-
方案一:借助第三方库
Decimal
引入:
$ npm install --save decimal.js
使用:
//加法运算
var a = 0.13;
var b = 0.25;
console.log('加法运算 a + b =', a + b);
console.log('使用Decimaljs a + b =', new Decimal(a).add(new Decimal(b)).toNumber());
//减法
var a = 1.0;
var b = 0.99
console.log('直接减法运算 a - b =', a - b);
console.log('使用Decimaljs a - b =', new Decimal(a).sub(new Decimal(b)).toNumber().toFixed(2);//保留两位数据
//乘法
var a = 1.01;
var b = 1.02;
console.log('直接乘法运算 a * b =', a * b);
console.log('使用Decimaljs a * b =', new Decimal(a).mul(new Decimal(b)).toNumber());
//除法
var a = 0.033;
var b = 10;
console.log('直接除法运算 a / b =', a / b);
console.log('使用Decimaljs a / b =', new Decimal(a).div(new Decimal(b)).toNumber());
- 方案二:toFixed()
const a = 0.11
const b = 0.2
const total = (a + b).toFixed(2)
console.log(total) //0.31
但需注意的是 toFixed 是四舍五入法。
0.1 + 0.2 如何 才能 === 0.3?
0.1 + 0.2 = 0.3 是常识,但是我们在JS中即给予否定。那么我们如何去判断两数相加是否等于第三个数?不难看出,在计算之后我们得到的数和0.3相差极小,也就是说是因为精度丢失所造成的误差,假如我们能够弥补这个误差,是不是就能够判断出相加后的结果等于0.3了呢?
- Number.EPSILON
Number.EPSILON 是一个数值极小的常量,我们只需要判断出 c - a - b 的绝对值小于这个常量,那么就表示误差在极小的范围,是一种解决比较浮点大小的常规解决方案。
console.log(0.1 + 0.2 === 0.3); // false
console.log(Math.abs(0.3 - 0.2 - 0.1) < Number.EPSILON); // true