PythonDecimal教程展示了如何在Python中使用Decimal执行高精度计算。
Python小数
Pythondecimal模块支持快速正确舍入的decimal浮点运算。
默认情况下,Python将任何包含小数点的数字解释为双精度浮点数。Decimal是一种浮点数类型,比浮点数精度更高,范围更小。它适用于财务和货币计算。它也更接近于人类处理数字的方式。
与基于硬件的二进制浮点数不同,decimal模块具有用户可更改的精度,对于给定问题,该精度可以根据需要设置。默认精度为28位。
某些值无法用浮点数据类型准确表示。例如,将0.1值存储在float(二进制浮点值)变量中,我们只能得到该值的近似值。同样,1/3的值也不能用十进制浮点型精确表示。
这两种类型都不是完美的;一般来说,decimal类型更适用于金融和货币计算,而double/float类型更适用于科学计算。
Python十进制默认精度
Decimal的默认精度为28位,而float有18位。
#!/usr/bin/python
from decimal import Decimal
x = 1 / 3
print(type(x))
print(x)
print("-----------------------")
y = Decimal(1) / Decimal(3)
print(type(y))
print(y)
该示例比较了Python中两种浮点类型的精度。
$ ./defprec.py <class 'float'> 0.3333333333333333 ----------------------- <class 'decimal.Decimal'> 0.3333333333333333333333333333
Python比较浮点值
比较浮点值时应谨慎行事。虽然在许多现实世界的问题中,一个小错误可以忽略不计,但金融和货币计算必须准确。
#!/usr/bin/python
from decimal import Decimal
x = 0.1 + 0.1 + 0.1
print(x == 0.3)
print(x)
print("----------------------")
x = Decimal('0.1') + Decimal('0.1') + Decimal('0.1')
print(x == Decimal('0.3'))
print(float(x) == 0.3)
print(x)
该示例执行浮点值与内置float和Decimal类型的比较。
$ ./comparing.py False 0.30000000000000004 ---------------------- True True 0.3
由于float类型中的一个小错误,0.1+0.1+0.1==0.3产生False。使用Decimal类型,我们得到了预期的输出。
PythonDecimal改变精度
可以更改Decimal类型的默认精度。在下面的示例中,我们还使用了mpmath模块,它是一个用于任意精度浮点的库-点算术。
$ pip install mpmath
我们需要先安装mpmath。
#!/usr/bin/python
from decimal import Decimal, getcontext
import math
import mpmath
getcontext().prec = 50
mpmath.mp.dps = 50
num = Decimal(1) / Decimal(7)
num2 = mpmath.mpf(1) / mpmath.mpf(7)
print(" math.sqrt: {0}".format(Decimal(math.sqrt(num))))
print("decimal.sqrt: {0}".format(num.sqrt()))
print(" mpmath.sqrt: {0}".format(mpmath.sqrt(num2)))
print('actual value: 0.3779644730092272272145165362341800608157513118689214')
在示例中,我们将精度更改为50位。我们比较了math.sqrt、Decimal的sqrt和mpmath.sqrt函数的准确性。
$ alter_precision.py
math.sqrt: 0.37796447300922719758631274089566431939601898193359375
decimal.sqrt: 0.37796447300922722721451653623418006081575131186892
mpmath.sqrt: 0.37796447300922722721451653623418006081575131186892
actual value: 0.3779644730092272272145165362341800608157513118689214
Python十进制舍入
Decimal类型提供了几个舍入选项:
- ROUND_CEILING-总是向上舍入到无穷大
- ROUND_DOWN-总是向零舍入
- ROUND_FLOOR-总是向下舍入到负无穷大
- ROUND_HALF_DOWN-如果最后一个有效数字大于或等于5,则从零舍入,否则向零舍入
- ROUND_HALF_EVEN-与ROUND_HALF_DOWN类似,只是如果值为5,则检查前面的数字;偶数会使结果向下舍入,奇数会使结果向上舍入。
- ROUND_HALF_UP-类似于ROUND_HALF_DOWN,除非最后一位有效数字为5,否则值将从零开始舍入
- ROUND_UP-从零舍入
- ROUND_05UP-如果最后一位数字是0或5,则从零舍入,否则朝零舍入
#!/usr/bin/python
import decimal
context = decimal.getcontext()
rounding_modes = [
'ROUND_CEILING',
'ROUND_DOWN',
'ROUND_FLOOR',
'ROUND_HALF_DOWN',
'ROUND_HALF_EVEN',
'ROUND_HALF_UP',
'ROUND_UP',
'ROUND_05UP',
]
col_lines = '-' * 10
print(f"{' ':20} {'1/7 (1)':^10} {'1/7 (2)':^10} {'1/7 (3)':^10} {'1/7 (4)':^10}")
print(f"{' ':20} {col_lines:^10} {col_lines:^10} {col_lines:^10} {col_lines:^10}")
for mode in rounding_modes:
print(f'{mode:20}', end=' ')
for precision in [1, 2, 3, 4]:
context.prec = precision
context.rounding = getattr(decimal, mode)
value = decimal.Decimal(1) / decimal.Decimal(7)
print(f'{value:<10}', end=' ')
print()
print('********************************************************************')
print(f"{' ':20} {'-1/7 (1)':^10} {'-1/7 (2)':^10} {'-1/7 (3)':^10} {'-1/7 (4)':^10}")
print(f"{' ':20} {col_lines:^10} {col_lines:^10} {col_lines:^10} {col_lines:^10}")
for mode in rounding_modes:
print(f'{mode:20}', end=' ')
for precision in [1, 2, 3, 4]:
context.prec = precision
context.rounding = getattr(decimal, mode)
value = decimal.Decimal(-1) / decimal.Decimal(7)
print(f'{value:<10}', end=' ')
print()
该示例显示了1/7和-1/7表达式的可用舍入选项。
$ ./rounding.py
1/7 (1) 1/7 (2) 1/7 (3) 1/7 (4)
---------- ---------- ---------- ----------
ROUND_CEILING 0.2 0.15 0.143 0.1429
ROUND_DOWN 0.1 0.14 0.142 0.1428
ROUND_FLOOR 0.1 0.14 0.142 0.1428
ROUND_HALF_DOWN 0.1 0.14 0.143 0.1429
ROUND_HALF_EVEN 0.1 0.14 0.143 0.1429
ROUND_HALF_UP 0.1 0.14 0.143 0.1429
ROUND_UP 0.2 0.15 0.143 0.1429
ROUND_05UP 0.1 0.14 0.142 0.1428
********************************************************************
-1/7 (1) -1/7 (2) -1/7 (3) -1/7 (4)
---------- ---------- ---------- ----------
ROUND_CEILING -0.1 -0.14 -0.142 -0.1428
ROUND_DOWN -0.1 -0.14 -0.142 -0.1428
ROUND_FLOOR -0.2 -0.15 -0.143 -0.1429
ROUND_HALF_DOWN -0.1 -0.14 -0.143 -0.1429
ROUND_HALF_EVEN -0.1 -0.14 -0.143 -0.1429
ROUND_HALF_UP -0.1 -0.14 -0.143 -0.1429
ROUND_UP -0.2 -0.15 -0.143 -0.1429
ROUND_05UP -0.1 -0.14 -0.142 -0.1428
Python分数
我们可以使用Fraction处理有理数。
#!/usr/bin/python
from decimal import Decimal
from fractions import Fraction
x = Decimal(1) / Decimal(3)
y = x * Decimal(3)
print(y == Decimal(1))
print(x)
print(y)
print("-----------------------")
u = Fraction(1) / Fraction(3)
v = u * Fraction(3)
print(v == 1)
print(u)
print(v)
Decimal不能精确表示1/3表达式。在某些情况下,我们可以使用Fraction类型来获得准确的结果。
$ ./fract.py False 0.3333333333333333333333333333 0.9999999999999999999999999999 ----------------------- True 1/3 1
PythonSymPy理性
也可以使用SymPy的Rational类型来处理有理数。
$ pip install sympy
我们安装了sympy模块。
#!/usr/bin/python from sympy import Rational r1 = Rational(1/10) r2 = Rational(1/10) r3 = Rational(1/10) val = (r1 + r2 + r3) * 3 print(val.evalf()) val2 = (1/10 + 1/10 + 1/10) * 3 print(val2)
在示例中,我们比较了Rational和内置float的精度(1/10+1/10+1/10)*3表达式。
$ ./symbolic.py 0.900000000000000 0.9000000000000001
float类型有一个小错误。
在本教程中,我们使用了PythonDecimal类型。
列出所有Python教程。
