Python如何实现精确运算

2021-09-14 14:27发布

在Python中进行运算时,怎么能实现精确运算,而不是显示浮点数

在Python中进行运算时,怎么能实现精确运算,而不是显示浮点数

5条回答
茄子酱
2楼 · 2021-09-15 09:12

需要对浮点数执行精确的计算操作,并且不希望有任何小误差的出现.

 

浮点数的一个普遍问题是它们并不能精确的表示十进制数。并且,即使是最简单的
数学运算也会产生小的误差,比如:

1
2
3
4
5
6
>>> a = 4.2
>>> b = 2.1
>>> a + b
6.300000000000001
>>> (a + b) == 6.3
False

 

”False“是由于底层CPU和IEEE标准通过自己的浮点单位去执行算术导致的。Python的浮点数据类型使用底层表示存储数据,所以无法无法避免这样的误差。

 

如果想更加精确(代价是性能损耗),可以使用decimal模块

1
2
3
4
5
6
7
8
9
>>> 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对象支持所有的常用数学运算。

 

真实世界中很少会要求精确到普通浮点数能提供的17 位精度,执行大量运算的时候速度很重要。所以使用不要随便用decimal.

总的来说, decimal 模块主要用在涉及到金融的领域。在这类程序中,哪怕是一点
小小的误差在计算过程中蔓延都是不允许的。因此, decimal 模块为解决这类问题提
供了方法。当Python 和数据库打交道的时候也通常会遇到Decimal 对象,并且,通
常也是在处理金融数据的时候。


香蕉牛油果酸奶
3楼 · 2021-09-15 09:15

在进行浮点数计算时它们无法精确表达出所有的十进制小数位。

1
2
3
4
5
a = 4.1
b = 5.329
print(a+b)
  
9.428999999999998

这些误差实际上是底层CPU的浮点运算单元和IEEE754浮点数算数标准的一种“特性”。python的浮点数类型保存的数据采用的是原始表示形式,因此使用float实例时就不能避免这样的误差。

我们可以使用decimal模块避免这种操作(如果不介意牺牲下性能):

1
2
3
4
5
6
from decimal import Decimal
a = Decimal('4.1')
b = Decimal('5.329')
print(a+b)
  
9.429

注意Decimal的参数必须是字符串,不能是浮点型,否则误差依旧存在。

decimal模块的主要功能是允许控制计算过程中的各个方面,包括数字的尾数和四舍五入。

1
2
3
4
5
6
7
8
9
10
11
12
13
from decimal import Decimal
from decimal import localcontext
a = Decimal(4.1)
b = Decimal(5.329)
print(a/b)
print('================')
with localcontext() as ctx:
  ctx.prec = 3
  print(a/b)
  
0.7693751172827922400071261708
================
0.769

getcontext也可以实现和localcontext一样的功能

1
2
3
4
5
6
7
from decimal import Decimal, getcontext
a = Decimal(4.1)
b = Decimal(5.329)
getcontext().prec = 3
print(a/b)
  
0.769

误差我们不能完全消除,我们只能尽力优化算法,使得误差尽可能小。在大数和小数相加时要格外注意。

1
2
3
4
5
6
7
8
9
10
nums = [3.21e+18, 1, -3.21e+18]
print(sum(nums))
print('=========================')
import math
res = math.fsum(nums)
print(res)
  
0.0
=========================
1.0


夏虫语冰
4楼 · 2021-09-15 10:49

可以使用decimal模块来设置计算的精度。举个例子。


1
2
3
4
5
6
7
>>> from decimal import *
>>> getcontext().prec = 6
>>> Decimal(1/ Decimal(7)
Decimal('0.142857')
>>> getcontext().prec = 28
>>> Decimal(1/ Decimal(7)
Decimal('0.1428571428571428571428571429')


aijingda
5楼 · 2021-09-15 13:45

这个,从设计来考虑,我们首先需要明白除法的机制。

a÷b=c.c1c2c3c4…d

1  d=0时,除法结束

d>0时,d×10÷b=c5+d1

商变为c.c1c2c3c4c5

然后重复1的过程。

所以

1 a整除b=c

a求余b=d

2  d=0时,结束。

3 d>0时

d×10整除b=c1

商为c.c1

d×10求余b=d1

然后重复2开始的步骤。

这样,商就是字符串,

每次求被除数÷除数的商和余数。

第一次的商是整数位,以后的商就是小数位,拼接字符串

余数判断是否是0,0结束,否则余数×10再重复

1
2
3
4
5
6
7
8
9
>>> 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
>>>