You are here:  Home » Python » decimal – 十进制定点和浮点运算详解 (4) Python语言的数学和数学模块(必读进阶学习教程)(参考资料)

decimal模块支持快速正确舍入的十进制浮点运算。与数据类型相比,它具有几个优点 float

  • Decimal“基于浮点模型,该模型的设计以人为本,并且必然具有最重要的指导原则——计算机必须提供一种与人们在学校学习的算术相同的算术。” – 摘自十进制算术规范。
  • 十进制数可以精确表示。相反,像 1.1和之类的数字2.2在二进制浮点数中没有精确的表示。最终用户通常不希望像二进制浮点数那样显示。1.1 + 2.23.3000000000000003
  • 精确性延续到算术中。在十进制浮点数中,正好等于零。在二进制浮点数中,结果是. 虽然接近于零,但差异会阻止可靠的相等性测试,并且差异会累积。因此,在具有严格相等不变量的会计应用程序中,十进制是首选。0.1 + 0.1 + 0.1 - 0.35.5511151231257827e-017
  • decimal 模块包含重要位置的概念,因此是. 保留尾随零以指示重要性。这是货币应用程序的习惯表示。对于乘法,“教科书”方法使用被乘数中的所有数字。例如,给出而给出。1.30 + 1.202.501.3 * 1.21.561.30 * 1.201.5600
  • 与基于硬件的二进制浮点数不同,decimal 模块具有用户可更改的精度(默认为 28 位),对于给定问题,它可以根据需要设置为最大:
>>>from decimal import *
>>>getcontext().prec = 6
>>>Decimal(1) / Decimal(7)
Decimal('0.142857')
>>>getcontext().prec = 28
>>>Decimal(1) / Decimal(7)
Decimal('0.1428571428571428571428571429')
  • 二进制和十进制浮点数都是根据已发布的标准实现的。虽然内置 float 类型只公开了其功能的一小部分,但 decimal 模块公开了标准的所有必需部分。需要时,程序员可以完全控制舍入和信号处理。这包括通过使用异常来阻止任何不精确的操作来强制执行精确算术的选项。
  • decimal 模块旨在支持“不带偏见地支持精确的非四舍五入的十进制算术(有时称为定点算术)和四舍五入的浮点算术。” – 摘自十进制算术规范。

模块设计以三个概念为中心:十进制数、算术上下文和信号。

十进制数是不可变的。它有一个符号、系数数字和一个指数。为了保持显着性,系数数字不会截断尾随零。小数还包括特殊值,例如 Infinity-InfinityNaN。该标准还-0区别于+0.

算术的上下文是指定精度、舍入规则、指数限制、指示操作结果的标志以及确定信号是否被视为异常的陷阱启动器的环境。舍入选项包括ROUND_CEILINGROUND_DOWN、 ROUND_FLOORROUND_HALF_DOWNROUND_HALF_EVEN、 ROUND_HALF_UPROUND_UPROUND_05UP

信号是在计算过程中出现的一组异常情况。根据应用程序的需要,信号可能会被忽略、被视为信息或被视为异常。十进制模块中的信号有:ClampedInvalidOperationDivisionByZeroInexactRoundedSubnormalOverflow,UnderflowFloatOperation

对于每个信号,都有一个标志和一个陷阱启动器。当遇到信号时,其标志设置为 1,然后,如果陷阱启动器设置为 1,则会引发异常。标志是粘性的,因此用户需要在监控计算之前重置它们。

快速入门教程

使用小数的通常开始是导入模块,查看当前上下文,getcontext()并在必要时为精度、舍入或启用的陷阱设置新值:

>>> from decimal import *
>>> getcontext()
Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999,
        capitals=1, clamp=0, flags=[], traps=[Overflow, DivisionByZero,
        InvalidOperation])

>>> getcontext().prec = 7       # Set a new precision

Decimal 实例可以从整数、字符串、浮点数或元组构造。从整数或浮点数构造执行该整数或浮点数的值的精确转换。十进制数包括特殊值,例如 NaN代表“不是数字”、正数和负数 Infinity,以及-0

>>> getcontext().prec = 28
>>> Decimal(10)
Decimal('10')
>>> Decimal('3.14')
Decimal('3.14')
>>> Decimal(3.14)
Decimal('3.140000000000000124344978758017532527446746826171875')
>>> Decimal((0, (3, 1, 4), -2))
Decimal('3.14')
>>> Decimal(str(2.0 ** 0.5))
Decimal('1.4142135623730951')
>>> Decimal(2) ** Decimal('0.5')
Decimal('1.414213562373095048801688724')
>>> Decimal('NaN')
Decimal('NaN')
>>> Decimal('-Infinity')
Decimal('-Infinity')

如果FloatOperation信号被捕获,构造函数中小数和浮点数的意外混合或排序比较会引发异常:

>>> c = getcontext()
>>> c.traps[FloatOperation] = True
>>> Decimal(3.14)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
decimal.FloatOperation: [<class 'decimal.FloatOperation'>]
>>> Decimal('3.5') < 3.7
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
decimal.FloatOperation: [<class 'decimal.FloatOperation'>]
>>> Decimal('3.5') == 3.5
True

3.3 版中的新功能。

新 Decimal 的重要性仅由输入的位数决定。上下文精度和舍入仅在算术运算期间起作用。

>>> getcontext().prec = 6
>>> Decimal('3.0')
Decimal('3.0')
>>> Decimal('3.1415926535')
Decimal('3.1415926535')
>>> Decimal('3.1415926535') + Decimal('2.7182818285')
Decimal('5.85987')
>>> getcontext().rounding = ROUND_UP
>>> Decimal('3.1415926535') + Decimal('2.7182818285')
Decimal('5.85988')

如果超出了 C 版本的内部限制,则构造小数加注InvalidOperation

>>> Decimal("1e9999999999999999999")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
decimal.InvalidOperation: [<class 'decimal.InvalidOperation'>]

在 3.3 版中更改。

小数与 Python 的大部分其他部分交互良好。这是一个小十进制浮点飞行马戏团:

>>> data = list(map(Decimal, '1.34 1.87 3.45 2.35 1.00 0.03 9.25'.split()))
>>> max(data)
Decimal('9.25')
>>> min(data)
Decimal('0.03')
>>> sorted(data)
[Decimal('0.03'), Decimal('1.00'), Decimal('1.34'), Decimal('1.87'),
 Decimal('2.35'), Decimal('3.45'), Decimal('9.25')]
>>> sum(data)
Decimal('19.29')
>>> a,b,c = data[:3]
>>> str(a)
'1.34'
>>> float(a)
1.34
>>> round(a, 1)
Decimal('1.3')
>>> int(a)
1
>>> a * 5
Decimal('6.70')
>>> a * b
Decimal('2.5058')
>>> c % a
Decimal('0.77')

并且一些数学函数也可用于 Decimal:

>>> getcontext().prec = 28
>>> Decimal(2).sqrt()
Decimal('1.414213562373095048801688724')
>>> Decimal(1).exp()
Decimal('2.718281828459045235360287471')
>>> Decimal('10').ln()
Decimal('2.302585092994045684017991455')
>>> Decimal('10').log10()
Decimal('1')

quantize()方法将数字四舍五入为固定指数。此方法对于经常将结果四舍五入到固定位数的货币应用程序很有用:

>>> Decimal('7.325').quantize(Decimal('.01'), rounding=ROUND_DOWN)
Decimal('7.32')
>>> Decimal('7.325').quantize(Decimal('1.'), rounding=ROUND_UP)
Decimal('8')

如上所示,该getcontext()函数访问当前上下文并允许更改设置。这种方法可以满足大多数应用程序的需要。

对于更高级的工作,使用 Context() 构造函数创建备用上下文可能很有用。要激活备用,请使用该setcontext() 功能。

根据标准,该decimal模块提供了两个随时可用的标准上下文,BasicContext以及ExtendedContext. 前者对于调试特别有用,因为启用了许多陷阱:

>>> myothercontext = Context(prec=60, rounding=ROUND_HALF_DOWN)
>>> setcontext(myothercontext)
>>> Decimal(1) / Decimal(7)
Decimal('0.142857142857142857142857142857142857142857142857142857142857')

>>> ExtendedContext
Context(prec=9, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999,
        capitals=1, clamp=0, flags=[], traps=[])
>>> setcontext(ExtendedContext)
>>> Decimal(1) / Decimal(7)
Decimal('0.142857143')
>>> Decimal(42) / Decimal(0)
Decimal('Infinity')

>>> setcontext(BasicContext)
>>> Decimal(42) / Decimal(0)
Traceback (most recent call last):
  File "<pyshell#143>", line 1, in -toplevel-
    Decimal(42) / Decimal(0)
DivisionByZero: x / 0

上下文也有信号标志,用于监视计算过程中遇到的异常情况。标志在明确清除之前保持设置状态,因此最好在每组受监视的计算之前使用该clear_flags()方法清除标志。

>>> setcontext(ExtendedContext)
>>> getcontext().clear_flags()
>>> Decimal(355) / Decimal(113)
Decimal('3.14159292')
>>> getcontext()
Context(prec=9, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999,
        capitals=1, clamp=0, flags=[Inexact, Rounded], traps=[])

lags条目显示 的有理近似值被Pi四舍五入(超出上下文精度的数字被丢弃)并且结果不准确(一些丢弃的数字是非零的)。

traps使用上下文字段中的字典设置各个陷阱:

>>> setcontext(ExtendedContext)
>>> Decimal(1) / Decimal(0)
Decimal('Infinity')
>>> getcontext().traps[DivisionByZero] = 1
>>> Decimal(1) / Decimal(0)
Traceback (most recent call last):
  File "<pyshell#112>", line 1, in -toplevel-
    Decimal(1) / Decimal(0)
DivisionByZero: x / 0
大多数程序只在程序开始时调整一次当前上下文。而且,在许多应用程序中,数据Decimal通过循环内的单个转换转换为。通过设置上下文和创建小数,程序的大部分操作数据与使用其他 Python 数字类型没有什么不同。

十进制对象

decimal.Decimal值=“0”上下文=无
Decimal根据value构造一个新对象。

可以是整数、字符串、元组float、 或其他Decimal 对象。如果没有给出,则返回Decimal('0')。如果value是一个字符串,在删除前导和尾随空白字符以及整个下划线后,它应该符合十进制数字字符串语法:

sign           ::=  '+' | '-'
digit          ::=  '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
indicator      ::=  'e' | 'E'
digits         ::=  digit [digit]...
decimal-part   ::=  digits '.' [digits] | ['.'] digits
exponent-part  ::=  indicator [sign] digits
infinity       ::=  'Infinity' | 'Inf'
nan            ::=  'NaN' [digits] | 'sNaN' [digits]
numeric-value  ::=  decimal-part [exponent-part] | infinity
numeric-string ::=  [sign] numeric-value | [sign] nan

上面出现的其他 Unicode 十进制数字也是允许digit 的。这些包括来自各种其他字母表的十进制数字(例如,Arabic-Indic 和 Devanāgarī 数字)以及全角数字'\uff10''\uff19'.

如果value是 a tuple,它应该有三个组成部分,一个符号(0正号或1负号),一个tuple数字和一个整数指数。例如, 返回.Decimal((0, (1, 4, 1, 4), -3))Decimal('1.414')

如果value是 a float,则二进制浮点值将无损地转换为其精确的十进制等效值。这种转换通常需要 53 位或更多位的精度。例如,Decimal(float('1.1')) 转换为 Decimal('1.100000000000000088817841970012523233890533447265625').

上下文精度不影响存储的位数。这完全由value中的位数决定。例如, Decimal('3.00000')即使上下文精度仅为三,也记录所有五个零。

context参数的目的是确定如果value是格式错误的字符串该怎么做。如果上下文陷入困境InvalidOperation,则会引发异常;否则,构造函数返回一个值为 的新 Decimal NaN

一旦构建,Decimal对象就是不可变的。

在 3.2 版更改:现在允许构造函数的参数是一个float 实例。

在 3.3 版更改:如果 设置了陷阱,则float参数会引发异常。FloatOperation默认情况下,陷阱处于关闭状态。

在 3.6 版更改:允许使用下划线进行分组,就像代码中的整数和浮点文字一样。

十进制浮点对象与其他内置数字类型(例如float和)共享许多属性int。所有常用的数学运算和特殊方法都适用。同样,小数对象可以被复制、腌制、打印、用作字典键、用作集合元素、比较、排序和强制转换为另一种类型(例如float或 int)。

Decimal 对象的算术与整数和浮点数的算术之间存在一些细微差别。当余数运算符%应用于 Decimal 对象时,结果的符号是 被除数的符号而不是除数的符号:

>>> (-7) % 4
1
>>> Decimal(-7) % Decimal(4)
Decimal('-3')

整数除法运算符的//行为类似,返回真商的整数部分(截断为零)而不是它的底数,以保持通常的身份:x == (x // y) * y + x % y

>>> -7 // 4
-2
>>> Decimal(-7) // Decimal(4)
Decimal('-1')

%and运算符(分别)按照规范中的描述实现//andremainder操作 。divide-integer

Decimal 对象通常不能与fractions.Fraction算术运算中的浮点数或实例组合:例如,尝试将 a 添加Decimal到 afloat将引发 a TypeError。但是,可以使用 Python 的比较运算符将一个Decimal 实例x与另一个数字进行比较y。这避免了在不同类型的数字之间进行相等比较时混淆结果。

在 3.2 版更改:Decimal现在完全支持实例和其他数字类型之间的混合类型比较。

除了标准的数字属性,十进制浮点对象还有一些专门的方法:

adjusted
在移出系数最右边的数字直到只剩下前导数字后返回调整后的指数: Decimal('321e+5').adjusted()返回 7。用于确定最高有效位相对于小数点的位置。
as_integer_ratio
返回一对整数,将给定 实例表示为分数,以最小项表示并具有正分母:(n, d)Decimal

>>> Decimal('-3.14').as_integer_ratio()
(-157, 50)

转换是准确的。引发无穷大的 OverflowError 和 NaN 的 ValueError。

3.6 版中的新功能。

as_tuple
返回数字的命名元组表示形式: 。DecimalTuple(sign, digits, exponent)
canonical
返回参数的规范编码。目前,Decimal实例的编码始终是规范的,因此此操作返回其参数不变。
compare(othercontext=None)
比较两个 Decimal 实例的值。 compare()返回一个 Decimal 实例,如果任一操作数为 NaN,则结果为 NaN:

a or b is a NaN  ==> Decimal('NaN')
a < b            ==> Decimal('-1')
a == b           ==> Decimal('0')
a > b            ==> Decimal('1')
compare_signal(othercontext=None)
此操作与compare()方法相同,除了所有 NaN 信号。也就是说,如果两个操作数都不是信号 NaN,则任何安静的 NaN 操作数都被视为信号 NaN。
compare_total(othercontext=None)
使用它们的抽象表示而不是它们的数值来比较两个操作数。 类似于 compare() 方法,但结果给出了 Decimal 实例的总排序。 具有相同数值但不同表示的两个 Decimal 实例在此排序中比较不相等:

>>> Decimal('12.0').compare_total(Decimal('12'))
Decimal('-1')

安静和信号 NaN 也包含在总排序中。 如果两个操作数具有相同的表示,则此函数的结果为 Decimal(‘0’),如果第一个操作数在总顺序上低于第二个,则为 Decimal(‘-1’),如果 第一个操作数在总顺序上高于第二个操作数。 有关总订单的详细信息,请参阅规范。

此操作不受上下文影响并且是安静的:没有更改标志,也没有执行舍入。 作为例外,如果无法准确转换第二个操作数,C 版本可能会引发 InvalidOperation

compare_total_mag(othercontext=None)

使用它们的抽象表示而不是它们的值来比较两个操作数compare_total(),但忽略每个操作数的符号。 x.compare_total_mag(y)相当于 x.copy_abs().compare_total(y.copy_abs()).

此操作不受上下文影响并且是安静的:没有更改标志,也没有执行舍入。作为例外,如果无法准确转换第二个操作数,C 版本可能会引发 InvalidOperation。

conjugate
Just returns self,此方法仅符合Decimal Specification。
copy_abs
返回参数的绝对值。此操作不受上下文影响并且是安静的:没有更改标志,也没有执行舍入。
copy_negate
返回参数的否定。此操作不受上下文影响并且是安静的:没有更改标志,也没有执行舍入。
copy_sign(othercontext=None)
返回第一个操作数的副本,其符号设置为与第二个操作数的符号相同。例如:

>>> Decimal('2.3').copy_sign(Decimal('-1.5'))
Decimal('-2.3')

此操作不受上下文影响并且是安静的:没有更改标志,也没有执行舍入。作为例外,如果无法准确转换第二个操作数,C 版本可能会引发 InvalidOperation

exp(context=None)

返回给定数字处的(自然)指数函数e**x的值。使用 ROUND_HALF_EVEN舍入模式正确舍入结果。

>>> Decimal(1).exp()
Decimal('2.718281828459045235360287471')
>>> Decimal(321).exp()
Decimal('2.561702493119680037517373933E+139')

from_float(f)

将浮点数准确地转换为十进制数的类方法。

注意Decimal.from_float(0.1)Decimal(‘0.1’)不同。由于 0.1 不能用二进制浮点数精确表示,因此该值存储为最接近的可表示值,即 0x1.999999999999ap-4。十进制的等效值是 0.1000000000000000055511151231257827021181583404541015625

>>> Decimal.from_float(0.1)
Decimal('0.1000000000000000055511151231257827021181583404541015625')
>>> Decimal.from_float(float('nan'))
Decimal('NaN')
>>> Decimal.from_float(float('inf'))
Decimal('Infinity')
>>> Decimal.from_float(float('-inf'))
Decimal('-Infinity')

fma(otherthirdcontext=None)

融合乘加。返回 self*other+third 中间产品 self*other 没有四舍五入。

>>> Decimal(2).fma(3, 5)
Decimal('11')
is_canonical
如果参数是规范的则返回True, 否则返回False。目前,一个Decimal实例总是规范的,所以这个操作总是返回True
is_finite
如果参数是有限数,则返回 True;如果参数是无穷大或 NaN,则返回 False。
is_infinite
如果参数是正无穷大或负无穷大,则返回 True,否则返回 False。
is_nan
如果参数是(安静的或发信号的)NaN,则返回 True,否则返回 False。
is_normal(context=None)
如果参数是正常的有限数,则返回 True。 如果参数为零、次正规、无限或 NaN,则返回 False。
is_qnan
如果参数是一个安静的 NaN,则返回 True,否则返回 False。
is_signed
如果参数有负号则返回 True,否则返回 False。 请注意,零和 NaN 都可以带符号。
is_snan
如果参数是信号 NaN 则返回 True,否则返回 False。
is_subnormal(context=None)
如果参数是次正规的,则返回 True,否则返回 False。
is_zero
如果参数是(正或负)零,则返回 True,否则返回 False。
ln(context=None)
返回操作数的自然(以 e 为底)对数。 结果使用 ROUND_HALF_EVEN 舍入模式正确舍入。
log10(context=None)
返回操作数的以十为底的对数。使用ROUND_HALF_EVEN舍入模式正确舍入结果。
logb(context=None)
对于非零数,将其操作数的调整指数作为 Decimal 实例返回。 如果操作数为零,则返回 Decimal(‘-Infinity’) 并引发 DivisionByZero 标志。 如果操作数是无穷大,则返回 Decimal(‘Infinity’)。
logical_and(othercontext=None)
logical_and() 是一个逻辑运算,需要两个逻辑操作数(请参阅逻辑操作数)。 结果是两个操作数的数字和。
logical_invert(context=None)
logical_invert()是逻辑运算。结果是操作数的数字反转。
logical_or(othercontext=None)
logical_or()是一个逻辑运算,需要两个逻辑操作数(请参阅逻辑操作数)。结果是or两个操作数的数字方式。
logical_xor(othercontext=None)
logical_xor()是一个逻辑运算,需要两个逻辑操作数(请参阅逻辑操作数)。结果是两个操作数的数字异或。
max(othercontext=None)
与 max(self, other) 一样,除了在返回之前应用上下文舍入规则并且 NaN 值被发出信号或被忽略(取决于上下文以及它们是发出信号还是静默)。
max_mag(othercontext=None)
max()方法类似,但比较是使用操作数的绝对值完成的。
min(othercontext=None)
与 min(self, other) 类似,除了在返回之前应用上下文舍入规则并且 NaN 值被发出信号或被忽略(取决于上下文以及它们是发出信号还是静默)。
min_mag(othercontext=None)
min()方法类似,但比较是使用操作数的绝对值完成的。
next_minus(context=None)
返回在给定上下文中(如果没有给出上下文,则在当前线程的上下文中)可表示的小于给定操作数的最大数字。
next_plus(context=None)
返回在给定上下文中(如果没有给出上下文,则在当前线程的上下文中)可表示的大于给定操作数的最小数字。
next_toward(othercontext=None)
如果两个操作数不相等,则返回在第二个操作数的方向上最接近第一个操作数的数。如果两个操作数在数值上相等,则返回第一个操作数的副本,其符号设置为与第二个操作数的符号相同。
normalize(context=None)
通过去除最右边的尾随零并将任何结果转换为等于来规范化Decimal('0')数字 Decimal('0e0')。用于为等价类的属性生成规范值。例如,Decimal('32.100')和 Decimal('0.321000e+2')都归一化为等效值 Decimal('32.1')
number_class(context=None)
返回描述操作数类的字符串。返回值为以下十个字符串之一。

  • "-Infinity",表示操作数为负无穷大。
  • "-Normal",表示操作数是负正规数。
  • "-Subnormal",表示操作数为负且次正规。
  • "-Zero",表示操作数为负零。
  • "+Zero",表示操作数为正零。
  • "+Subnormal",表示操作数为正且次正规。
  • "+Normal", 表示操作数是正正规数。
  • "+Infinity",表示操作数为正无穷大。
  • "NaN",表示操作数是一个安静的 NaN(非数字)。
  • "sNaN",表示操作数是一个信号 NaN。
quantizeexp , rounding=None , context=None 
在四舍五入后返回一个等于第一个操作数并具有第二个操作数的指数的值。
>>> Decimal('1.41421356').quantize(Decimal('1.000'))
Decimal('1.414')
与其他操作不同,如果量化操作后系数的长度大于精度,则 InvalidOperation发出信号。这保证了除非出现错误情况,否则量化指数始终等于右侧操作数的指数。

与其他操作不同的是,量化从不发出下溢信号,即使结果不正常且不准确。

如果第二个操作数的指数大于第一个操作数的指数,则可能需要进行舍入。在这种情况下,舍入模式由给定的rounding参数决定,否则由给定的 context参数决定;如果两个参数都没有给出,则使用当前线程上下文的舍入模式。

只要结果指数大于 Emax或小于 ,就会返回错误Etiny

radix
Return , 类在其中执行所有算术运算Decimal(10)的基数(基数) 。Decimal包括在内是为了与规范兼容。
remainder_near(othercontext=None)
将 self 除以 other 返回余数。 这与 self % other 的不同之处在于,选择余数的符号是为了最小化其绝对值。 更准确地说,返回值是 self – n * other,其中 n 是最接近 self / other 精确值的整数,如果两个整数同样接近,则选择偶数。

如果结果为零,则其符号将是自我的符号。

>>> Decimal(18).remainder_near(Decimal(10))
Decimal('-2')
>>> Decimal(25).remainder_near(Decimal(10))
Decimal('5')
>>> Decimal(35).remainder_near(Decimal(10))
Decimal('-5')
rotate(othercontext=None)
返回将第一个操作数的数字按第二个操作数指定的数量旋转的结果。第二个操作数必须是 -precision 到 precision 范围内的整数。第二个操作数的绝对值给出了要旋转的位数。如果第二个操作数是正数,则向左旋转;否则旋转是向右的。如有必要,第一个操作数的系数在左侧用零填充以达到长度精度。第一个操作数的符号和指数不变。
same_quantum(othercontext=None)
测试 self 和 other 是否具有相同的指数,或者两者是否都是 NaN

此操作不受上下文影响并且是安静的:没有更改标志,也没有执行舍入。作为例外,如果无法准确转换第二个操作数,C 版本可能会引发 InvalidOperation

scaleb(othercontext=None)
返回第一个操作数,指数由第二个调整。等价地,返回第一个操作数乘以10**other。第二个操作数必须是整数。
shift(othercontext=None)
返回将第一个操作数的数字移位第二个操作数指定的量的结果。第二个操作数必须是 -precision 到 precision 范围内的整数。第二个操作数的绝对值给出了要移动的位数。如果第二个操作数是正数,则向左移动;否则向右移动。移入系数的数字为零。第一个操作数的符号和指数不变。
sqrt(context=None)
将参数的平方根返回到全精度。
to_eng_string (context=None)
如果需要指数,则使用工程符号转换为字符串。

工程符号的指数是 3 的倍数。这可以在小数点左侧保留最多 3 位数字,并且可能需要添加一个或两个尾随零。

例如,这将转换Decimal('123E+1')Decimal('1.23E+3').

to_integral(rounding=Nonecontext=None)
to_integral_value()方法相同。to_integral 保留该名称是为了与旧版本兼容。
to_integral_exact(rounding=Nonecontext=None)
四舍五入到最接近的整数,发信号Inexact或 Rounded如果发生四舍五入则视情况而定。舍入模式由rounding给定的参数决定,否则由给定的 决定context。如果没有给出任何参数,则使用当前上下文的舍入模式。
to_integral_value(rounding=Nonecontext=None)
舍入到最接近的整数而不发出信号Inexact或 Rounded。如果给定,应用舍入否则,在提供的上下文或当前上下文中使用舍入方法。

逻辑操作数

logical_and()、logical_invert()、logical_or() 和 logical_xor() 方法期望它们的参数是逻辑操作数。 逻辑操作数是 Decimal 实例,其指数和符号均为零,并且其数字全部为 0 或 1。

上下文对象

上下文是算术运算的环境。它们控制精度,设置舍入规则,确定哪些信号被视为例外,并限制指数的范围。

每个线程都有自己的当前上下文,可以使用 getcontext()setcontext()函数访问或更改:

decimal.getcontext
返回活动线程的当前上下文。
decimal.setcontext(c)
将活动线程的当前上下文设置为c

您还可以使用with语句和localcontext() 函数来临时更改活动上下文。

decimal.localcontext(ctx=None)
返回一个上下文管理器,它将在进入 with 语句时将活动线程的当前上下文设置为ctx的副本,并在退出 with 语句时恢复先前的上下文。如果未指定上下文,则使用当前上下文的副本。

例如,下面的代码将当前的小数精度设置为 42 位,进行一次计算,然后自动恢复到之前的上下文:

from decimal import localcontext

with localcontext() as ctx:
    ctx.prec = 42   # Perform a high precision calculation
    s = calculate_something()
s = +s  # Round the final result back to the default precision

也可以使用Context下面描述的构造函数创建新的上下文。此外,该模块还提供了三个预制上下文:

classdecimal.BasicContext
这是通用十进制算术规范定义的标准上下文。 精度设置为九。 舍入设置为 ROUND_HALF_UP。 所有标志都被清除。 除 InexactRoundedSubnormal 外,所有陷阱均已启用(视为例外)。

因为启用了许多陷阱,所以此上下文对于调试很有用。

classdecimal.ExtendedContext
这是通用十进制算术规范定义的标准上下文。 精度设置为九。 舍入设置为 ROUND_HALF_EVEN。 所有标志都被清除。 没有启用陷阱(以便在计算期间不会引发异常)。

因为陷阱被禁用,所以此上下文对于希望结果值为 NaN 或 Infinity 而不是引发异常的应用程序很有用。 这允许应用程序在可能会停止程序的情况下完成运行。

classdecimal.DefaultContext
上下文构造函数使用此上下文作为新上下文的原型。 更改字段(例如精度)具有更改 Context 构造函数创建的新上下文的默认值的效果。

此上下文在多线程环境中最有用。 在线程启动之前更改其中一个字段具有设置系统范围默认值的效果。 不建议在线程启动后更改字段,因为这需要线程同步以防止竞争条件。

在单线程环境中,最好根本不使用此上下文。 相反,只需如下所述显式创建上下文。

默认值为 prec=28、rounding=ROUND_HALF_EVEN,并为 OverflowInvalidOperationDivisionByZero 启用陷阱。

除了提供的三个上下文之外,还可以使用 Context构造函数创建新的上下文。

class decimal.Contextprec=Nonerounding=NoneEmin=NoneEmax=Nonecapitals=Noneclamp=Noneflags=Nonetraps=None 
创建一个新的上下文。如果未指定字段或为 None,则从 DefaultContext 复制默认值。如果未指定标志字段或为 None,则清除所有标志。

prec 是 [1, MAX_PREC] 范围内的整数,用于设置上下文中算术运算的精度。

舍入选项是舍入模式部分中列出的常量之一。

traps 和 flags 字段列出了要设置的任何信号。通常,新上下文应该只设置陷阱并清除标志。

Emin 和 Emax 字段是指定指数允许的外部限制的整数。 Emin 必须在 [MIN_EMIN, 0] 范围内,Emax 在 [0, MAX_EMAX] 范围内。

capitals 字段为 0 或 1(默认值)。如果设置为 1,则指数以大写字母 E 打印;否则,使用小写 e:Decimal(‘6.02e+23’)。

clamp 字段为 0(默认值)或 1。如果设置为 1,则在此上下文中可表示的 Decimal 实例的指数 e 严格限制在 Emin – prec + 1 <= e <= Emax – prec + 1 范围内. 如果 clamp 为 0,则较弱的条件成立:Decimal 实例的调整指数最多为 Emax。当 clamp 为 1 时,一个大的正态数将在可能的情况下减少其指数,并在其系数中添加相应数量的零,以适应指数约束;这会保留数字的值,但会丢失有关重要尾随零的信息。例如:

>>> Context(prec=6, Emax=999, clamp=1).create_decimal('1.23e999')
Decimal('1.23000E+999')

钳位值为 1 允许与 IEEE 754 中指定的固定宽度十进制交换格式兼容。

Context 类定义了几种通用方法以及大量用于在给定上下文中直接进行算术运算的方法。 此外,对于上面描述的每个 Decimal 方法(adjusted() 和 as_tuple() 方法除外)都有相应的 Context 方法。 例如,对于 Context 实例 C 和 Decimal 实例 x,C.exp(x) 等同于 x.exp(context=C)。 在接受 Decimal 实例的任何地方,每个 Context 方法都接受一个 Python 整数(int 的一个实例)。

clear_flags
将所有标志重置为0
clear_traps
将所有陷阱重置为0

3.3 版中的新功能。

copy
返回上下文的副本。
copy_decimal(
返回 Decimal 实例 num 的副本。
create_decimal(
从num创建一个新的 Decimal 实例,但使用self作为上下文。与Decimal构造函数不同,上下文精度、舍入方法、标志和陷阱应用于转换。

这很有用,因为常量的精度通常高于应用程序所需的精度。另一个好处是舍入会立即消除超出当前精度的数字的意外影响。在以下示例中,使用未舍入的输入意味着将零加到总和中可以改变结果:

>>> getcontext().prec = 3
>>> Decimal('3.4445') + Decimal('1.0023')
Decimal('4.45')
>>> Decimal('3.4445') + Decimal(0) + Decimal('1.0023')
Decimal('4.44')
该方法实现了IBM规范的to-number操作。如果参数是字符串,则不允许有前导或尾随空格或下划线。
create_decimal_from_float(f)
从 float f创建一个新的 Decimal 实例,但使用self 作为上下文进行舍入。与Decimal.from_float()类方法不同,上下文精度、舍入方法、标志和陷阱应用于转换。

>>> context = Context(prec=5, rounding=ROUND_DOWN)
>>> context.create_decimal_from_float(math.pi)
Decimal('3.1415')
>>> context = Context(prec=5, traps=[Inexact])
>>> context.create_decimal_from_float(math.pi)
Traceback (most recent call last):
    ...
decimal.Inexact: None
Etiny
返回等于 Emin – prec + 1 的值,这是次正规结果的最小指数值。 发生下溢时,指数设置为 Etiny。
Etop
返回等于 Emax – prec + 1 的值。

使用小数的常用方法是创建 Decimal 实例,然后应用在活动线程的当前上下文中发生的算术运算。 另一种方法是使用上下文方法在特定上下文中进行计算。 这些方法与 Decimal 类的方法类似,此处仅作简要说明。

abs
返回x的绝对值。
addx , 
返回xy的总和。
canonical
返回相同的 Decimal 对象x
comparex , 
比较xy的数值。
compare_signalx , 
比较两个操作数的数值。
compare_totalx , 
使用它们的抽象表示比较两个操作数。
compare_total_magx , 
使用抽象表示比较两个操作数,忽略符号。
copy_abs
返回x的副本,并将符号设置为 0。
copy_negate
返回x的副本,并将符号反转。
copy_signx , 
将符号从y复制到x
dividex , 
返回x除以y
divide_intx , 
返回x除以y,截断为整数。
divmodx , 
将两个数字相除并返回结果的整数部分。
exp
返回e ** x
fmax , y , 
返回x乘以y加上z
is_canonical
如果x是规范的则返回True;否则返回False
is_finite
如果x是有限的则返回True;否则返回False
is_infinite
如果x是无限的则返回True;否则返回False
is_nan
如果 x 是 qNaN 或 sNaN,则返回 True; 否则返回 False。
is_normal
如果x是一个普通数则返回True;否则返回False
is_qnan
如果 x 是一个安静的 NaN,则返回 True; 否则返回 False。
is_signed
如果x为负则返回True;否则返回False
is_snan
如果 x 是信号 NaN,则返回 True; 否则返回 False。
is_subnormal
如果x是次正规的则返回True;否则返回False
is_zero
如果x为零则返回True;否则返回False
ln
返回x的自然(以 e 为底)对数。
log10
返回x的以 10 为底的对数。
logb
返回操作数的 MSD 大小的指数。
logical_andx , 
在每个操作数的数字之间应用逻辑运算和。
logical_invert
反转x中的所有数字。
logical_orx , 
在每个操作数的数字之间应用逻辑运算或。
logical_xorx , 
在每个操作数的数字之间应用逻辑运算xor 。
maxx , 
比较两个值的数值并返回最大值。
max_magx , 
比较数值并忽略其符号。
minx , 
比较两个值的数值并返回最小值。
min_magx , 
比较数值并忽略其符号。
minus
减号对应于 Python 中的一元前缀减号运算符。
multiplyx , 
返回xy的乘积。
next_minus
返回小于x的最大可表示数。
next_plus
返回大于x的最小可表示数。
next_towardx , 
返回最接近x的数字,朝向y的方向。
normalize
将x简化为最简单的形式。
number_class
返回x的类的指示。
plus
Plus 对应于 Python 中的一元前缀加运算符。此操作应用上下文精度和舍入,因此它不是身份操作。
powerx , y , modulo=None 
返回 x 的 y 次方,如果给定则减少模模。

使用两个参数,计算 x**y。如果 x 是负数,则 y 必须是整数。结果将是不精确的,除非 y 是整数并且结果是有限的并且可以精确地用“精度”数字表示。使用上下文的舍入模式。结果在 Python 版本中始终正确舍入。

在 3.3 版更改:C 模块根据正确舍入的 exp() 和 ln() 函数计算 power() 。结果是明确定义的,但只是“几乎总是正确舍入”。

使用三个参数,计算 (x**y) % 模数。对于三参数形式,对参数有以下限制:

所有三个参数都必须是整数

y 必须是非负的

x 或 y 中至少有一个必须为非零

模数必须为非零且最多具有“精度”数字

Context.power(x, y, modulo) 产生的值等于通过无限精度计算 (x**y) % modulo 获得的值,但计算效率更高。结果的指数为零,与 x、y 和模数的指数无关。结果总是准确的。

quantizex , 
返回等于x(四舍五入)的值,其指数为y
radix
只返回 10,因为这是十进制,:)
remainderx , 
返回整数除法的余数。

如果结果的符号非零,则与原始股息的符号相同。

remainder_nearx , 
返回 x – y * n,其中 n 是最接近 x / y 精确值的整数(如果结果为 0,则其符号将为 x 的符号)。
rotatex , 
返回x , y次的旋转副本。
same_quantumx , 
True如果两个操作数具有相同的指数,则返回。
scalebx , 
将第二个值与其 exp 相加后返回第一个操作数。
shiftx , 
返回x , y次的移位副本。
sqrt
非负数的平方根到上下文精度。
subtractx , 
返回xy之间的差值。
to_eng_string
如果需要指数,则使用工程符号转换为字符串。

工程符号的指数是 3 的倍数。这可以在小数点左侧保留最多 3 位数字,并且可能需要添加一个或两个尾随零。

to_integral_exact
四舍五入为整数。
to_sci_string
使用科学记数法将数字转换为字符串。

常量

本节中的常量仅与 C 模块相关。它们也包含在纯 Python 版本中以实现兼容性。

32-bit 64-bit
decimal.MAX_PREC
425000000 999999999999999999
decimal.MAX_EMAX
425000000 999999999999999999
decimal.MIN_EMIN
-425000000 -999999999999999999
decimal.MIN_ETINY
-849999999

-1999999999999999997

decimal.HAVE_THREADS
值为True。已弃用,因为 Python 现在总是有线程。

3.9 版后弃用。

decimal.HAVE_CONTEXTVAR
默认值为True。如果 Python 被编译--without-decimal-contextvar,C 版本使用线程本地上下文而不是协程本地上下文,并且值为False. 这在某些嵌套上下文场景中稍微快一些。

版本 3.9 中的新功能:向后移植到 3.7 和 3.8

舍入模式

decimal.ROUND_CEILING
绕向Infinity
decimal.ROUND_DOWN
向零舍入。
decimal.ROUND_FLOOR
绕向-Infinity
decimal.ROUND_HALF_DOWN
四舍五入到最接近的关系趋向于零。
decimal.ROUND_HALF_EVEN
四舍五入到最接近的关系到最接近的偶数。
decimal.ROUND_HALF_UP
四舍五入到最接近的关系从零开始。
decimal.ROUND_UP
从零舍入。
decimal.ROUND_05UP
如果向零四舍五入后的最后一位数字是 0 或 5,则从零四舍五入;否则向零舍入。

信号

信号表示计算过程中出现的条件。每个对应一个上下文标志和一个上下文陷阱启动器。

只要遇到条件,就会设置上下文标志。计算之后,可以检查标志以供参考(例如,确定计算是否准确)。检查标志后,请务必在开始下一次计算之前清除所有标志。

如果为信号设置了上下文的陷阱启动器,则该条件会导致引发 Python 异常。例如,如果DivisionByZero设置了陷阱,则DivisionByZero在遇到条件时会引发异常。

classdecimal.Clamped
更改指数以适应表示约束。

通常,当指数落在上下文 EminEmax限制之外时,就会发生钳制。如果可能,通过向系数添加零来减少指数以适合。

classdecimal.DecimalException
其他信号的基类和ArithmeticError.
classdecimal.DivisionByZero
表示非无限数除以零。

可以与除法、模除法或将数字增加到负幂时发生。如果此信号未被捕获,则返回Infinity或 -Infinity带有由计算输入确定的符号。

classdecimal.Inexact
表示发生了舍入,结果不准确。

在舍入期间丢弃非零数字时发出信号。返回四舍五入的结果。信号标志或陷阱用于检测结果何时不准确。

classdecimal.InvalidOperation
执行了无效操作。

表示请求了一个没有意义的操作。如果没有被困,则返回NaN。可能的原因包括:

Infinity - Infinity
0 * Infinity
Infinity / Infinity
x % 0
Infinity % x
sqrt(-x) and x > 0
0 ** 0
x ** (non-integer)
x ** Infinity
classdecimal.Overflow
数值溢出。

表示指数大于Emax四舍五入后的指数。如果没有陷入困境,结果取决于舍入模式,要么向内拉到最大的可表示有限数,要么向外舍入到Infinity. 在任何一种情况下,InexactRounded 也会发出信号。

classdecimal.Rounded
尽管可能没有信息丢失,但发生了舍入。

每当四舍五入丢弃数字时发出信号;即使这些数字为零(例如四舍五入5.005.0)。如果没有陷入,则返回结果不变。该信号用于检测有效数字的丢失。

classdecimal.Subnormal
指数低于Emin四舍五入前。

当运算结果不正常(指数太小)时发生。如果没有陷入,则返回结果不变。

classdecimal.Underflow
结果四舍五入为零的数字下溢。

当通过舍入将次正规结果推为零时发生。Inexact 并且Subnormal也发出信号。

classdecimal.FloatOperation
为混合浮点数和小数启用更严格的语义。

如果信号未被捕获(默认),则允许在Decimal构造函数 create_decimal()和所有比较运算符中混合使用浮点数和小数。转换和比较都是准确的。FloatOperation通过在上下文标志中设置,混合操作的任何发生都会被静默记录下来。from_float() 使用或create_decimal_from_float()不设置标志的显式转换。

否则(信号被捕获),只有相等比较和显式转换是静默的。所有其他混合操作加注FloatOperation

下表总结了信号的层次结构:

exceptions.ArithmeticError(exceptions.Exception)
    DecimalException
        Clamped
        DivisionByZero(DecimalException, exceptions.ZeroDivisionError)
        Inexact
            Overflow(Inexact, Rounded)
            Underflow(Inexact, Rounded, Subnormal)
        InvalidOperation
        Rounded
        Subnormal
        FloatOperation(DecimalException, exceptions.TypeError)

浮点注释

通过提高精度来减少舍入误差

十进制浮点数的使用,消除了十进制表示错误(使精确表示成为可能0.1);但是,当非零数字超过固定精度时,某些操作仍会产生舍入错误。

舍入误差的影响可以通过添加或减去几乎抵消的量来放大,从而导致重要性损失。Knuth 提供了两个有启发性的示例,其中精度不足的舍入浮点运算导致加法的关联和分配属性崩溃:

# Examples from Seminumerical Algorithms, Section 4.2.2.
>>> from decimal import Decimal, getcontext
>>> getcontext().prec = 8

>>> u, v, w = Decimal(11111113), Decimal(-11111111), Decimal('7.51111111')
>>> (u + v) + w
Decimal('9.5111111')
>>> u + (v + w)
Decimal('10')

>>> u, v, w = Decimal(20000), Decimal(-6), Decimal('6.0000003')
>>> (u*v) + (u*w)
Decimal('0.01')
>>> u * (v+w)
Decimal('0.0060000')

decimal模块可以通过充分扩展精度来恢复身份,以避免失去重要性:

>>> getcontext().prec = 20
>>> u, v, w = Decimal(11111113), Decimal(-11111111), Decimal('7.51111111')
>>> (u + v) + w
Decimal('9.51111111')
>>> u + (v + w)
Decimal('9.51111111')
>>>
>>> u, v, w = Decimal(20000), Decimal(-6), Decimal('6.0000003')
>>> (u*v) + (u*w)
Decimal('0.0060000')
>>> u * (v+w)
Decimal('0.0060000')

特殊值

十进制模块的数字系统提供特殊值,包括 NaN、sNaN、-Infinity、Infinity 和两个零,+0 和 -0。

无穷大可以直接构造为:Decimal(‘Infinity’)。此外,当 DivisionByZero 信号未被捕获时,它们可能会因除以零而产生。同样,当溢出信号未被捕获时,无穷大可能是由于四舍五入超出了最大可表示数的限制。

无穷大是有符号的(仿射的),可用于算术运算,在这些运算中它们被视为非常大的不确定数。例如,将一个常数加到无穷大会得到另一个无穷大的结果。

某些操作不确定并返回 NaN,或者如果 InvalidOperation 信号被捕获,则引发异常。例如,0/0 返回 NaN,表示“不是数字”。这种 NaN 的变体是安静的,一旦创建,将流经其他计算,始终会产生另一个 NaN。此行为对于偶尔缺少输入的一系列计算很有用——它允许计算继续进行,同时将特定结果标记为无效。

一个变体是 sNaN,它发出信号而不是在每次操作后保持安静。当无效结果需要中断计算以进行特殊处理时,这是一个有用的返回值。

在涉及 NaN 的情况下,Python 的比较运算符的行为可能有点令人惊讶。如果其中一个操作数是安静的或发出信号的 NaN 的相等性测试总是返回 False(即使在执行 Decimal(‘NaN’)==Decimal(‘NaN’) 时),而不等式测试总是返回 True。如果任一操作数为 NaN,则尝试使用 <、<=、> 或 >= 运算符中的任何一个来比较两个小数将引发 InvalidOperation 信号,如果未捕获此信号,则返回 False。请注意,通用十进制算术规范并未指定直接比较的行为;这些涉及 NaN 的比较规则取自 IEEE 854 标准(请参阅第 5.7 节中的表 3)。为确保严格符合标准,请改用 compare() 和 compare-signal() 方法。

带符号的零可能来自下溢的计算。如果以更高的精度进行计算,它们会保留可能产生的符号。由于它们的大小为零,因此正零和负零都被视为相等,并且它们的符号是信息性的。

除了两个不同但相等的有符号零之外,还有各种精度不同但值相等的零表示形式。这需要一点时间来适应。对于习惯于归一化浮点表示的人来说,以下计算返回的值是否为零并不是显而易见的:

>>> 1 / Decimal('Infinity')
Decimal('0E-1000026')

使用线程

该函数为每个线程getcontext()访问不同的对象。Context拥有单独的线程上下文意味着线程可以在getcontext().prec=10不干扰其他线程的情况下进行更改(例如 )。

同样,该setcontext()函数会自动将其目标分配给当前线程。

如果setcontext()之前没有调用过getcontext(),那么 getcontext()会自动创建一个新的上下文供当前线程使用。

新上下文是从名为DefaultContext的原型上下文中复制的。要控制默认值以便每个线程在整个应用程序中使用相同的值,请直接修改DefaultContext对象。这应该 在任何线程启动之前完成,这样调用getcontext(). 例如:

# Set applicationwide defaults for all threads about to be launched
DefaultContext.prec = 12
DefaultContext.rounding = ROUND_DOWN
DefaultContext.traps = ExtendedContext.traps.copy()
DefaultContext.traps[InvalidOperation] = 1
setcontext(DefaultContext)

# Afterwards, the threads can be started
t1.start()
t2.start()
t3.start()
 . . .

食谱

以下是一些用作实用函数并演示使用Decimal该类的方法的方法:

def moneyfmt(value, places=2, curr='', sep=',', dp='.',
             pos='', neg='-', trailneg=''):
    """Convert Decimal to a money formatted string.

    places:  required number of places after the decimal point
    curr:    optional currency symbol before the sign (may be blank)
    sep:     optional grouping separator (comma, period, space, or blank)
    dp:      decimal point indicator (comma or period)
             only specify as blank when places is zero
    pos:     optional sign for positive numbers: '+', space or blank
    neg:     optional sign for negative numbers: '-', '(', space or blank
    trailneg:optional trailing minus indicator:  '-', ')', space or blank

    >>> d = Decimal('-1234567.8901')
    >>> moneyfmt(d, curr='$')
    '-$1,234,567.89'
    >>> moneyfmt(d, places=0, sep='.', dp='', neg='', trailneg='-')
    '1.234.568-'
    >>> moneyfmt(d, curr='$', neg='(', trailneg=')')
    '($1,234,567.89)'
    >>> moneyfmt(Decimal(123456789), sep=' ')
    '123 456 789.00'
    >>> moneyfmt(Decimal('-0.02'), neg='<', trailneg='>')
    '<0.02>'

    """
    q = Decimal(10) ** -places      # 2 places --> '0.01'
    sign, digits, exp = value.quantize(q).as_tuple()
    result = []
    digits = list(map(str, digits))
    build, next = result.append, digits.pop
    if sign:
        build(trailneg)
    for i in range(places):
        build(next() if digits else '0')
    if places:
        build(dp)
    if not digits:
        build('0')
    i = 0
    while digits:
        build(next())
        i += 1
        if i == 3 and digits:
            i = 0
            build(sep)
    build(curr)
    build(neg if sign else pos)
    return ''.join(reversed(result))

def pi():
    """Compute Pi to the current precision.

    >>> print(pi())
    3.141592653589793238462643383

    """
    getcontext().prec += 2  # extra digits for intermediate steps
    three = Decimal(3)      # substitute "three=3.0" for regular floats
    lasts, t, s, n, na, d, da = 0, three, 3, 1, 0, 0, 24
    while s != lasts:
        lasts = s
        n, na = n+na, na+8
        d, da = d+da, da+32
        t = (t * n) / d
        s += t
    getcontext().prec -= 2
    return +s               # unary plus applies the new precision

def exp(x):
    """Return e raised to the power of x.  Result type matches input type.

    >>> print(exp(Decimal(1)))
    2.718281828459045235360287471
    >>> print(exp(Decimal(2)))
    7.389056098930650227230427461
    >>> print(exp(2.0))
    7.38905609893
    >>> print(exp(2+0j))
    (7.38905609893+0j)

    """
    getcontext().prec += 2
    i, lasts, s, fact, num = 0, 0, 1, 1, 1
    while s != lasts:
        lasts = s
        i += 1
        fact *= i
        num *= x
        s += num / fact
    getcontext().prec -= 2
    return +s

def cos(x):
    """Return the cosine of x as measured in radians.

    The Taylor series approximation works best for a small value of x.
    For larger values, first compute x = x % (2 * pi).

    >>> print(cos(Decimal('0.5')))
    0.8775825618903727161162815826
    >>> print(cos(0.5))
    0.87758256189
    >>> print(cos(0.5+0j))
    (0.87758256189+0j)

    """
    getcontext().prec += 2
    i, lasts, s, fact, num, sign = 0, 0, 1, 1, 1, 1
    while s != lasts:
        lasts = s
        i += 2
        fact *= i * (i-1)
        num *= x * x
        sign *= -1
        s += num / fact * sign
    getcontext().prec -= 2
    return +s

def sin(x):
    """Return the sine of x as measured in radians.

    The Taylor series approximation works best for a small value of x.
    For larger values, first compute x = x % (2 * pi).

    >>> print(sin(Decimal('0.5')))
    0.4794255386042030002732879352
    >>> print(sin(0.5))
    0.479425538604
    >>> print(sin(0.5+0j))
    (0.479425538604+0j)

    """
    getcontext().prec += 2
    i, lasts, s, fact, num, sign = 1, 0, x, 1, x, 1
    while s != lasts:
        lasts = s
        i += 2
        fact *= i * (i-1)
        num *= x * x
        sign *= -1
        s += num / fact * sign
    getcontext().prec -= 2
    return +s

十进制常见问题

Q. 打字很麻烦decimal.Decimal('1234.5')。使用交互式解释器时有没有办法尽量减少打字?

A. 一些用户将构造函数缩写为一个字母:

>>> D = decimal.Decimal
>>> D('1.23') + D('3.45')
Decimal('4.68')

Q. 在有两位小数的定点应用中,有些输入有很多位,需要四舍五入。其他人不应该有多余的数字,需要进行验证。应该使用什么方法?

A. 该quantize()方法四舍五入到固定的小数位数。如果Inexact设置了陷阱,它也可用于验证:

>>> TWOPLACES = Decimal(10) ** -2       # same as Decimal('0.01')
>>> # Round to two places
>>> Decimal('3.214').quantize(TWOPLACES)
Decimal('3.21')
>>> # Validate that a number does not exceed two places
>>> Decimal('3.21').quantize(TWOPLACES, context=Context(traps=[Inexact]))
Decimal('3.21')
>>> Decimal('3.214').quantize(TWOPLACES, context=Context(traps=[Inexact]))
Traceback (most recent call last):
   ...
Inexact: None

问:一旦我有了有效的两个位置输入,我如何在整个应用程序中保持不变?

A. 某些运算如整数的加、减和乘将自动保留不动点。其他操作,如除法和非整数乘法,会改变小数位数,需要跟进一个quantize()步骤:

>>> a = Decimal('102.72')           # Initial fixed-point values
>>> b = Decimal('3.17')
>>> a + b                           # Addition preserves fixed-point
Decimal('105.89')
>>> a - b
Decimal('99.55')
>>> a * 42                          # So does integer multiplication
Decimal('4314.24')
>>> (a * b).quantize(TWOPLACES)     # Must quantize non-integer multiplication
Decimal('325.62')
>>> (b / a).quantize(TWOPLACES)     # And quantize division
Decimal('0.03')

在开发定点应用程序时,定义处理quantize()步骤的函数很方便:

def mul(x, y, fp=TWOPLACES):
    return (x * y).quantize(fp)
def div(x, y, fp=TWOPLACES):
    return (x / y).quantize(fp)
>>> mul(a, b)                       # Automatically preserve fixed-point
Decimal('325.62')
>>> div(b, a)
Decimal('0.03')

Q. 表达相同价值的方式有很多种。数字200、 200.0002E202E+4在不同的精度下都具有相同的值。有没有办法将它们转换为单一可识别的规范值?

A. 该normalize()方法将所有等效值映射到单个代表:

>>> values = map(Decimal, '200 200.000 2E2 .02E+4'.split())
>>> [v.normalize() for v in values]
[Decimal('2E+2'), Decimal('2E+2'), Decimal('2E+2'), Decimal('2E+2')]

问:一些十进制值总是以指数表示法打印。有没有办法获得非指数表示?

A. 对于某些值,指数表示法是表示系数中有效位数的唯一方法。例如,表示 5.0E+35000保持值不变但不能显示原始的二位意义。

如果应用程序不关心跟踪重要性,则很容易删除指数和尾随零,失去重要性,但保持值不变:

def remove_exponent(d):
    return d.quantize(Decimal(1)) if d == d.to_integral() else d.normalize()
>>> remove_exponent(Decimal('5E+3'))
Decimal('5000')

问:有没有办法将常规浮点数转换为 a Decimal ?

A. 是的,任何二进制浮点数都可以精确地表示为 Decimal,尽管精确转换可能需要比直觉所建议的更精确:

>>> Decimal(math.pi)
Decimal('3.141592653589793115997963468544185161590576171875')

问:在复杂的计算中,我如何才能确保我没有因为精度不足或舍入异常而得到虚假结果。

A. decimal 模块使测试结果变得容易。最佳做法是使用更高的精度和各种舍入模式重新运行计算。差异很大的结果表明精度不足、舍入模式问题、输入条件不佳或算法数值不稳定。

问:我注意到上下文精度适用于操作结果,但不适用于输入。混合不同精度的值有什么需要注意的吗?

答:是的。原则是所有值都被认为是精确的,这些值的算术也是如此。只有结果是四舍五入的。输入的优点是“你输入的就是你得到的”。一个缺点是,如果您忘记输入没有被四舍五入,结果可能看起来很奇怪:

>>> getcontext().prec = 3
>>> Decimal('3.104') + Decimal('2.104')
Decimal('5.21')
>>> Decimal('3.104') + Decimal('0.000') + Decimal('2.104')
Decimal('5.20')

解决方案是提高精度或使用一元加运算强制舍入输入:

>>> getcontext().prec = 3
>>> +Decimal('1.23456789')      # unary plus triggers rounding
Decimal('1.23')

或者,可以使用以下 Context.create_decimal()方法在创建时舍入输入:

>>> Context(prec=5, rounding=ROUND_DOWN).create_decimal('1.2345678')
Decimal('1.2345')

问:CPython 实现对于大数来说速度快吗?

答:是的。在 CPython 和 PyPy3 实现中,十进制模块的 C/CFFI 版本集成了高速libmpdec库,用于任意精度正确四舍五入的十进制浮点运算。 对中型数字libmpdec使用Karatsuba 乘法,对非常大的数字使用数论变换 。但是,要实现这种性能提升,需要为未舍入计算设置上下文。

c = getcontext()
c.prec = MAX_PREC
c.Emax = MAX_EMAX
c.Emin = MIN_EMIN