python布尔运算可以比较浮点数吗_Python3 基础之:令人困惑的浮点数运算

0、1 + 0.68 不等于 1.68

我们先来看一行简单的代码:

a = 1 + 0.68

b = 1.68

is_equal = (a == b)

print("{}=={}is{}".format(a, b, is_equal))

逻辑上,上面的代码的输出结果应该为:

1.68 == 1.68 is True

然而,实际上真正的输出结果是:

1.6800000000000002 == 1.68 is False

你是否在实际的开发过程中遇到类似的问题,并因为付出了大量的DEBUG时间,最终才发现这么一个违反直觉的现象。

我们可以信手就举几个违反直觉的、看似相等,实则不想等的例子:

0.1 + 0.1 + 0.1 == 0.3 # False

0.99 * 3 == 2.97 # False

事实上,这个问题是所有编程语言都会遇到的问题,它都不能算是一个BUG,而算是一个非常正常的现象。

那么问题来了:我们怎么才能比较两个浮点数是否想等呢?

我们将在本文的最后回答这个问题。

1、浮点数的精度问题

我们都知道,计算机是以二进制的形式保存数据的。无论是整数还是浮点数,都不例外。举个例子,十进制浮点数字0.125的二进制表达式为:

0.001

# 0.125 = 0*2 + 0/2 + 0/4 + 1/8

# 0. 0 0 1

如果你看不懂上面的转换,你应该先了解一下十进制浮点数与二进制浮点数之间的转换,再来阅读本文。本文是在假设你已经了解这个规则的基础上编写的。

我们知道,当我们用十进制小数表达1/3的时候,它是一个无穷循环的小数。

# 1/3 的十进制小数表达:

0.33333333333333333333333333...

我们无法穷举所有的3,所以没有办法通过穷举再准确地表达1/3,而只是通过尽量多的穷举,去更接近的表达(近似表达)。

同样的,计算在表达一个数值(二进制)的时候,也是通过只能通过穷举一定的位数,来达到表达一个浮点数的效果。当计算机表述一个64位的浮点数时,一般是用10位来表达指数,用54位来表达精度。所以,当一个小数的位数超过54的时候,计算机就无法精准的表达,而只能取其近似值。

举个例子,十进制数0.1的二进制表达式为:

0.0001100110011001100110011001100110011001100110011...

如果是64位的浮点数,那么计算机只能取小数点后54位的精度。

我们先来看一段代码:

a = 0.1

print(a)

print("{:.20f}".format(a))

其它结果为:

0.1

0.10000000000000000555

第一个打印的结果,看似正确,事实上只是一个表象;这是计算机为我们做了尽量接近原数值的推算和展示(这个超过本文的讨论范围,不深入讨论)。

当我们把精度取到20位的时候(第二行结果),我们就会出现计算机在存储0.1的这数值的时候,其实并不是准确的存储了0.1,而是存储了一个近似值。正如我们前面所提到的,用有限位的二进值小数,并没有办法精确的表达十进制小数0.1。

2、如何比较两个浮点数是否相等

很遗憾,我们的答案是:不能。

这是一个非常重要的计算机常识。

如果你尝试通过==来判定两个浮点数是否相等,你有可能就陷入了第一节所讨论的陷阱。所以,除非你非常明确自己的意图,否则你千万不要这么做。

但是判定两个浮点数是否相等,却是一个非常普通而常见的操作,我们应该怎么做呢?

答案是:取近似值。

当我们直接用==判定两个64位浮点数(注意我们这里提到了位数)是否相等时,事实上是对比两个浮点数在54位的精度上(与小数点后54不是同一个概念)的近似值是否相等。

同理,当你判定两个浮点数是否相等时,你应该取一个适当的精度。而这个精度应该根据你实际功能的需要决定。

回到我们第0节所提到的1.68的问题,当我们取小数点后15位以内时,这两个数值就可以判定为近似相等。

a = 1.68

b = 1 + 0.68

print("a={:.20}".format(a))

print("b={:.20}".format(b))

eps = 0.00001

if abs(a - b) < eps:

print("a == b")

上面代码的打印结果为:

a=1.6799999999999999378

b=1.6800000000000001599

a == b

浮点数计算一个很专业和复杂的问题,我这里只是抛砖引玉,简单地介绍,如果你感兴趣,可以专门去了解一下。这里有几个链接仅各位参考一下:https://docs.python.org/3/tutorial/floatingpoint.html​docs.python.orgBinary numbers – floating point conversion​blog.penjee.com

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值