Python中实现精确的浮点数运算详解
本文深入解析Python中浮点数缺乏精确性的根本原因,即实数无限精度与计算机有限内存的矛盾。通过对比标准浮点运算与Decimal模块,展示如何利用字符串表示数字来消除舍入误差,并提供控制精度和四舍五入的具体代码示例,适用于对计算精度有严格要求的场景。
为什么说浮点数缺乏精确性?
在深入探讨之前,有必要先厘清浮点数为何缺乏精确性。这并非Python独有的问题,而是源于实数具有无限精度而计算机内存有限的根本矛盾。
举例来说,假设我们只能使用整数(即精度仅保留到个位,计算机中的浮点数同样受限于有限精度,以C语言中的双精度浮点数double为例,其精度为52个二进制位)。当试图表示任意实数(无限精度)时,只能通过舍入(rounding)来进行近似表示。
例如,1.2会被表示为1,2.4表示为2,3.6表示为4。
那么结果如何?
在计算1.2 - 1.2时,由于计算机内部的表示问题,实际执行的运算是1 - 1,结果为0,这恰好碰巧正确;
在计算1.2 + 1.2 - 2.4时,由于同样的表示问题,实际执行的运算是1 + 1 - 2,结果仍为0,再次碰巧正确;
然而,在计算1.2 + 1.2 + 1.2 - 3.6时,由于表示问题,实际执行的运算是1 + 1 + 1 - 4,结果为-1,这次运气就不太好了!
这里的1.2、2.4、3.6相当于问题中提到的0.1、0.2和0.3,而1、2、4则是计算机内部真正参与运算的数值。这样解释是否清楚?
其他相关信息可参考IEEE 754浮点数标准,例如CSAPP第二章的内容(虽然可能读者对此兴趣不大)。
另外:不仅浮点数在计算机内部的表示存在误差,运算过程本身也可能产生误差。例如,整数2可以在计算机内准确表示,但计算根号2时就会产生误差;再如,两个浮点数相除,虽然被除数和除数都是精确表示的,但除法结果的精度可能超出计算机内实数的表示范围,从而引入误差。
好了,下面进入正文:
起步
浮点数的一个普遍问题是它们无法精确表示十进制数。
>>> a = 4.2 >>> b = 2.1 >>> a + b 6.300000000000001 >>> (a + b) == 6.3 False >>>
这是由于底层CPU和IEEE 754标准通过其浮点单元执行算术时的特性所致。看似有限的小数,在计算机的二进制表示中却是无限的。
通常情况下,这种微小的误差是可以接受的。如果不能容忍这种误差(例如在金融领域),则需要考虑采用其他解决方案。
Decimal
使用此模块可避免任何微小误差。
>>> from decimal import Decimal
>>> a = Decimal('4.2')
>>> b = Decimal('2.1')
>>> a + b
Decimal('6.3')
>>> print(a + b)
6.3
>>> (a + b) == Decimal('6.3')
True
尽管代码看起来有些奇特,使用字符串来表示数字,但Decimal支持所有常用的数学运算。decimal模块允许你控制计算的各个方面,包括数字位数和四舍五入。在这样做之前,需要创建一个临时上下文环境来更改这些设置:
>>> from decimal import Decimal, localcontext
>>> a = Decimal('1.3')
>>> b = Decimal('1.7')
>>> print(a / b)
0.7647058823529411764705882353
>>> with localcontext() as ctx:
... ctx.prec = 3
... print(a / b)
...
0.765
>>> with localcontext() as ctx:
... ctx.prec = 50
... print(a / b)
...
0.76470588235294117647058823529411764705882352941176
>>>
由于Decimal的高精度数字自然也就用字符串来做展示和中转。
总结
总的来说,当涉及金融领域时,哪怕是一点小小的误差在计算过程中都是不允许的。因此decimal模块为解决这类问题提供了方法。
好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对本站的支持。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













