为什么 0.1+0.2 不等于 0.3 ; 14.2+80.9
2021-10-18 本文已影响0人
lazy_tomato
START
- 番茄又来写点啥了。
- 最近遇到一个很有趣的bug,在这里记录一下。
业务场景
- 有两个金额A,B,我都做了保留两位小数的处理
- 还有一个金额C,等于金额A的金额B的和
- 突然测试给我讲,番茄,你这金额C没有保留两位小数哦。
- 查看了相关的bug,得出来的金额C展示的是
95.10000000000001
到这里我第一反应,确实金额C没有做保留两位小数的处理
但是在我排查代码逻辑的时候,突然想到,明明金额A和金额B我都做了保留两位小数的处理呀。
随即,在浏览器中打印了一下,发现了一个有趣的事情。
486135a7e3036aa7ffb072be437eded.png
14.2+80.9 = 95.10000000000001
问题分析
看到浏览器这里打印,14.2+80.9 = 95.10000000000001
。我就想到我们熟知的0.1+0.2 = 0.30000000000000004
JS红宝书相关说明呢:
-
浮点数值的精度最高是17位小数,但是在算数计算式其进度远不如整数。
-
关于浮点数值计算会产生误差的问题,有一点需要明确。这是基于
IEEE754
数值的浮点计算的通病。JS;JAVA;PYTHON 都会出现:
0.1+0.2 = 0.30000000000000004
根本原因是什么呢:
首先,我们要知道,计算机存储数据呢,是以二进制0/1
存储的,所以我们把0.1
和0.2
(十进制的)转换为二进制的:
0.1 => 0.0001 1001 1001 1001…(1001无限循环)
0.2 => 0.0011 0011 0011 0011…(0011无限循环)
但是我们计算机的硬件存储的位数是有限制的不可能无限循环下去,一般双精度浮点数总共占用64位,其中最多53位为有效精度数字(包括符号位),所以存储时只能存储:
0.1=>0.0001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001
0.2=>0.0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011
所以
0.1+0.2=>
0.0100 1100 1100 1100 1100 1100 11001 100 1100 1100 1100 1100 1100
转换为十进制就是:0.30000000000000004
解决方案
遇到这种情况怎么处理呢。
-
Math.round()
方法四舍五入或者toFixed()
方法保留指定的位数(对精度要求不高,可用这种方法) -
将小数转为整数再做计算。
2.1 这里可以配合
Math.pow(10, n)
放大10的n
次方;2.2 小数点的位数可以这样获取:
// num 我们需要计算的数字 // l 小数点的位数 try { l = num.toString().split('.')[1].length } catch (e) { l = 0 }
END
- 问题还是小问题,但是依旧很有意思,老生常谈。
- 记录当下,ღ( ´・ᴗ・` )