查看原文
其他

注意!这是所有编程语言共同的坑

脚本之家 2022-09-23

The following article is from 麦叔编程 Author 小K

 关注脚本之家”,与百万开发者在一起

出品 | 麦叔编程 (ID:maishucode)

已获得原公众号的授权转载

我们的电脑也叫计算机,但是这个计算机在计算小数(浮点数)运算的时候可能存在一个重大的Bug。

我们计算下1.2-0.1,这个算式我们口算都能算出结果1.1

但是我用Python算了下,结果不是1.1而是1.0999999999999999

如果不相信是这个结果,你可以自己尝试下下方的代码:

a = 1.2
b = 0.1

print(a-b)

有些小伙伴肯定会说,差这么一点点有啥关系嘛,无伤大雅的。

但是如果,刚好有个判断条件:

a = 1.2
b = 0.1
c = a - b

if c >= 1.1:
    print(True)
else:
    print(False)

输出结果:

False

从代码层面上看,毫无异议条件判断走的是True的分支,但是在debug的时候,却一直是False,这时身为开发的你,心态是否会爆炸呢?

造成的原因

人类在进行一些数值计算的时候,习惯性都会先转成十进制,而计算机则会转换成二进制再计算。

10除以3等于几?0.333333333...无限循环的小数对吧,如果让我写出具体的值估计十辈子都写不完后面的333333....

正因如此,0.1在转换成二进制的值时,对计算机来说这就是一个无限循环的数:

所以为了防止爆内存的事发生,计算机也像人类学习,四舍五入保留若干位小数。

计算机的保留逻辑是,舍0存1。

我所知的编程语言基本上都是这样的机制。

解决方法

如果都照这个算法,那资本家不就算亏了么?

我往银行存我银行里假如有1.8亿的存款,我再往里存0.1亿,那我什么都没做就有1.9000000000000001亿了。

把亿单位转成个位数,

1900000000.0000001元,

白嫖0.0000001元。

资本家怎么会让我有白嫖的机会呢?所以引入了decimal模块解决它:

import decimal

a1 = 1.8
b1 = 0.1
c1 = a1 + b1
print(c1)

a2 = decimal.Decimal("1.8")
b2 = decimal.Decimal("0.1")
c2 = a2+b2
print(c2)

输出结果:

1.9000000000000001
1.9

还有一种方法,就是把计算的结果以分数的形式展示出来。

以前上学的时候,同学问我,10除以3等于多少,我说3.333333...,然他说3.33333333... 乘以3等于几?我回答9.9999999999....

他就继续问我,为什么10/3 乘以 3 等于10?

所以我就想,有些情况是不是用分数表示一个值的时候会更加准确呢?

在产生无限循环小数的除法运算中使用fractions模块:

from fractions import Fraction

print(10/3)
print(Fraction(10,3))

输出结果:

3.3333333333333335
10/3

最后附上一个知乎上未解之谜

我把代码复写了下,供大家研究~

def add_digit(num):
    if num < 1:
        return 0
    else:
        n1 = num/10
        small_point = n1 - int(n1)
        # print(small_point)
        n2 = small_point*10
        print(n2)
        return int(n2) + add_digit(n1)

print(add_digit(12))

<END>

程序员专属卫衣

商品直购链接

👇👇

【☝🏼点击查看更多详情】

  推荐阅读:

专属定制,程序员秒懂的极客卫衣!

千万千万不要在方法上打断点!太坑了!

没有二十年功力,写不出这一行“看似无用”的代码!

99%的Java程序员会踩的6个坑

年轻人的手机,「不想换」还是「换不起」?

Windows11家庭版/专业版,正版终身授权!

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存