9.4. 十进制—十进制固定点和浮点算法

2.4 版的新Function。

decimal模块支持十进制浮点运算。与float数据类型相比,它具有一些优点:

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

  • 小数可以精确表示。相反,数字1.12.2在二进制浮点数中没有确切的表示形式。finally用户通常不希望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 类型仅公开其Function的适度部分,而十进制模块则公开标准的所有必需部分。在需要时,程序员可以完全控制舍入和 signal 处理。这包括pass使用异常来阻止任何不精确操作来强制执行精确算术的选项。

  • 十进制模块被设计为支持“在不存在偏见的情况下,精确的未舍入的十进制算术(有时称为定点算术)和舍入的浮点算术。” –摘自十进制算术规范。

模块设计围绕三个概念:十进制数,算术上下文和 signal。

十进制数字是不可变的。它具有一个符号,系数数字和一个指数。为了保留重要性,系数数字不会截断尾随零。小数还包括特殊值,例如Infinity-InfinityNaN。该标准还将-0+0区分开。

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

signal 是在计算过程中出现的一组特殊情况。根据应用程序的需求,signal 可能会被忽略,被认为是信息性的或被视为异常。十进制模块中的 signal 为:ClampedInvalidOperationDivisionByZeroInexactRoundedSubnormalOverflowUnderflow

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

See also

9.4.1. 快速入门教程

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

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

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

十进制实例可以由整数,字符串,浮点数或 Tuples 构造。从整数或浮点数进行构造会对该整数或浮点数的值进行精确转换。十进制数字包括特殊值,例如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.41421356237')
>>> Decimal(2) ** Decimal('0.5')
Decimal('1.414213562373095048801688724')
>>> Decimal('NaN')
Decimal('NaN')
>>> Decimal('-Infinity')
Decimal('-Infinity')

新十进制的重要性仅由 Importing 的位数确定。上下文精度和舍入仅在算术运算中起作用。

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

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

>>> data = 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)     # round() first converts to binary floating point
1.3
>>> int(a)
1
>>> a * 5
Decimal('6.70')
>>> a * b
Decimal('2.5058')
>>> c % a
Decimal('0.77')

还有一些 math 函数可用于 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()构造函数创建备用上下文可能会很有用。要启用备用Function,请使用setcontext()Function。

根据该标准,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=-999999999, Emax=999999999,
        capitals=1, 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

上下文还具有 signal 标志,用于监视在计算过程中遇到的异常情况。这些标志将保持设置状态直到被明确清除为止,因此最好使用clear_flags()方法在每组监视的计算之前清除这些标志。

>>> setcontext(ExtendedContext)
>>> getcontext().clear_flags()
>>> Decimal(355) / Decimal(113)
Decimal('3.14159292')
>>> getcontext()
Context(prec=9, rounding=ROUND_HALF_EVEN, Emin=-999999999, Emax=999999999,
        capitals=1, flags=[Rounded, Inexact], 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 数字类型一样。

9.4.2. 小数对象

    • class * decimal. Decimal([* value * [,* context *]])
    • 根据* value *构造一个新的Decimal对象。
  • value 可以是整数,字符串,Tuples,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

如果* value *是一个 unicode 字符串,则也可以在上面出现digit的地方使用其他 Unicode 十进制数字。其中包括来自其他各种字母的十进制数字(例如,阿拉伯文-印度和德文加里数字)以及全角数字u'\uff10'u'\uff19'

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

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

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

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

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

在 2.6 版中进行了更改:从字符串创建 Decimal 实例时,允许使用前导和尾随空格字符。

在 2.7 版中进行了更改:构造函数的参数现在允许为float实例。

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

小数对象的算术与整数和浮点数的算术之间存在一些细微差异。当将余数运算符%应用于十进制对象时,结果的符号是股息的符号,而不是除数的符号:

>>> (-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的try将引发TypeError。此规则有一个 exception:可以使用 Python 的比较运算符将float实例xDecimal实例y进行比较。如果没有这个 exception,则Decimalfloat实例之间的比较将遵循参考手册Expressions部分中描述的比较不同类型对象的一般规则,从而导致结果混乱。

在 2.7 版中进行了更改:float实例xDecimal实例y之间的比较现在返回基于xy的值的结果。在较早版本中,x < y对任何Decimal实例x和任何float实例y返回相同(任意)结果。

除标准数值属性外,十进制浮点对象还具有许多专用方法:

  • adjusted ( )

    • 移出系数的最右边数字后,返回调整后的指数,直到仅保留前导数字为止:Decimal('321e+5').adjusted()返回 7.用于确定最高有效数字相对于小数点的位置。
  • as_tuple ( )

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

在 2.6 版中进行了更改:使用命名的 Tuples。

  • canonical ( )
    • 返回参数的规范编码。当前,Decimal实例的编码始终是规范的,因此此操作将其参数保持不变。

2.6 版的新Function。

  • compare(其他 [,上下文])
    • 比较两个十进制实例的值。此操作的行为与通常的比较方法cmp()相同,除了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(其他 [,上下文])
    • 此操作与compare()方法相同,除了所有 NaNsignal。也就是说,如果两个操作数都不是信令 NaN,则将任何安静的 NaN 操作数都视为信令 NaN。

2.6 版的新Function。

  • compare_total(其他)
    • 使用它们的抽象表示而不是数值比较两个操作数。与compare()方法类似,但是结果给出了Decimal实例的总排序。具有相同数值但表示形式不同的两个Decimal实例按以下 Sequences 比较不相等:
>>> Decimal('12.0').compare_total(Decimal('12'))
Decimal('-1')

安静和信令 NaN 也包括在总排序中。如果两个操作数具有相同的表示形式,则此函数的结果为Decimal('0');如果第一个操作数的总 Sequences 低于第二个操作数,则为Decimal('-1');如果第一个操作数的总 Sequences 高于第二个操作数,则为Decimal('1')。有关总订单的详细信息,请参见规格。

2.6 版的新Function。

  • compare_total_mag(其他)
    • 使用它们的抽象表示来比较两个操作数,而不是像compare_total()那样比较它们的值,但忽略每个操作数的符号。 x.compare_total_mag(y)等效于x.copy_abs().compare_total(y.copy_abs())

2.6 版的新Function。

  • conjugate ( )
    • 只是返回自身,此方法仅符合十进制规范。

2.6 版的新Function。

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

2.6 版的新Function。

  • copy_negate ( )
    • 返回参数的取反。此操作不受上下文的影响,并且是安静的:不更改标志且不执行舍入。

2.6 版的新Function。

  • copy_sign(其他)
    • 返回第一个操作数的副本,其符号设置为与第二个操作数的符号相同。例如:
>>> Decimal('2.3').copy_sign(Decimal('-1.5'))
Decimal('-2.3')

此操作不受上下文的影响,并且是安静的:不更改标志且不执行舍入。

2.6 版的新Function。

  • exp([上下文])
    • 以给定的数字返回(自然)指数函数e**x的值。使用ROUND_HALF_EVEN舍入模式可以正确舍入结果。
>>> Decimal(1).exp()
Decimal('2.718281828459045235360287471')
>>> Decimal(321).exp()
Decimal('2.561702493119680037517373933E+139')

2.6 版的新Function。

  • from_float(* f *)
    • 精确地将浮点数转换为十进制数的类方法。

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

Note

从 Python 2.7 起,也可以直接从float构造Decimal实例。

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

2.7 版的新Function。

  • fma(其他第三 [,上下文])
    • 融合乘法加法。返回自身其他三分之一,中间产品自身其他不四舍五入。
>>> Decimal(2).fma(3, 5)
Decimal('11')

2.6 版的新Function。

  • is_canonical ( )
    • 如果参数为规范则返回True,否则返回False。当前,Decimal实例始终是规范的,因此此操作始终返回True

2.6 版的新Function。

  • is_finite ( )
    • 如果参数为有限数,则返回True;如果参数为无穷大或 NaN,则返回False

2.6 版的新Function。

  • is_infinite ( )
    • 如果参数是正无穷大或负无穷大,则返回True,否则返回False

2.6 版的新Function。

  • is_nan ( )
    • 如果参数是(安静或信令)NaN,则返回True,否则返回False

2.6 版的新Function。

  • is_normal ( )
    • 如果参数是* normal 有限非零数字且调整后的指数大于或等于 Emin *,则返回True。如果参数为零,次正规,无限或 NaN,则返回False。请注意,术语“正常”在这里与normalize()方法(用于创建规范值)的含义不同。

2.6 版的新Function。

  • is_qnan ( )
    • 如果参数是一个安静的 NaN,则返回True,否则返回False

2.6 版的新Function。

  • is_signed ( )
    • 如果自变量带有负号,则返回True,否则返回False。请注意,零和 NaN 都可以携带符号。

2.6 版的新Function。

  • is_snan ( )
    • 如果自变量是 signalNaN,则返回True,否则返回False

2.6 版的新Function。

  • is_subnormal ( )
    • 如果自变量是次正规的,则返回True,否则返回False。如果数字为非零,有限且调整后的指数小于* Emin *,则该数字为次正规的。

2.6 版的新Function。

  • is_zero ( )
    • 如果参数为(正数或负数)零,则返回True,否则返回False

2.6 版的新Function。

  • ln([上下文])
    • 返回操作数的自然对数(以 e 为底)。使用ROUND_HALF_EVEN舍入模式可以正确舍入结果。

2.6 版的新Function。

  • log10([上下文])
    • 返回操作数的以 10 为底的对数。使用ROUND_HALF_EVEN舍入模式可以正确舍入结果。

2.6 版的新Function。

  • logb([上下文])
    • 对于非零数字,将其操作数的调整后指数作为Decimal实例返回。如果操作数为零,则返回Decimal('-Infinity')并引发DivisionByZero标志。如果操作数是无穷大,则返回Decimal('Infinity')

2.6 版的新Function。

  • logical_and(其他 [,上下文])

2.6 版的新Function。

  • logical_invert([上下文])

2.6 版的新Function。

  • logical_or(其他 [,上下文])

2.6 版的新Function。

  • logical_xor(其他 [,上下文])

2.6 版的新Function。

  • max(其他 [,上下文])

    • max(self, other)相似,不同之处在于在返回之前应用了上下文舍入规则,并且发了 signal 或忽略了NaN值(取决于上下文以及它们是发 signal 还是安静)。
  • max_mag(其他 [,上下文])

    • max()方法类似,但是比较是使用操作数的绝对值完成的。

2.6 版的新Function。

  • min(其他 [,上下文])

    • min(self, other)相似,不同之处在于在返回之前应用了上下文舍入规则,并且发了 signal 或忽略了NaN值(取决于上下文以及它们是发 signal 还是安静)。
  • min_mag(其他 [,上下文])

    • min()方法类似,但是比较是使用操作数的绝对值完成的。

2.6 版的新Function。

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

2.6 版的新Function。

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

2.6 版的新Function。

  • next_toward(其他 [,上下文])
    • 如果两个操作数不相等,则在第二个操作数的方向上返回最接近第一个操作数的数字。如果两个操作数在数值上相等,则返回第一个操作数的副本,其符号设置为与第二个操作数的符号相同。

2.6 版的新Function。

  • normalize([上下文])

    • pass去除最右边的尾随零并将所有等于Decimal('0')的结果转换为Decimal('0e0')来归一化该数字。用于为等效类的属性生成规范值。例如,Decimal('32.100')Decimal('0.321000e+2')都归一化为等效值Decimal('32.1')
  • number_class([上下文])

    • 返回描述操作数的* class *的字符串。返回的值是以下十个字符串之一。
  • "-Infinity",表示操作数为负无穷大。

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

  • "-Subnormal",表示操作数为负且不正常。

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

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

  • "+Subnormal",表示操作数为正且为次正规的。

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

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

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

  • "sNaN",指示操作数是信令 NaN。

2.6 版的新Function。

  • quantize((* exp * [,舍入 [,上下文 [,* watchexp *]]])
    • 四舍五入后,返回等于第一个操作数的值,并具有第二个操作数的指数。
>>> Decimal('1.41421356').quantize(Decimal('1.000'))
Decimal('1.414')

与其他操作不同,如果量化操作之后的系数长度大于精度,则会发出InvalidOperationsignal。这样可以确保,除非有错误情况,否则量化的指数始终等于右侧操作数的指数。

同样与其他操作不同,即使结果不合标准且不精确,量化也永远不会表示下溢。

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

如果设置了* watchexp *(默认值),则每当所得指数大于Emax或小于Etiny时,都会返回错误。

  • radix ( )
    • 返回Decimal(10),即Decimal类在其中进行所有算术运算的基数(基数)。包括在内是为了与规范兼容。

2.6 版的新Function。

  • remainder_near(其他 [,上下文])
    • 返回* 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(其他 [,上下文])
    • 返回将第一个操作数的位数旋转第二个操作数指定的数量的结果。第二个操作数必须是-precision 至 precision 范围内的整数。第二个操作数的绝对值给出旋转的位数。如果第二个操作数为正,则向左旋转;否则,向左旋转。否则旋转向右。如有必要,第一个操作数的系数在左侧填充零以达到长度精度。第一个操作数的符号和指数保持不变。

2.6 版的新Function。

  • same_quantum(其他 [,上下文])

    • 测试 self 和 other 是否具有相同的指数或都为NaN
  • scaleb(其他 [,上下文])

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

2.6 版的新Function。

  • shift(其他 [,上下文])
    • 返回将第一个操作数的位数移位第二个操作数指定的数量的结果。第二个操作数必须是-precision 至 precision 范围内的整数。第二个操作数的绝对值给出要移位的位数。如果第二个操作数为正,则向左移动;否则,向左移动。否则,移动将向右。移入系数的数字为零。第一个操作数的符号和指数保持不变。

2.6 版的新Function。

  • sqrt([上下文])

    • 将参数的平方根返回全精度。
  • to_eng_string([上下文])

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

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

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

  • to_integral([[四舍五入* [,上下文]])

  • to_integral_exact([[四舍五入* [,上下文]])

    • 四舍五入到最接近的整数,如果发生舍入,则酌情发出InexactRoundedsignal。舍入模式由rounding参数(如果给定)确定,否则由给定context确定。如果没有给出任何参数,则使用当前上下文的舍入模式。

2.6 版的新Function。

  • to_integral_value([[四舍五入* [,上下文]])
    • 四舍五入到最接近的整数,而不用 signalInexactRounded。如果给出,则应用四舍五入;否则,在提供的* context *或当前上下文中使用舍入方法。

在 2.6 版中更改:从to_integral重命名为to_integral_value。旧名称对于兼容性仍然有效。

9.4.2.1. 逻辑操作数

logical_and()logical_invert()logical_or()logical_xor()方法期望它们的参数是逻辑操作数逻辑操作数是一个Decimal实例,其指数和符号均为零,并且其数字均为01

9.4.3. 上下文对象

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

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

  • decimal. getcontext ( )

    • 返回活动线程的当前上下文。
  • decimal. setcontext(* c *)

    • 将活动线程的当前上下文设置为* c *。

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

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

2.5 版的新Function。

例如,以下代码将当前的十进制精度设置为 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

with localcontext(BasicContext):      # temporarily use the BasicContext
    print Decimal(1) / Decimal(7)
    print Decimal(355) / Decimal(113)

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

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

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

  • 类别 decimal. ExtendedContext
    • 这是通用十进制算术规范定义的标准上下文。精度设置为九。舍入设置为ROUND_HALF_EVEN。所有标志被清除。没有启用陷阱(因此在计算过程中不会引发异常)。

因为禁用了陷阱,所以此上下文对于希望结果值为NaNInfinity而不是引发异常的应用程序很有用。这允许应用程序在可能会导致程序停止的条件下完成运行。

  • 类别 decimal. DefaultContext
    • Context构造函数将此上下文用作新上下文的原型。更改字段(这样的精度)会更改Context构造函数创建的新上下文的默认值。

此上下文在多线程环境中最有用。在启动线程之前更改字段之一会影响设置系统范围的默认值。不建议在线程启动后更改字段,因为这将需要线程同步以防止出现竞争状况。

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

默认值是 precision = 28,舍入= ROUND_HALF_EVEN,并启用了溢出,InvalidOperation 和 DivisionByZero 陷阱。

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

    • class * decimal. Context(* prec = None rounding = None traps = None flags = None Emin = None Emax = None capitals = 1 *)
    • 创建一个新的上下文。如果未指定字段或为None,则从DefaultContext复制默认值。如果未指定* flags 字段或为None,则清除所有标志。
  • prec *字段是一个正整数,用于设置上下文中算术运算的精度。

四舍五入选项是以下之一:

  • ROUND_CEILING(朝Infinity),

  • ROUND_DOWN(接近零),

  • ROUND_FLOOR(朝-Infinity),

  • ROUND_HALF_DOWN(关系最接近零)

  • ROUND_HALF_EVEN(关系最接近,偶数最接近偶数),

  • ROUND_HALF_UP(关系从零开始到最近)或

  • ROUND_UP(远离零)。

  • ROUND_05UP(如果四舍五入后的最后一位数字为零,则为零;否则为零)

  • traps flags *字段列出了所有要设置的 signal。通常,新的上下文应仅设置陷阱并清除标志。

  • Emin Emax *字段是整数,用于指定指数允许的外部限制。

  • capitals *字段是01(默认值)。如果设置为1,则指数以大写的E打印;否则,使用小写的eDecimal('6.02e+23')

在 2.6 版中进行了更改:添加了ROUND_05UP舍入模式。

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

  • clear_flags ( )

    • 将所有标志重置为0
  • copy ( )

    • 返回上下文的副本。
  • copy_decimal(* num *)

    • 返回 Decimal 实例 num 的副本。
  • create_decimal(* num *)

    • 从* num 创建一个新的 Decimal 实例,但使用 self *作为上下文。与Decimal构造函数不同,上下文精度,舍入方法,标志和陷阱将应用于转换。

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

>>> getcontext().prec = 3
>>> Decimal('3.4445') + Decimal('1.0023')
Decimal('4.45')
>>> Decimal('3.4445') + Decimal(0) + Decimal('1.0023')
Decimal('4.44')

该方法实现了 IBM 规范的数字运算。如果参数是字符串,则不允许前导或尾随空格。

  • create_decimal_from_float(* f *)
    • 从浮点* 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):
    ...
Inexact: None

2.7 版的新Function。

  • Etiny ( )

    • 返回等于Emin - prec + 1的值,该值是次正规结果的最小指数值。当发生下溢时,指数设置为Etiny
  • Etop ( )

    • 返回等于Emax - prec + 1的值。

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

  • abs(* x *)

    • 返回* x *的绝对值。
  • add(* x y *)

    • 返回* x y *的总和。
  • canonical(* x *)

    • 返回相同的 Decimal 对象* x *。
  • compare(* x y *)

    • 对* x y *进行数字比较。
  • 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 *)

    • 将符号从* y 复制到 x *。
  • 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 *)

    • 如果* x *是规范的,则返回True;否则返回False
  • is_finite(* x *)

    • 如果* x *是有限的,则返回True;否则返回False
  • is_infinite(* x *)

    • 如果* x *是无限的,则返回True;否则返回False
  • is_nan(* x *)

    • 如果* x *是 qNaN 或 sNaN,则返回True;否则返回False
  • is_normal(* x *)

    • 如果* x *是一个正常数字,则返回True;否则返回False
  • is_qnan(* x *)

    • 如果* x *是一个安静的 NaN,则返回True;否则返回False
  • is_signed(* x *)

    • 如果* x *为负,则返回True;否则返回False
  • is_snan(* x *)

    • 如果* x *是一个信令 NaN,则返回True;否则返回False
  • is_subnormal(* x *)

    • 如果* x *是次正规的,则返回True;否则返回False
  • is_zero(* x *)

    • 如果* x *为零,则返回True;否则返回False
  • ln(* x *)

    • 返回* x *的自然(以 e 为底)对数。
  • log10(* x *)

    • 返回* x *的以 10 为底的对数。
  • logb(* x *)

    • 返回操作数的 MSD 大小的指数。
  • logical_and(* x y *)

    • 在每个操作数的数字之间应用逻辑运算符
  • logical_invert(* x *)

    • 反转* x *中的所有数字。
  • logical_or(* x y *)

    • 在每个操作数的数字之间应用逻辑运算符
  • logical_xor(* x y *)

    • 在每个操作数的数字之间应用逻辑运算* xor *。
  • max(* x y *)

    • 数字比较两个值并返回最大值。
  • max_mag(* x y *)

    • pass数值比较数值并将其符号忽略。
  • min(* x y *)

    • 数字比较两个值并返回最小值。
  • min_mag(* x y *)

    • pass数值比较数值并将其符号忽略。
  • minus(* x *)

    • Minus 对应于 Python 中的一元前缀减号运算符。
  • multiply(* x y *)

    • 返回* x y *的乘积。
  • next_minus(* x *)

    • 返回小于* x *的最大可表示数字。
  • next_plus(* x *)

    • 返回大于* x *的最小可表示数字。
  • next_toward(* x y *)

    • 返回朝* y 方向最接近 x *的数字。
  • normalize(* x *)

    • 将* x *简化为最简单的形式。
  • number_class(* x *)

    • 返回* x *类的指示。
  • plus(* x *)

    • 加号对应于 Python 中的一元前缀加号运算符。此操作应用上下文精度和舍入,因此它不是身份操作。
  • power(* x y * [,* modulo *])

    • 返回xy的幂,如果给定,则模modulo的模数减少。

有两个参数,计算x**y。如果x为负,则y必须为整数。除非y是整数且结果是有限的并且可以用“精度”数字精确表示,否则结果将是不精确的。应始终使用当前线程上下文的舍入模式正确舍入结果。

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

Note

  • 这三个参数都必须是整数

  • y必须为非负数

  • xy中的至少一个必须为非零

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

Context.power(x, y, modulo)所得的值等于以无限精度计算(x**y) % modulo所获得的值,但计算效率更高。无论xymodulo的指数如何,结果的指数均为零。结果总是准确的。

在 2.6 版中进行了更改:y现在可能在x**y中是非整数的。三参数版本的要求更严格。

  • 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 *)

    • 返回* x y *次的旋转副本。
  • same_quantum(* x y *)

    • 如果两个操作数具有相同的指数,则返回True
  • scaleb(* x y *)

    • 将第二个值与其 exp 相加后,返回第一个操作数。
  • shift(* x y *)

    • 返回* x y *次的移位副本。
  • sqrt(* x *)

    • 非负数的平方根到上下文精度。
  • subtract(* x y *)

    • 返回* x y *之间的差。
  • to_eng_string(* x *)

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

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

  • to_integral_exact(* x *)

    • 四舍五入为整数。
  • to_sci_string(* x *)

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

9.4.4. Signals

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

每当遇到条件时,就设置上下文标志。计算之后,出于信息目的(例如,确定计算是否准确)可以检查标志。检查标志后,请确保在开始下一次计算之前清除所有标志。

如果为 signal 设置了上下文的陷阱启用器,则该条件将引发 Python 异常。例如,如果设置了DivisionByZero陷阱,则遇到条件时会引发DivisionByZero异常。

  • 类别 decimal. Clamped
    • 更改了指数以适合表示约束。

通常,当指数超出上下文的EminEmax限制时,就会发生钳位。如果可能,pass将零加到系数上来减小指数以适合。

  • 类别 decimal. DecimalException

  • 类别 decimal. DivisionByZero

    • 表示将非无限数除以零。

可能发生除法,模除或将数字加到负幂的情况。如果未捕获此 signal,则返回Infinity-Infinity以及由 Importing 确定的符号。

  • 类别 decimal. Inexact
    • 表示发生了舍入并且结果不精确。

在舍入过程中丢弃非零数字时发出 signal。舍入结果返回。signal 标志或陷阱用于检测结果不准确的时间。

  • 类别 decimal. InvalidOperation
    • 执行了无效的操作。

表示请求的操作没有意义。如果未捕获,则返回NaN。可能的原因包括:

Infinity - Infinity
0 * Infinity
Infinity / Infinity
x % 0
Infinity % x
x._rescale( non-integer )
sqrt(-x) and x > 0
0 ** 0
x ** (non-integer)
x ** Infinity
  • 类别 decimal. Overflow
    • Numerical overflow.

表示四舍五入后的指数大于Emax。如果没有被捕获,则结果取决于舍入模式,是向内拉到可表示的最大有限数,还是向外舍入到Infinity。无论哪种情况,都将发出InexactRounded的 signal。

  • 类别 decimal. Rounded
    • 尽管可能没有丢失任何信息,但仍进行了四舍五入。

当舍入舍弃数字时发出 signal;即使这些数字为零(例如将5.00舍入到5.0)。如果未捕获,则返回结果不变。该 signal 用于检测有效数字的丢失。

  • 类别 decimal. Subnormal
    • 四舍五入之前,指数低于Emin

当运算结果不正常(指数太小)时发生。如果未捕获,则返回结果不变。

  • 类别 decimal. Underflow
    • 数字下溢,结果四舍五入为零。

pass舍入将非正规结果推为零时发生。还会发出InexactSubnormal的 signal。

下表总结了 signal 的层次结构:

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

9.4.5. 浮点 Comments

9.4.5.1. 以更高的精度缓解舍入误差

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

舍入误差的影响可以pass相加或相减几乎抵消的量来放大,从而导致重要性下降。 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模块可以pass充分扩展精度来避免丢失重要性,从而恢复身份:

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

9.4.5.2. 特殊值

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

无限性可以直接用Decimal('Infinity')构造。同样,当未捕获DivisionByZerosignal 时,它们可能由零除而产生。同样,当未捕获Overflowsignal 时,四舍五入可能会超出最大可表示数的限制。

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

某些操作不确定并返回NaN,或者如果捕获了InvalidOperationsignal,则引发异常。例如,0/0返回NaN,表示“不是数字”。 NaN的这种变化是安静的,一旦创建,它将流经其他计算,总是产生另一个NaN。此行为对于偶尔缺少 Importing 的一系列计算很有用-它允许在将特定结果标记为无效的同时 continue 进行计算。

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

当涉及到NaN时,Python 比较运算符的行为可能会有些令人惊讶。其中一个操作数为安静或发 signalNaN的相等性测试始终返回False(即使执行Decimal('NaN')==Decimal('NaN')时),而不相等性的测试始终返回True。如果任一操作数是NaN,则try使用<<=>>=中的任何一个来比较两个十进制数都会提高InvalidOperationsignal,如果未捕获此 signal 则返回False。请注意,“通用十进制算术”规范未指定直接比较的行为。这些涉及NaN的比较规则取自 IEEE 854 标准(请参见 5.7 节中的表 3)。为确保严格遵守标准,请改用compare()compare-signal()方法。

有符号的零可能来自下溢的计算。他们保留了如果以更高的精度进行计算会导致的结果。由于其大小为零,因此正零和负零均被视为相等,并且其符号具有参考意义。

除了两个不同但相等的有符号零外,还有各种表示形式的零,它们的精度不同,但值相等。这需要一点时间来适应。对于习惯于标准化浮点表示的眼睛而言,以下计算返回的值为零并不立即显而易见:

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

9.4.6. 使用线程

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

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

如果未在getcontext()之前调用setcontext(),则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()
 . . .

9.4.7. Recipes

以下是一些用作 Util Function的方法,并说明了使用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 = map(str, digits)
    build, next = result.append, digits.pop
    if sign:
        build(trailneg)
    for i in range(places):
        build(next() if digits else '0')
    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.

    >>> 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.

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

9.4.8. 十进制常见问题解答

问:键入decimal.Decimal('1234.5')很麻烦。使用交互式解释器时,有没有一种方法可以减少 typing?

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

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

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

答: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

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

答:一些操作,例如加,减和乘以整数将自动保留定点。其他运算(例如除法和非整数乘法)将更改小数位数,并且需要跟上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都以不同的精度具有相同的值。有没有办法将它们转换为单个可识别的规范值?

答:normalize()方法将所有等效值 Map 到单个代表:

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

问:一些十进制值始终以指数表示法打印。有没有一种方法可以获取非指数表示形式?

答:对于某些值,指数表示法是表达系数中有效位数的唯一方法。例如,将5.0E+3表示为5000可使值保持不变,但不能显示原件的两位数含义。

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

def remove_exponent(d):
    '''Remove exponent and trailing zeros.

    >>> remove_exponent(Decimal('5E+3'))
    Decimal('5000')

    '''
    return d.quantize(Decimal(1)) if d == d.to_integral() else d.normalize()

问:是否可以将常规浮点数转换为Decimal

答:是的,尽管精确转换可能比直觉表明的精度更高,但是任何二进制浮点数都可以精确地表示为十进制。

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

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

答:十进制模块使测试结果变得容易。最佳实践是使用更高的精度和各种舍入模式重新运行计算。差异很大的结果表明精度不足,舍入模式问题,Importing 条件不良或数值不稳定的算法。

问:我注意到上下文精度应用于运算结果,而不应用于 Importing。混合不同精度的值时有什么需要注意的吗?

答:是的。原理是所有值都被认为是精确的,这些值的算术也是如此。仅将结果四舍五入。Importing 的好处是“您 Importing 的就是您得到的”。缺点是,如果您忘记了 Importing 没有四舍五入的结果,结果可能看起来很奇怪:

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

解决方案是使用一元加号操作提高精度或强制舍入 Importing:

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

另外,也可以使用Context.create_decimal()方法对 Importing 进行四舍五入:

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