javasSript小数相加精度问题

木头的喵喵拖孩

问题描述

众所周知,在 javasSript 中 0.1 + 0.3 会出现精度问题,那么其他小数相加会不会也有问题呢?还有相乘呢?
测试用代码和结果如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
// 相加的精度问题
let arr = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9];
for (let i = 0; i < arr.length; i++) {
let val1 = arr[i];
for (let j = i; j < arr.length; j++) {
let val2 = arr[j];
console.log(`${val1} + ${val2} = ${val1 + val2}`);
}
}
// 0.1 + 0.1 = 0.2
// 0.1 + 0.2 = 0.30000000000000004
// 0.1 + 0.3 = 0.4
// 0.1 + 0.4 = 0.5
// 0.1 + 0.5 = 0.6
// 0.1 + 0.6 = 0.7
// 0.1 + 0.7 = 0.7999999999999999
// 0.1 + 0.8 = 0.9
// 0.1 + 0.9 = 1
// 0.2 + 0.2 = 0.4
// 0.2 + 0.3 = 0.5
// 0.2 + 0.4 = 0.6000000000000001
// 0.2 + 0.5 = 0.7
// 0.2 + 0.6 = 0.8
// 0.2 + 0.7 = 0.8999999999999999
// 0.2 + 0.8 = 1
// 0.2 + 0.9 = 1.1
// 0.3 + 0.3 = 0.6
// 0.3 + 0.4 = 0.7
// 0.3 + 0.5 = 0.8
// 0.3 + 0.6 = 0.8999999999999999
// 0.3 + 0.7 = 1
// 0.3 + 0.8 = 1.1
// 0.3 + 0.9 = 1.2
// 0.4 + 0.4 = 0.8
// 0.4 + 0.5 = 0.9
// 0.4 + 0.6 = 1
// 0.4 + 0.7 = 1.1
// 0.4 + 0.8 = 1.2000000000000002
// 0.4 + 0.9 = 1.3
// 0.5 + 0.5 = 1
// 0.5 + 0.6 = 1.1
// 0.5 + 0.7 = 1.2
// 0.5 + 0.8 = 1.3
// 0.5 + 0.9 = 1.4
// 0.6 + 0.6 = 1.2
// 0.6 + 0.7 = 1.2999999999999998
// 0.6 + 0.8 = 1.4
// 0.6 + 0.9 = 1.5
// 0.7 + 0.7 = 1.4
// 0.7 + 0.8 = 1.5
// 0.7 + 0.9 = 1.6
// 0.8 + 0.8 = 1.6
// 0.8 + 0.9 = 1.7000000000000002
// 0.9 + 0.9 = 1.8

// 相乘也会出现精度问题
let arr = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9];
for (let i = 0; i < arr.length; i++) {
let val1 = arr[i];
for (let j = i; j < arr.length; j++) {
let val2 = arr[j];
console.log(`${val1} * ${val2} = ${val1 * val2}`);
}
}
// 0.1 * 0.1 = 0.010000000000000002
// 0.1 * 0.2 = 0.020000000000000004
// 0.1 * 0.3 = 0.03
// 0.1 * 0.4 = 0.04000000000000001
// 0.1 * 0.5 = 0.05
// 0.1 * 0.6 = 0.06
// 0.1 * 0.7 = 0.06999999999999999
// 0.1 * 0.8 = 0.08000000000000002
// 0.1 * 0.9 = 0.09000000000000001
// 0.2 * 0.2 = 0.04000000000000001
// 0.2 * 0.3 = 0.06
// 0.2 * 0.4 = 0.08000000000000002
// 0.2 * 0.5 = 0.1
// 0.2 * 0.6 = 0.12
// 0.2 * 0.7 = 0.13999999999999999
// 0.2 * 0.8 = 0.16000000000000003
// 0.2 * 0.9 = 0.18000000000000002
// 0.3 * 0.3 = 0.09
// 0.3 * 0.4 = 0.12
// 0.3 * 0.5 = 0.15
// 0.3 * 0.6 = 0.18
// 0.3 * 0.7 = 0.21
// 0.3 * 0.8 = 0.24
// 0.3 * 0.9 = 0.27
// 0.4 * 0.4 = 0.16000000000000003
// 0.4 * 0.5 = 0.2
// 0.4 * 0.6 = 0.24
// 0.4 * 0.7 = 0.27999999999999997
// 0.4 * 0.8 = 0.32000000000000006
// 0.4 * 0.9 = 0.36000000000000004
// 0.5 * 0.5 = 0.25
// 0.5 * 0.6 = 0.3
// 0.5 * 0.7 = 0.35
// 0.5 * 0.8 = 0.4
// 0.5 * 0.9 = 0.45
// 0.6 * 0.6 = 0.36
// 0.6 * 0.7 = 0.42
// 0.6 * 0.8 = 0.48
// 0.6 * 0.9 = 0.54
// 0.7 * 0.7 = 0.48999999999999994
// 0.7 * 0.8 = 0.5599999999999999
// 0.7 * 0.9 = 0.63
// 0.8 * 0.8 = 0.6400000000000001
// 0.8 * 0.9 = 0.7200000000000001
// 0.9 * 0.9 = 0.81

解决方案

Number.EPSILON 解决相等判断的问题

如何解决 0.1 + 0.2 !== 0.3 的问题呢?

Number.EPSILON 是 es6 中新增的一个常量,用来表示一个最小的浮点数,这个值是 2 的-52 次方,也就是 2 的-52 次方的值,这个值可以用来判断两个浮点数是否相等。

所以可以自己实现一个判断浮点数相等的方法,如下:

1
2
3
4
5
6
7
8
9
/**
* 自定义判断浮点数相等的方法,对于精度要求较高的浮点数相等的方法,可以使用这个方法替代“===”
*/
function isEqual(num1, num2) {
return Math.abs(num1 - num2) < Number.EPSILON;
}

console.log(0.1 + 0.2 === 0.3); // false
console.log(isEqual(0.1 + 0.2, 0.3)); // true

目前该方法只能用作判断相等,不能让浮点数相加后的结果等于期望结果,如果需要浮点数相加后的结果等于期望结果,可以使用第三方库,比如 mathjs 库。

解决浮点数相互计算的问题

可以通过乘以一个倍数,去掉小数,然后相加,再除以这个倍数,来还原浮点数。

1
2
3
4
5
6
7
function add(num1, num2) {
const multiple = 10 ** 6; // 这个值任意取,只要可以将小数去掉即可
return (num1 * multiple + num2 * multiple) / multiple;
}

console.log(0.1 + 0.2); // 0.30000000000000004
console.log(add(0.1, 0.2)); // 0.3

使用第三方库

最好的办法其实还是使用第三方库。不用自己造轮子,省时省精力还不易出错。

mathjs

mathjs 官网
mathjs npm

1
npm i mathjs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const mathjs = require("mathjs");

// 相加

let a1 = 0.1 + 0.2;
console.log("原生相加结果:", a1); // 0.30000000000000004

let a2 = parseFloat(mathjs.add(mathjs.bignumber(0.1), mathjs.bignumber(0.2)));
console.log("mathjs相加结果:", a2); // 0.3

// 相乘
let b1 = 0.1 * 0.1;
console.log("原生相乘结果:", b1); // 0.010000000000000002

let b2 = parseFloat(
mathjs.multiply(mathjs.bignumber(0.1), mathjs.bignumber(0.1))
);
console.log("mathjs相乘结果:", b2); // 0.01

decimal.js

decimal.js 官网
decimal.js npm

1
npm i decimal.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const Decimal = require("decimal.js");

// 相加

let a1 = 0.1 + 0.2;
console.log("原生相加结果:", a1); // 0.30000000000000004

let a2 = parseFloat(new Decimal(0.1).plus(0.2));
console.log("decimal.js相加结果:", a2); // 0.3

// 相乘
let b1 = 0.1 * 0.1;
console.log("原生相乘结果:", b1); // 0.6000000000000001

let b2 = parseFloat(new Decimal(0.1).times(0.1));
console.log("mathjs相乘结果:", b2); // 0.01
  • 标题: javasSript小数相加精度问题
  • 作者: 木头的喵喵拖孩
  • 创建于: 2023-02-24 14:22:10
  • 更新于: 2024-05-21 10:56:15
  • 链接: https://blog.xx-xx.top/2023/02/24/javasSript小数相加精度问题/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。