decimal ---小数不动点和浮点运算

源代码: Lib/decimal.py


这个 decimal 模块提供对快速正确舍入的十进制浮点运算的支持。它比 float datatype:

  • “十进制”是基于一个浮点模型,它是为人们设计的,并且必须有一个最重要的指导原则——计算机必须提供一个与人们在学校学习的算法相同的算法。”——摘自《十进制算法规范》。

  • 小数可以精确表示。相比之下,数字 1.12.2 在二进制浮点中没有精确的表示。最终用户通常不会期望 1.1 + 2.2 显示为 3.3000000000000003 就像二进制浮点那样。

  • 精确性转化为算术。在十进制浮点中, 0.1 + 0.1 + 0.1 - 0.3 完全等于零。在二进制浮点中,结果是 5.5511151231257827e-017 . 虽然接近于零,但这些差异会妨碍可靠的相等性测试,并且差异会累积。因此,在具有严格等同性不变量的会计应用程序中,小数是首选的。

  • 十进制模块包含一个重要位置的概念,以便 1.30 + 1.202.50 . 尾随的零表示重要性。这是货币应用的惯用表达方式。对于乘法,“教科书”方法使用乘法中的所有数字。例如, 1.3 * 1.2 给予 1.56 虽然 1.30 * 1.20 给予 1.5600 .

  • 与基于硬件的二进制浮点不同,十进制模块具有用户可更改的精度(默认值为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模块公开了标准的所有必需部分。当需要时,程序员可以完全控制舍入和信号处理。这包括一个选项,通过使用异常来阻止任何不精确的操作来强制执行精确的算术。

  • 十进制模块的设计是为了支持“无偏见的精确无边界十进制算术(有时称为定点算术)和四舍五入浮点算术。”——摘自十进制算术规范。

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

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

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

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

对于每个信号,都有一个标志和一个陷阱启用程序。遇到信号时,其标志设置为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

十进制实例可以由整数、字符串、浮点数或元组构成。整数或浮点的构造执行该整数或浮点值的精确转换。十进制数字包括特殊值,例如 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 新版功能.

一个新的十进制的重要性完全由输入的位数决定。上下文精度和舍入只在算术运算中起作用。

>>> 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')

一些数学函数也可以用十进制表示:

>>> 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 模块提供两个随时可用的标准上下文, BasicContextExtendedContext . 前者对于调试特别有用,因为启用了许多陷阱:

>>> 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=[])

这个 flags 条目显示 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数值类型没有任何不同。

十进制对象

class decimal.Decimal(value='0', context=None)

构建新的 Decimal 基于对象 value .

value 可以是整数、字符串、元组, float ,或另一个 Decimal 对象。如果没有 value 给予,回报 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 出现在上面。这些数字包括来自各种其他字母的十进制数字(例如,阿拉伯-印度和德文-加-加数字)以及全宽数字。 '\uff10' 通过 '\uff19' .

如果 value 是一个 tuple ,应该有三个部分,一个标志 (0 积极或 1 对于阴性),a tuple 数字和整数指数。例如, Decimal((0, (1, 4, 1, 4), -3)) 返回 Decimal('1.414') .

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

这个 context 精度不影响存储的位数。仅由 value . 例如, Decimal('3.00000') 记录所有五个零,即使上下文精度只有三个。

目的 context 参数是决定如果 value 是格式错误的字符串。如果上下文陷阱 InvalidOperation 引发异常;否则,构造函数返回一个新的十进制值,该值为 NaN .

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

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

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

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

十进制浮点对象与其他内置数字类型(如 floatint . 所有常见的数学运算和特殊方法都适用。同样,十进制对象可以被复制、pickle、打印、用作字典键、用作集合元素、比较、排序和强制转换为其他类型(例如 floatint

十进制对象的算术与整数和浮点数的算术有一些小的区别。当余数运算符 % 应用于十进制对象,结果的符号是 股息 而不是除数的符号:

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

整数除法运算符 // 类似地,返回真商的整数部分(截断为零),而不是其底端,以便保留通常的标识 x == (x // y) * y + x % y ::

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

这个 %// 操作员执行 remainderdivide-integer 操作(分别)如规范所述。

decimal对象通常不能与float或 fractions.Fraction 在算术运算中:试图增加 Decimal 到A float 例如,将引发 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)

转换是精确的。在无穷大上引发溢出错误,在NaN上引发值错误。

3.6 新版功能.

as_tuple()

返回A named tuple 数字表示: DecimalTuple(sign, digits, exponent) .

canonical()

返回参数的规范编码。当前,对 Decimal 实例始终是规范的,因此此操作返回的参数不变。

compare(other, context=None)

比较两个十进制实例的值。 compare() 返回一个十进制实例,如果其中一个操作数是NaN,则结果是NaN::

a or b is a NaN  ==> Decimal('NaN')
a < b            ==> Decimal('-1')
a == b           ==> Decimal('0')
a > b            ==> Decimal('1')
compare_signal(other, context=None)

此操作与 compare() 方法,除了所有Nans信号。也就是说,如果两个操作数都不是信号NaN,那么任何安静的NaN操作数都将被视为信号NaN。

compare_total(other, context=None)

使用两个操作数的抽象表示而不是数值来比较它们。类似于 compare() 方法,但结果给出了 Decimal 实例。二 Decimal 具有相同数值但不同表示形式的实例在此顺序中比较不相等:

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

安静和信号NAN也包括在总订单中。这个函数的结果是 Decimal('0') 如果两个操作数具有相同的表示形式, Decimal('-1') 如果第一个操作数的总数低于第二个操作数,并且 Decimal('1') 如果第一个操作数的合计顺序高于第二个操作数。总订单详情见规范。

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

compare_total_mag(other, context=None)

使用两个操作数的抽象表示形式而不是其值(如中所示)进行比较 compare_total() ,但忽略每个操作数的符号。 x.compare_total_mag(y) 等于 x.copy_abs().compare_total(y.copy_abs()) .

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

conjugate()

只返回self,此方法只符合十进制规范。

copy_abs()

返回参数的绝对值。此操作不受上下文影响,并且是安静的:不更改标志,不执行舍入。

copy_negate()

返回参数的否定值。此操作不受上下文影响,并且是安静的:不更改标志,不执行舍入。

copy_sign(other, context=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 .

注解

从python 3.2开始, Decimal 实例也可以直接从 float .

>>> 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')

3.1 新版功能.

fma(other, third, context=None)

熔合乘法加法。回归自我 其他+三分之一,中间产品本身没有四舍五入 其他。

>>> Decimal(2).fma(3, 5)
Decimal('11')
is_canonical()

返回 True 如果参数是规范的,并且 False 否则。目前,A Decimal 实例始终是规范的,因此此操作始终返回 True .

is_finite()

返回 True 如果参数是有限数,并且 False 如果参数是无穷大或NaN。

is_infinite()

返回 True 如果参数为正无穷大或负无穷大, False 否则。

is_nan()

返回 True 如果参数是(安静的或发出信号的)NaN和 False 否则。

is_normal(context=None)

返回 True 如果参数是 正常的 有限数。返回 False 如果参数为零、次正规、无穷大或NaN。

is_qnan()

返回 True 如果参数是一个安静的南,和 False 否则。

is_signed()

返回 True 如果参数有负号,并且 False 否则。注意,0和nan都可以携带符号。

is_snan()

返回 True 如果参数是一个信号NaN,并且 False 否则。

is_subnormal(context=None)

返回 True 如果参数低于正常值,并且 False 否则。

is_zero()

返回 True 如果参数为(正或负)零且 False 否则。

ln(context=None)

返回操作数的自然(底E)对数。使用 ROUND_HALF_EVEN 舍入模式。

log10(context=None)

返回操作数以10为底的对数。使用 ROUND_HALF_EVEN 舍入模式。

logb(context=None)

对于非零数,返回其操作数的调整后指数作为 Decimal 实例。如果操作数为零,则 Decimal('-Infinity') 被退回并且 DivisionByZero 标志被升起。如果操作数是无穷大,则 Decimal('Infinity') 返回。

logical_and(other, context=None)

logical_and() 是一个逻辑运算,需要两个 逻辑操作数 (见 逻辑操作数 )结果是按数字顺序 and 两个操作数中的一个。

logical_invert(context=None)

logical_invert() 是一个逻辑操作。其结果是操作数的位逆运算。

logical_or(other, context=None)

logical_or() 是一个逻辑运算,需要两个 逻辑操作数 (见 逻辑操作数 )结果是按数字顺序 or 两个操作数中的一个。

logical_xor(other, context=None)

logical_xor() 是一个逻辑运算,需要两个 逻辑操作数 (见 逻辑操作数 )。结果是两个操作数的按位异或。

max(other, context=None)

类似于 max(self, other) 除了返回前应用上下文舍入规则,以及 NaN 值被发出信号或忽略(取决于上下文以及它们是发出信号还是安静)。

max_mag(other, context=None)

类似于 max() 方法,但使用操作数的绝对值进行比较。

min(other, context=None)

类似于 min(self, other) 除了返回前应用上下文舍入规则,以及 NaN 值被发出信号或忽略(取决于上下文以及它们是发出信号还是安静)。

min_mag(other, context=None)

类似于 min() 方法,但使用操作数的绝对值进行比较。

next_minus(context=None)

返回小于给定操作数的给定上下文(如果没有给定上下文,则返回当前线程上下文)中可表示的最大数。

next_plus(context=None)

返回大于给定操作数的给定上下文(如果没有给定上下文,则返回当前线程上下文)中可表示的最小数字。

next_toward(other, context=None)

如果两个操作数不相等,则返回第二个操作数方向上最接近第一个操作数的数字。如果两个操作数在数字上相等,则返回第一个操作数的副本,符号设置为与第二个操作数的符号相同。

normalize(context=None)

通过去掉最右边的尾随零并转换等于 Decimal('0')Decimal('0e0') .用于为等价类的属性生成规范值。例如, Decimal('32.100')Decimal('0.321000e+2') 两者都正常化为等效值 Decimal('32.1') .

number_class(context=None)

返回描述 classes 操作数的。返回值是以下十个字符串之一。

  • "-Infinity" ,表示操作数为负无穷大。

  • "-Normal" ,表示操作数是负数。

  • "-Subnormal" ,指示操作数是负的和次正规的。

  • "-Zero" ,指示操作数为负零。

  • "+Zero" ,指示操作数为正零。

  • "+Subnormal" ,表示操作数是正的和次正规的。

  • "+Normal" ,指示操作数是正态数。

  • "+Infinity" ,指示操作数为正无穷大。

  • "NaN" ,指示操作数是安静的NaN(不是数字)。

  • "sNaN" ,表示操作数是一个信号NaN。

quantize(exp, rounding=None, context=None)

返回一个值,该值等于舍入后的第一个操作数,并具有第二个操作数的指数。

>>> Decimal('1.41421356').quantize(Decimal('1.000'))
Decimal('1.414')

与其他运算不同,如果量化运算后系数的长度大于精度,则 InvalidOperation 发出信号。这样可以保证,除非存在错误条件,否则量化指数始终等于右侧操作数的指数。

与其他运算不同的是,量子化从来没有信号下溢,即使结果是次正规的和不精确的。

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

每当结果的指数大于 Emax 或少于 Etiny .

radix()

返回 Decimal(10) ,其中 Decimal 类执行其所有算术。包括与规范的兼容性。

remainder_near(other, context=None)

从除法返回余数 self 通过 other . 这与 self % other 其中,余数的符号被选择以使其绝对值最小化。更准确地说,返回值是 self - n * other 在哪里? n 是最接近 self / other ,如果两个整数相等,则选择偶数整数。

如果结果为零,则其符号将是 self .

>>> 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(other, context=None)

返回将第一个操作数的位数旋转第二个操作数指定的数值的结果。第二个操作数必须是范围-精度到精度之间的整数。第二个操作数的绝对值给出了要旋转的位置数。如果第二个操作数为正,则旋转方向为左;否则旋转方向为右。如有必要,第一个操作数的系数用零填充在左边,以达到长度精度。第一个操作数的符号和指数不变。

same_quantum(other, context=None)

测试自我和他人是否具有相同的指数或两者是否都是 NaN .

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

scaleb(other, context=None)

返回第一个操作数,其指数由第二个操作数调整。等价地,返回第一个操作数乘以 10**other . 第二个操作数必须是整数。

shift(other, context=None)

返回将第一个操作数的位数移动到第二个操作数指定的数值的结果。第二个操作数必须是范围-精度到精度之间的整数。第二个操作数的绝对值给出要移位的位置数。如果第二个操作数是正数,则移位是向左的;否则移位是向右的。移到系数中的数字是零。第一个操作数的符号和指数不变。

sqrt(context=None)

将参数的平方根返回到完全精度。

to_eng_string(context=None)

如果需要指数,则使用工程符号转换为字符串。

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

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

to_integral(rounding=None, context=None)

to_integral_value() 方法。这个 to_integral 名称已保留以与旧版本兼容。

to_integral_exact(rounding=None, context=None)

四舍五入到最接近的整数,发出信号 InexactRounded 如果发生舍入,则视情况而定。舍入模式由 rounding 参数(如果给定),否则按给定 context . 如果两个参数都没有给定,则使用当前上下文的舍入模式。

to_integral_value(rounding=None, context=None)

四舍五入到最接近的整数,无需发出信号 InexactRounded .如果给出,则适用 舍入 ;否则,在提供的 context 或当前上下文。

逻辑操作数

这个 logical_and()logical_invert()logical_or()logical_xor() 方法的参数应为 逻辑操作数 . 一 逻辑操作数 是一个 Decimal 指数和符号都为零且数字都为零的实例 01 .

上下文对象

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

每个线程都有自己的当前上下文,可以使用 getcontext()setcontext() 功能:

decimal.getcontext()

返回活动线程的当前上下文。

decimal.setcontext(c)

将活动线程的当前上下文设置为 c .

您也可以使用 with 声明和 localcontext() 函数临时更改活动上下文。

decimal.localcontext(ctx=None)

返回将活动线程的当前上下文设置为的副本的上下文管理器。 ctx 在进入WITH语句时,并在退出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 建造商描述如下。此外,该模块还提供了三个预先设置的上下文:

class decimal.BasicContext

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

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

class decimal.ExtendedContext

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

由于陷阱被禁用,因此对于希望结果值为的应用程序,此上下文非常有用。 NaNInfinity 而不是提出例外。这样,应用程序就可以在有可能停止程序的情况下完成运行。

class decimal.DefaultContext

此上下文由 Context 构造函数作为新上下文的原型。更改字段(这样的精度)会影响更改由 Context 构造函数。

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

在单线程环境中,最好不要使用此上下文。相反,只需按照下面的描述显式地创建上下文。

默认值为 prec \ 28rounding \ ROUND_HALF_EVEN ,并为启用陷阱 OverflowInvalidOperationDivisionByZero .

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

class decimal.Context(prec=None, rounding=None, Emin=None, Emax=None, capitals=None, clamp=None, flags=None, traps=None)

创建新上下文。如果未指定字段或 None ,默认值从 DefaultContext . 如果 flags 未指定字段或 None ,清除所有标志。

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

这个 舍入 选项是部分中列出的常量之一 Rounding Modes .

这个 陷阱flags 字段列出要设置的任何信号。一般来说,新的上下文应该只设置陷阱并清除标志。

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

这个 首都 字段为 01 (默认值)。如果设置为 1 ,指数用大写字母打印 E ;否则,小写 e 使用: Decimal('6.02e+23') .

这个 夹紧 字段为 0 (违约)或 1 . 如果设置为 1 指数 e A的 Decimal 在此上下文中可表示的实例严格限制在范围内 Emin - prec + 1 <= e <= Emax - prec + 1 . 如果 夹紧0 然后一个较弱的条件成立:调整后的 Decimal 实例最多 Emax .什么时候? 夹紧1 ,如果可能的话,一个大的正规数将使其指数减小,并将相应数量的零添加到其系数中,以适应指数约束;这将保留数字的值,但会丢失有关重要尾随零的信息。例如::

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

A 夹紧 价值 1 允许与IEEE754中指定的固定宽度十进制交换格式兼容。

这个 Context 类定义了几个通用方法以及大量直接在给定上下文中执行算术的方法。此外,对于每个 Decimal 上述方法(除了 adjusted()as_tuple() 方法)有一个对应的 Context 方法。例如,对于 Context 实例 CDecimal 实例 xC.exp(x) 等于 x.exp(context=C) . 各 Context 方法接受一个python整数(的一个实例 int )接受十进制实例的任何地方。

clear_flags()

将所有标志重置为 0 .

clear_traps()

将所有陷阱重置为 0 .

3.3 新版功能.

copy()

返回上下文的副本。

copy_decimal(num)

返回十进制实例num的副本。

create_decimal(num)

从创建新的十进制实例 num 但使用 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)

从浮点创建新的十进制实例 f 但四舍五入使用 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

3.1 新版功能.

Etiny()

返回一个等于 Emin - prec + 1 这是次正规结果的最小指数值。下溢发生时,指数设置为 Etiny .

Etop()

返回一个等于 Emax - prec + 1 .

使用小数的通常方法是 Decimal 实例,然后应用在活动线程的当前上下文中发生的算术操作。另一种方法是使用上下文方法在特定上下文中进行计算。这些方法与 Decimal 只在这里简单地叙述。

abs(x)

返回的绝对值 x .

add(x, y)

返回 xy .

canonical(x)

返回相同的十进制对象 x .

compare(x, y)

比较 xy 数值的。

compare_signal(x, y)

用数字比较两个操作数的值。

compare_total(x, y)

使用抽象表示比较两个操作数。

compare_total_mag(x, y)

使用抽象表示比较两个操作数,忽略符号。

copy_abs(x)

返回的副本 x 符号设置为0。

copy_negate(x)

返回的副本 x 标志倒置。

copy_sign(x, y)

复制签名自 yx .

divide(x, y)

返回 x 除以 y .

divide_int(x, y)

返回 x 除以 y ,截断为整数。

divmod(x, y)

将两个数字相除并返回结果的整数部分。

exp(x)

返回 e ** x .

fma(x, y, z)

返回 x 乘以 y 加上 z .

is_canonical(x)

返回 True 如果 x 是规范的;否则返回 False .

is_finite(x)

返回 True 如果 x 是有限的;否则返回 False .

is_infinite(x)

返回 True 如果 x 是无限的;否则返回 False .

is_nan(x)

返回 True 如果 x 是qnan或snan;否则返回 False .

is_normal(x)

返回 True 如果 x 是一个正常数字;否则返回 False .

is_qnan(x)

返回 True 如果 x 是一个安静的Nan;否则返回 False .

is_signed(x)

返回 True 如果 x 为负;否则返回 False .

is_snan(x)

返回 True 如果 x 是一个信号NaN;否则返回 False .

is_subnormal(x)

返回 True 如果 x 低于正常值;否则返回 False .

is_zero(x)

返回 True 如果 x 为零;否则返回 False .

ln(x)

返回的自然(底E)对数 x .

log10(x)

返回以10为底的对数 x .

logb(x)

返回操作数msd大小的指数。

logical_and(x, y)

应用逻辑操作 and 在每个操作数的数字之间。

logical_invert(x)

将中的所有数字反转 x .

logical_or(x, y)

应用逻辑操作 or 在每个操作数的数字之间。

logical_xor(x, y)

应用逻辑操作 xor 在每个操作数的数字之间。

max(x, y)

用数字比较两个值并返回最大值。

max_mag(x, y)

将数值与忽略的符号进行比较。

min(x, y)

用数字比较两个值并返回最小值。

min_mag(x, y)

将数值与忽略的符号进行比较。

minus(x)

减号对应于python中的一元前缀减号运算符。

multiply(x, y)

返回的产品 xy .

next_minus(x)

返回小于的最大可表示数字 x .

next_plus(x)

返回大于的最小可表示数字 x .

next_toward(x, y)

返回最接近的数字 x ,朝向 y .

normalize(x)

减少 x 最简单的形式。

number_class(x)

返回类的指示 x .

plus(x)

加号对应于python中的一元前缀加号运算符。此操作应用上下文精度和舍入,因此 not 标识操作。

power(x, y, modulo=None)

返回 x 的力量 y ,约化模 modulo 如果给出的话。

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

Decimal(0) ** Decimal(0) 结果在 InvalidOperation 如果 InvalidOperation 没有被困住,结果 Decimal('NaN') .

在 3.3 版更改: C模块计算 power() 就正确的四舍五入而言 exp()ln() 功能。结果是明确的,但只有“几乎总是正确的四舍五入”。

用三个参数,计算 (x**y) % modulo . 对于三个参数形式,对参数的以下限制保持不变:

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

  • y 必须为非负

  • 至少其中之一 xy 必须是非零

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

产生于 Context.power(x, y, modulo) 等于通过计算得到的值 (x**y) % modulo 具有无边界精度,但计算效率更高。结果的指数为零,与 xymodulo . 结果总是准确的。

quantize(x, y)

返回一个等于 x (四舍五入),指数为 y .

radix()

只返回10,因为这是小数,:)

remainder(x, y)

返回整数除法的余数。

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

remainder_near(x, y)

返回 x - y * n 在哪里 n 是最接近的整数 x / y (如果结果为0,则其符号将是 x

rotate(x, y)

返回的旋转副本 xy 时代。

same_quantum(x, y)

返回 True 如果两个操作数的指数相同。

scaleb(x, y)

将第二个值的exp相加后返回第一个操作数。

shift(x, y)

返回的移位副本 xy 时代。

sqrt(x)

非负数的平方根到上下文精度。

subtract(x, y)

返回两者之间的差额 xy .

to_eng_string(x)

如果需要指数,则使用工程符号转换为字符串。

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

to_integral_exact(x)

四舍五入为整数。

to_sci_string(x)

使用科学记数法将数字转换为字符串。

常量

本节中的常数仅与C模块相关。为了兼容性,它们也包含在纯Python版本中。

32位

64位

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 遇到条件时引发异常。

class decimal.Clamped

更改了指数以适应表示约束。

通常,当指数落在上下文的 EminEmax 限制。如果可能的话,通过在系数上加零,将指数减小为合适的。

class decimal.DecimalException

其他信号的基类和 ArithmeticError .

class decimal.DivisionByZero

表示非无限数除以零。

可以用除法、模除法,也可以用负幂来增加一个数。如果此信号未被捕获,则返回 Infinity-Infinity 符号由计算输入决定。

class decimal.Inexact

指示发生舍入,结果不准确。

舍入过程中丢弃非零数字的信号。返回舍入结果。信号标志或陷阱用于检测结果何时不准确。

class decimal.InvalidOperation

执行的操作无效。

指示请求的操作没有意义。如果没有陷阱,返回 NaN .可能的原因包括:

Infinity - Infinity
0 * Infinity
Infinity / Infinity
x % 0
Infinity % x
sqrt(-x) and x > 0
0 ** 0
x ** (non-integer)
x ** Infinity
class decimal.Overflow

数字溢出。

指示指数大于 Emax 舍入发生后。如果没有捕获,则结果取决于舍入模式,要么向内拉到最大的可表示有限个数,要么向外舍入到 Infinity . 在任何一种情况下, InexactRounded 也有信号。

class decimal.Rounded

舍入发生,但可能没有丢失任何信息。

舍入丢弃数字时发出信号;即使这些数字为零(如舍入 5.005.0 )如果未被捕获,则返回未更改的结果。该信号用于检测有效数字的丢失。

class decimal.Subnormal

指数低于 Emin 四舍五入之前。

在操作结果低于正常值(指数太小)时发生。如果未被捕获,则返回未更改的结果。

class decimal.Underflow

数值下溢,结果四舍五入为零。

在通过舍入将次正常结果推送到零时发生。 InexactSubnormal 也有信号。

class decimal.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')

特殊价值观

数字系统 decimal 模块提供特殊值,包括 NaNsNaN-InfinityInfinity 两个零, +0-0 .

无穷大可以直接用以下方法构造: Decimal('Infinity') .此外,当 DivisionByZero 信号未被捕获。同样,当 Overflow 信号没有被捕获,无穷大可能是由于舍入超过了最大可表示数的限制。

无穷大是有符号的(仿射),可以用在算术运算中,它们被当作非常大的不确定数来处理。例如,将常量添加到无穷大会得到另一个无穷大的结果。

有些操作不确定并返回 NaN ,或者如果 InvalidOperation 信号被捕获,引发异常。例如, 0/0 返回 NaN 意思是“不是数字”。这种多样性 NaN 是安静的,一旦创建,将通过其他计算流,总是导致另一个 NaN . 这种行为对于一系列偶尔缺少输入的计算很有用——它允许计算继续进行,同时将特定结果标记为无效。

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

python的比较运算符的行为可能有点令人惊讶, NaN 涉及。一种相等性测试,其中一个操作数是静默或信令。 NaN 总是回报 False (即使在做的时候 Decimal('NaN')==Decimal('NaN') ,而对不等式的测试总是返回 True . 尝试使用任何 <<=>>= 操作员将引发 InvalidOperation 如果任一操作数是 NaN 并返回 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 对象。应该这样做 before 所有线程都已启动,这样线程之间就不会有争用条件调用 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

十进制常见问题解答

问:打字很麻烦 decimal.Decimal('1234.5') . 在使用交互式解释器时,是否有一种最小化键入的方法?

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

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

在有两位小数的定点应用程序中,有些输入有许多位,需要舍入。其他人不应该有多余的数字,需要验证。应该使用什么方法?

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

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

一些操作,如加、减、乘整数,将自动保留固定点。其他的操作,如除法和非整数乘法,会改变小数位数,需要在后面加上 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')

有很多方法可以表达相同的价值。数字 200200.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+3 作为 5000 保持值不变,但不能显示原始的两位重要性。

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

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

有没有办法把一个普通的浮点转换成 Decimal 是吗?

A.是的,任何二进制浮点数都可以精确地表示为十进制,但精确的转换可能比直觉更精确:

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

在一个复杂的计算中,我怎样才能确保我没有因为不够精确或舍入异常而得到错误的结果呢?

a.十进制模块便于测试结果。最佳实践是使用更高的精度和各种舍入模式重新运行计算。不同的结果表明精度不足、舍入模式问题、条件不好的输入或数值不稳定的算法。

我注意到上下文精度应用于操作结果,而不是输入。在混合不同精度的值时有什么需要注意的吗?

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

>>> 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 任意精度精确舍入十进制浮点运算库 1. libmpdec 使用 Karatsuba multiplication 对于中等大小的数字和 Number Theoretic Transform 对于非常大的数量。

上下文必须适合精确的任意精度算法。 EminEmax 应始终设置为最大值, clamp 应始终为0(默认值)。设置 prec 需要小心。

尝试bignum算法的最简单方法是使用 prec2:

>>> setcontext(Context(prec=MAX_PREC, Emax=MAX_EMAX, Emin=MIN_EMIN))
>>> x = Decimal(2) ** 256
>>> x / 128
Decimal('904625697166532776746648320380374280103671755200316906558262375061821325312')

对于不精确的结果, MAX_PREC 在64位平台上太大,可用内存不足:

>>> Decimal(1) / 3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
MemoryError

在过度分配的系统(如Linux)上,一种更复杂的方法是调整 prec 达到可用RAM的数量。假设您有8GB的RAM,并且预期有10个同时操作数,每个操作数的最大值为500MB:

>>> import sys
>>>
>>> # Maximum number of digits for a single operand using 500MB in 8-byte words
>>> # with 19 digits per word (4-byte and 9 digits for the 32-bit build):
>>> maxdigits = 19 * ((500 * 1024**2) // 8)
>>>
>>> # Check that this works:
>>> c = Context(prec=maxdigits, Emax=MAX_EMAX, Emin=MIN_EMIN)
>>> c.traps[Inexact] = True
>>> setcontext(c)
>>>
>>> # Fill the available precision with nines:
>>> x = Decimal(0).logical_invert() * 9
>>> sys.getsizeof(x)
524288112
>>> x + 2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  decimal.Inexact: [<class 'decimal.Inexact'>]

一般情况下(尤其是在没有过度分配的系统上),建议估计更紧的界限并设置 Inexact 如果所有的计算都是精确的,就设陷阱。

1

3.3 新版功能.

2

在 3.9 版更改: 这种方法现在适用于除非整数幂外的所有精确结果。