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教程。