0.1乘以0.1等于多少?

2,488 阅读3分钟

提问

0.1乘以0.1等于多少?

这不等于0.01吗?你肯定会以为我在侮辱你们的智商。确实,这道题让我们人类来算,是等于0.01,但现在是计算机时代,我们让计算机算0.1乘以0.1,答案还是0.01吗?

探索

有些同学可能已经知道了答案,既然我们是开发,那我们就自己验证一下,答案到底是多少吧。非开发人员也可以跟着我一起探秘一下最终答案,看看计算机处理和我们人类处理到底有什么不一样的地方。

let num: Float = 0.1
print(num * num)//0.010000001

这个打印结果你们有预料到吗?为了以防万一,我们再试几个。

let a = Float(1.0) - Float(0.9)
print(a)//0.100000024
let b = Float.init(0.9) - Float.init(0.8)
print(b)//0.099999964

咦?难道是Float精度不够?那我们用Double试一下

let num: Double = 0.1
print(num * num)//0.010000000000000002

let a = Double(1.0) - Double(0.9)
print(a)//0.09999999999999998
let b = Double.init(0.9) - Double.init(0.8)
print(b)//0.09999999999999998

可以看到,虽然精度准确了几位,但结果还不是我们想要的。这个结果如果你用来计算,那你会死的很惨,当然如果这个是金融系统里面的金额计算,那最后导致的金额偏差,就真的要拿程序员祭天了。

下面我再来试一个数据类型Decimal,当然,有经验的程序员早就知道Decimal了。

let num: Decimal = 0.1
print(num * num)//0.01
let a = Decimal(1.0) - Decimal(0.9)
print(a)//0.1
let b = Decimal.init(0.9) - Decimal.init(0.8)
print(b)//0.1

小结

Decimal计算,终于回归我们希望的值了。那什么时候需要用Decimal,什么时候不需要用,这个就需要我们来判断了。一般来说,后端返回给我们的数据都给String,这样如果只是单纯显示的话,直接拿来显示就行。当涉及到计算的时候,我们最好转成Decimal,防止丢精度。

原理

下面,我们再来稍微深入探究一下,为什么计算机小数计算会丢精度呢?

首先,我们知道,计算机只认识0和1,不管我们高级语言写的多复杂,到最后无一例外都会转成一连串的0和1,那很简单,0.1是我们十进制数,如果我们转成二进制是多少呢?

不会计算的同学,可以去百度搜索怎么计算https://jingyan.baidu.com/article/425e69e6e93ca9be15fc1626.html,也可以去这个网站直接搜结果https://www.rapidtables.com/convert/number/decimal-to-binary.html

那我们不妨手动计算一下,看得出的结果最后跟搜出的答案一样不一样。

0.1*2=0.2
0.2*2=0.4
0.4*2=0.8

0.8*2=1.6
0.6*2=1.2
0.2*2=0.4
0.4*2=0.8

0.8*2=1.6
0.6*2=1.2
0.2*2=0.4
0.4*2=0.8

我们可以看到,算到后面出现了循环,那我们算到的值是0.00011001100。。。与搜出的答案一致,计算机不可能让你无限循环下去,它最后一位进1了,当然最后进不进1,谁也不知道,我们看0.2它就没有进1。当然这不重要,重要的是,我们知道了原理,要避开这些坑。

总结

简单来说,十进制的小数转换成二进制的小数的时候,会出现无法用有限位的二进制小数表示的情况。类似于十进制中的循环小数,比如1/3=0.333333这种。所以,一旦涉及到小数计算,我们就要使用高精度的Decimal。