python理解浮点数运算的误差_PyTips 0x1a - Python 浮点数运算

本文探讨了Python中浮点数运算产生的误差,源于浮点数在计算机内的二进制表示方式。通过IEEE浮点数表示法,解释了0.1在二进制下无法精确表示,导致运算误差。此外,文章还介绍了如何通过`decimal`和`fractions`模块提高浮点数运算的精度,并提醒在高精度需求时考虑使用科学计算包。
摘要由CSDN通过智能技术生成

版权声明:此文章转载自 Yushneng PyHub

如需转载请联系听云College团队成员阮小乙,邮箱:ruanqy#tingyun.com

本系列完整文档已托管在 Read the Docs: http://tips.pyhub.cc

浮点数用来存储计算机中的小数,与现实世界中的十进制小数不同的是,浮点数通过二进制的形式来表示一个小数。在深入了解浮点数的实现之前,先来看几个 Python 浮点数计算有意思的例子:0.1 == 0.10000000000000000000001True0.1+0.1+0.1 == 0.3False

IEEE 浮点数表示法

这些看起来违反常识的“错误”并非 Python 的错,而是由浮点数的规则所决定的,即使放到其它语言中结果也是这样的。要理解计算机中浮点数的表示规则,先来看现实世界中十进制小数是如何表示的:1.234 = 1 + 1/10 + 2/100 + 3/1000

可以用下面的公式来表示:

8376fcb187a50a6d12065baeda1d6ffc.png

其中 d_i 是十进制中 0~9 的数字。而如果是一个二进制的小数:1.001 = 1 + 0/2 + 0/4 + 1/8

可以用下面的公式来表示:

1c9b652570022ab07c9196caf52a2655.png

其中 d_i 是二进制中的 0 或 1。Python 中的浮点数都是双精度的,也就说采用 64 位来表示一个小数,那这 64 位分别有多少用来表示整数部分和小数部分呢?根据 IEEE 标准,考虑到符号位,双精度表示法是这样分配的:

4944201a6d1fea4cfce048c3ed294c66.png

也就是说用1位表示符号位,11位表示整数部分,52位表示小数部分。正如十进制中我们无法精确表示某些分数(如10/3),浮点数中通过 d1/2 + d2/4 + ... 的方式也会出现这种情况,比如上面的例子中,十进制中简单的 0.1 就无法在二进制中精确描述,而只能通过近似表示法表示出来:(0.1).as_integer_ratio()(3602879701896397, 36028797018963968)

也就是说 0.1 是通过 3602879701896397/36028797018963968 来近似表示的,很明显这样近似的表示会导致许多差距很小的数字公用相同的近似表示数,例如:(0.10000000000000001).as_integer_ratio()(3602879701896397, 36028797018963968)

在 Python 中所有这些可以用相同的近似数表示的数字统一采用最短有效数字来表示:print(0.10000000000000001)0.1

浮点数运算

既然有些浮点数是通过近似值表示的,那么在计算过程中就很容易出现误差,就像最开始的第二个例子一样:a = .1 + .1 + .1b = .3print(a.as_integer_ratio())

print(b.as_integer_ratio())

print(a == b)(1351079888211149, 4503599627370496)

(5404319552844595, 18014398509481984)

False

为了解决运算中的问题,IEEE 标准还指定了一个舍入规则(round),即 Python 中内置的 round 方法,我们可以通过舍入的方式取得两个数的近似值,来判断其近似值是否相等:round(a, 10) == round(b, 10)True

当然这种舍入的方式并不一定是可靠的,依赖于舍入的选择的位数,位数太大,就失去了 round 的作用,太小,就会引入别的错误:print(round(a, 17) == round(b, 17))

print(round(0.1, 1) == round(0.111, 1))False

True

Python 中使用更精确的浮点数可以通过 decimal 和 fractions 两个模块,从名字上也能猜到,decimal 表示完整的小数,而 fractions 通过分数的形式表示小数:from decimal import Decimal

a = Decimal(0.1)

b = Decimal(0.1000000000000001)

c = Decimal(0.10000000000000001)

print(a)

print(b)

print(c)

a == b == c0.1000000000000000055511151231257827021181583404541015625

0.10000000000000010269562977782697998918592929840087890625

0.1000000000000000055511151231257827021181583404541015625

Falsefrom fractions import Fraction

f1 = Fraction(1, 10) # 0.1print(float(f1))

f3 = Fraction(3, 10) # 0.3print(float(f3))

print(f1 + f1 + f1 == f3)0.1

0.3

True

总结

浮点数这些奇特的特性让我们不得不在使用的时候格外注意,尤其是当有一定的精度要求的情况下。如果真的是对精度要求较高且需要频繁使用浮点数,建议使用更专业的 SciPy 科学计算包。

想阅读更多技术文章,请访问听云技术博客,访问听云官方网站感受更多应用性能优化魔力。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值