9.4. decimal — 十进制定点和浮点运算

2.4 新版功能.

The decimal module provides support for decimal floating point arithmetic. It offers several advantages over the float datatype:

  • Decimal 类型的“设计是基于考虑人类习惯的浮点数模型,并且因此具有以下最高指导原则 —— 计算机必须提供与人们在学校所学习的算术相一致的算术。” —— 摘自 decimal 算术规范描述。

  • Decimal numbers can be represented exactly. In contrast, numbers like 1.1 and 2.2 do not have exact representations in binary floating point. End users typically would not expect 1.1 + 2.2 to display as 3.3000000000000003 as it does with binary floating point.

  • 精确性会延续到算术类操作中。 对于 decimal 浮点数,0.1 + 0.1 + 0.1 - 0.3 会精确地等于零。 而对于二进制浮点数,结果则为 5.5511151231257827e-017 。 虽然接近于零,但其中的误差将妨碍可靠的相等性检验,并且误差还会不断累积。 因此,decimal 更适合具有严格相等不变性要求的会计类应用。

  • decimal 模块包含了有效位的概念,使得 1.30 + 1.202.50 。 保留尾随零以表示有效位。 这是货币类应用的习惯表示法。 对于乘法,“教科书”方式使用被乘数中的所有数位。 例如, 1.3 * 1.2 给出 1.561.30 * 1.20 给出 1.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')
    
  • 二进制和 decimal 浮点数都是根据已发布的标准实现的。 虽然内置浮点类型只公开其功能的一小部分,但 decimal 模块公开了标准的所有必需部分。 在需要时,程序员可以完全控制舍入和信号处理。 这包括通过使用异常来阻止任何不精确操作来强制执行精确算术的选项。

  • decimal 模块旨在支持“无偏差,精确无舍入的十进制算术(有时称为定点数算术)和有舍入的浮点数算术”。 —— 摘自 decimal 算术规范说明。

该模块的设计以三个概念为中心:decimal 数值,算术上下文和信号。

decimal 数值是不可变对象。 它由符号,系数和指数位组成。 为了保持有效位,系数位不会截去末尾零。 decimal 数值也包括特殊值例如 Infinity-InfinityNaN 。 该标准还区分 -0+0

算术的上下文是指定精度、舍入规则、指数限制、指示操作结果的标志以及确定符号是否被视为异常的陷阱启用器的环境。 舍入选项包括 ROUND_CEILINGROUND_DOWNROUND_FLOORROUND_HALF_DOWN, ROUND_HALF_EVENROUND_HALF_UPROUND_UP 以及 ROUND_05UP.

Signals are groups of exceptional conditions arising during the course of computation. Depending on the needs of the application, signals may be ignored, considered as informational, or treated as exceptions. The signals in the decimal module are: Clamped, InvalidOperation, DivisionByZero, Inexact, Rounded, Subnormal, Overflow, and Underflow.

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

参见

9.4.1. 快速入门教程

通常使用 decimal 的方式是先导入该模块,通过 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

Decimal instances can be constructed from integers, strings, floats, or tuples. Construction from an integer or a float performs an exact conversion of the value of that integer or float. Decimal numbers include special values such as NaN which stands for “Not a number”, positive and negative Infinity, and -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')

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

Decimal 数字能很好地与 Python 的其余部分交互。 以下是一个小小的 decimal 浮点数飞行马戏团:

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

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 模块提供了两个现成的标准上下文 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

上下文还具有用于监视计算期间遇到的异常情况的信号标志。 标志保持设置直到明确清除,因此最好通过使用 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. Decimal 对象

class decimal.Decimal([value[, context]])

根据 value 构造一个新的 Decimal 对象。

value can be an integer, string, tuple, float, or another Decimal object. If no value is given, returns Decimal('0'). If value is a string, it should conform to the decimal numeric string syntax after leading and trailing whitespace characters are removed:

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

If value is a unicode string then other Unicode decimal digits are also permitted where digit appears above. These include decimal digits from various other alphabets (for example, Arabic-Indic and Devanāgarī digits) along with the fullwidth digits u'\uff10' through u'\uff19'.

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

如果 valuefloat ,则二进制浮点值无损地转换为其精确的十进制等效值。 此转换通常需要53位或更多位数的精度。 例如, Decimal(float('1.1')) 转换为``Decimal(‘1.100000000000000088817841970012523233890533447265625’)``。

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

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

构造完成后, Decimal 对象是不可变的。

在 2.6 版更改: leading and trailing whitespace characters are permitted when creating a Decimal instance from a string.

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

Decimal floating point objects share many properties with the other built-in numeric types such as float and int. All of the usual math operations and special methods apply. Likewise, decimal objects can be copied, pickled, printed, used as dictionary keys, used as set elements, compared, sorted, and coerced to another type (such as float or long).

算术对十进制对象和算术对整数和浮点数有一些小的差别。 当余数运算符 % 应用于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')

%// 运算符实现了 remainderdivide-integer 操作(分别),如规范中所述。

Decimal objects cannot generally be combined with floats in arithmetic operations: an attempt to add a Decimal to a float, for example, will raise a TypeError. There’s one exception to this rule: it’s possible to use Python’s comparison operators to compare a float instance x with a Decimal instance y. Without this exception, comparisons between Decimal and float instances would follow the general rules for comparing objects of different types described in the 表达式 section of the reference manual, leading to confusing results.

在 2.7 版更改: A comparison between a float instance x and a Decimal instance y now returns a result based on the values of x and y. In earlier versions x < y returned the same (arbitrary) result for any Decimal instance x and any float instance y.

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

adjusted()

在移出系数最右边的数字之后返回调整后的指数,直到只剩下前导数字:Decimal('321e+5').adjusted() 返回 7 。 用于确定最高有效位相对于小数点的位置。

as_tuple()

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

在 2.6 版更改: Use a named tuple.

canonical()

返回参数的规范编码。 目前,一个 Decimal 实例的编码始终是规范的,因此该操作返回其参数不变。

2.6 新版功能.

compare(other[, context])

Compare the values of two Decimal instances. This operation behaves in the same way as the usual comparison method __cmp__(), except that compare() returns a Decimal instance rather than an integer, and if either operand is a NaN then the result is a 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])

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

2.6 新版功能.

compare_total(other)

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

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

静默和发出信号的 NaN 也包括在总排序中。 这个函数的结果是 Decimal('0') 如果两个操作数具有相同的表示,或是 Decimal('-1') 如果第一个操作数的总顺序低于第二个操作数,或是 Decimal('1') 如果第一个操作数在总顺序中高于第二个操作数。 有关总排序的详细信息,请参阅规范。

2.6 新版功能.

compare_total_mag(other)

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

2.6 新版功能.

conjugate()

只返回self,这种方法只符合 Decimal 规范。

2.6 新版功能.

copy_abs()

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

2.6 新版功能.

copy_negate()

回到参数的否定。 此操作不受上下文影响并且是静默的:没有标志更改且不执行舍入。

2.6 新版功能.

copy_sign(other)

返回第一个操作数的副本,其符号设置为与第二个操作数的符号相同。 例如:

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

This operation is unaffected by the context and is quiet: no flags are changed and no rounding is performed.

2.6 新版功能.

exp([context])

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

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

2.6 新版功能.

from_float(f)

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

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

注解

From Python 2.7 onwards, a Decimal instance can also be constructed directly from a 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')

2.7 新版功能.

fma(other, third[, context])

混合乘法加法。 返回 self*other+third ,中间乘积 self*other 没有四舍五入。

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

2.6 新版功能.

is_canonical()

如果参数是规范的,则为返回 True,否则为 False 。 目前,Decimal 实例总是规范的,所以这个操作总是返回 True

2.6 新版功能.

is_finite()

如果参数是一个有限的数,则返回为 True ;如果参数为无穷大或 NaN ,则返回为 False

2.6 新版功能.

is_infinite()

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

2.6 新版功能.

is_nan()

如果参数为 NaN (无论是否静默),则返回为 True ,否则为 False

2.6 新版功能.

is_normal()

Return True if the argument is a normal finite non-zero number with an adjusted exponent greater than or equal to Emin. Return False if the argument is zero, subnormal, infinite or a NaN. Note, the term normal is used here in a different sense with the normalize() method which is used to create canonical values.

2.6 新版功能.

is_qnan()

如果参数为静默 NaN,返回 True,否则返回 False

2.6 新版功能.

is_signed()

如果参数带有负号,则返回为 True,否则返回 False。注意,0 和 NaN 都可带有符号。

2.6 新版功能.

is_snan()

如果参数为显式 NaN,则返回 True,否则返回 False

2.6 新版功能.

is_subnormal()

Return True if the argument is subnormal, and False otherwise. A number is subnormal is if it is nonzero, finite, and has an adjusted exponent less than Emin.

2.6 新版功能.

is_zero()

如果参数是0(正负皆可),则返回 True,否则返回 False

2.6 新版功能.

ln([context])

返回操作数的自然对数(以 e 为底)。结果是使用 ROUND_HALF_EVEN 舍入模式正确四舍五入的。

2.6 新版功能.

log10([context])

返回操作数的以十为底的对数。结果是使用 ROUND_HALF_EVEN 舍入模式正确四舍五入的。

2.6 新版功能.

logb([context])

对于一个非零数,返回其运算数的调整后指数作为一个 Decimal 实例。 如果运算数为零将返回 Decimal('-Infinity') 并且产生 the DivisionByZero 标志。如果运算数是无限大则返回 Decimal('Infinity')

2.6 新版功能.

logical_and(other[, context])

logical_and() 是需要两个 逻辑运算数 的逻辑运算(参考 逻辑操作数 )。结果是按位输出的两运算数的 “和”。

2.6 新版功能.

logical_invert([context])

logical_invert() 是一个逻辑运算。 结果是按位的倒转的运算数。

2.6 新版功能.

logical_or(other[, context])

logical_or() 是需要两个 logical operands 的逻辑运算(请参阅 逻辑操作数 )。结果是两个运算数的按位的 or

2.6 新版功能.

logical_xor(other[, context])

logical_xor() 是需要两个 逻辑运算数 的逻辑运算(参考 逻辑操作数 )。结果是按位输出的两运算数的异或运算。

2.6 新版功能.

max(other[, context])

max(self, other) 一样,除了在返回之前应用上下文舍入规则并且用信号通知或忽略 NaN 值(取决于上下文以及它们是发信号还是安静)。

max_mag(other[, context])

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

2.6 新版功能.

min(other[, context])

min(self, other) 一样,除了在返回之前应用上下文舍入规则并且用信号通知或忽略 NaN 值(取决于上下文以及它们是发信号还是安静)。

min_mag(other[, context])

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

2.6 新版功能.

next_minus([context])

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

2.6 新版功能.

next_plus([context])

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

2.6 新版功能.

next_toward(other[, context])

如果两运算数不相等,返回在第二个操作数的方向上最接近第一个操作数的数。如果两操作数数值上相等,返回将符号设置为与第二个运算数相同的第一个运算数的拷贝。

2.6 新版功能.

normalize([context])

通过去除尾随的零并将所有结果等于 Decimal('0') 的转化为 Decimal('0e0') 来标准化数字。用于为等效类的属性生成规范值。比如, Decimal('32.100')Decimal('0.321000e+2') 都被标准化为相同的值 Decimal('32.1')

number_class([context])

返回一个字符串描述运算数的 class 。返回值是以下十个字符串中的一个。

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

  • "-Normal" ,指示该操作数是负正常数字。

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

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

  • "-Zero" ,指示该操作数是正零。

  • "+Subnormal" ,指示该操作数是正的次正规数。

  • "+Normal" ,指示该操作数是正的正规数。

  • "+Infinity" ,指示该运算数是正无穷。

  • "NaN" ,指示该运算数是沉寂的 NaN (非数字)。

  • "sNaN" ,指示该运算数是信号 NaN 。

2.6 新版功能.

quantize(exp[, rounding[, context[, watchexp]]])

返回的值等于四舍五入的第一个运算数并且具有第二个操作数的指数。

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

与其他运算不同,如果量化运算后的系数长度大于精度,那么会发出一个 InvalidOperation 信号。这保证了除非有一个错误情况,量化指数恒等于右手运算数的指数。

与其他运算不同,量化永不信号下溢,即使结果不正常且不精确。

如果第二个运算数的指数大于第一个运算数的指数那或许需要四舍五入。在这种情况下,舍入模式由给定 rounding 参数决定,其余的由给定 context 参数决定;如果参数都未给定,使用当前线程上下文的舍入模式。

If watchexp is set (default), then an error is returned whenever the resulting exponent is greater than Emax or less than Etiny.

radix()

返回 Decimal(10),即 Decimal 类进行所有算术运算所用的数制(基数)。 这是为保持与规范描述的兼容性而加入的。

2.6 新版功能.

remainder_near(other[, context])

返回 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])

返回对第一个操作数的数码按第二个操作数所指定的数量进行轮转的结果。 第二个操作数必须为 -precision 至 precision 精度范围内的整数。 第二个操作数的绝对值给出要轮转的位数。 如果第二个操作数为正值则向左轮转;否则向右轮转。 如有必要第一个操作数的系数会在左侧填充零以达到 precision 所指定的长度。 第一个操作数的符号和指数保持不变。

2.6 新版功能.

same_quantum(other[, context])

检测自身与 other 是否具有相同的指数或是否均为 NaN

scaleb(other[, context])

返回第一个操作数使用第二个操作数对指数进行调整的结果。 等价于返回第一个操作数乘以 10**other 的结果。 第二个操作数必须为整数。

2.6 新版功能.

shift(other[, context])

返回第一个操作数的数码按第二个操作数所指定的数量进行移位的结果。 第二个操作数必须为 -precision 至 precision 范围内的整数。 第二个操作数的绝对值给出要移动的位数。 如果第二个操作数为正值则向左移位;否则向右移位。 移入系数的数码为零。 第一个操作数的符号和指数保持不变。

2.6 新版功能.

sqrt([context])

返回参数的平方根精确到完整精度。

to_eng_string([context])

转换为字符串,如果需要指数则会使用工程标注法。

工程标注法的指数是 3 的倍数。 这会在十进制位的左边保留至多 3 个数码,并可能要求添加一至两个末尾零。

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

to_integral([rounding[, context]])

to_integral_value() 方法相同。 保留 to_integral 名称是为了与旧版本兼容。

to_integral_exact([rounding[, context]])

舍入到最接近的整数,发出信号 Inexact 或者如果发生舍入则相应地发出信号 Rounded。 如果给出 rounding 形参则由其确定舍入模式,否则由给定的 context 来确定。 如果没有给定任何形参则会使用当前上下文的舍入模式。

2.6 新版功能.

to_integral_value([rounding[, context]])

舍入到最接近的整数而不发出 InexactRounded 信号。 如果给出 rounding 则会应用其所指定的舍入模式;否则使用所提供的 context 或当前上下文的舍入方法。

在 2.6 版更改: renamed from to_integral to to_integral_value. The old name remains valid for compatibility.

9.4.2.1. 逻辑操作数

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

9.4.3. Context 对象

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

每个线程都有自己的当前上下文,可使用 getcontext()setcontext() 函数来读取或修改:

decimal.getcontext()

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

decimal.setcontext(c)

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

Beginning with Python 2.5, you can also use the with statement and the localcontext() function to temporarily change the active context.

decimal.localcontext([c])

Return a context manager that will set the current context for the active thread to a copy of c on entry to the with-statement and restore the previous context when exiting the with-statement. If no context is specified, a copy of the current context is used.

2.5 新版功能.

例如,以下代码会将当前 decimal 精度设为 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 构造器来创建。 此外,模块还提供了三种预设的上下文:

class decimal.BasicContext

这是由通用十进制算术规范描述所定义的标准上下文。 精度设为九。 舍入设为 ROUND_HALF_UP。 清除所有旗标。 启用所有陷阱(视为异常),但 Inexact, RoundedSubnormal 除外。

由于启用了许多陷阱,此上下文适用于进行调试。

class decimal.ExtendedContext

这是由通用十进制算术规范描述所定义的标准上下文。 精度设为九。 舍入设为 ROUND_HALF_EVEN。 清除所有旗标。 不启用任何陷阱(因此在计算期间不会引发异常)。

由于禁用了陷阱,此上下文适用于希望结果值为 NaNInfinity 而不是引发异常的应用。 这允许应用在出现当其他情况下会中止程序的条件时仍能完成运行。

class decimal.DefaultContext

此上下文被 Context 构造器用作新上下文的原型。 改变一个字段(例如精度)的效果将是改变 Context 构造器所创建的新上下文的默认值。

此上下文最适用于多线程环境。 在线程开始前改变一个字段具有设置全系统默认值的效果。 不推荐在线程开始后改变字段,因为这会要求线程同步避免竞争条件。

在单线程环境中,最好完全不使用此上下文。 而是简单地电显式创建上下文,具体如下所述。

The default values are precision=28, rounding=ROUND_HALF_EVEN, and enabled traps for Overflow, InvalidOperation, and DivisionByZero.

在已提供的三种上下文之外,还可以使用 Context 构造器创建新的上下文。

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

创建一个新上下文。 如果某个字段未指定或为 None,则从 DefaultContext 拷贝默认值。 如果 flags 字段未指定或为 None,则清空所有旗标。

The prec field is a positive integer that sets the precision for arithmetic operations in the context.

The rounding option is one of:

  • ROUND_CEILING (towards Infinity),

  • ROUND_DOWN (towards zero),

  • ROUND_FLOOR (towards -Infinity),

  • ROUND_HALF_DOWN (to nearest with ties going towards zero),

  • ROUND_HALF_EVEN (to nearest with ties going to nearest even integer),

  • ROUND_HALF_UP (to nearest with ties going away from zero), or

  • ROUND_UP (away from zero).

  • ROUND_05UP (away from zero if last digit after rounding towards zero would have been 0 or 5; otherwise towards zero)

trapsflags 字段列出要设置的任何信号。 通常,新上下文应当只设置 traps 而让 flags 为空。

The Emin and Emax fields are integers specifying the outer limits allowable for exponents.

capitals 字段为 01 (默认值)。 如果设为 1,指数将附带打印大写的 E;其他情况则将使用小写的 e: Decimal('6.02e+23')

在 2.6 版更改: The ROUND_05UP rounding mode was added.

The Context class defines several general purpose methods as well as a large number of methods for doing arithmetic directly in a given context. In addition, for each of the Decimal methods described above (with the exception of the adjusted() and as_tuple() methods) there is a corresponding Context method. For example, for a Context instance C and Decimal instance x, C.exp(x) is equivalent to x.exp(context=C). Each Context method accepts a Python integer (an instance of int or long) anywhere that a Decimal instance is accepted.

clear_flags()

将所有旗标重置为 0

copy()

返回上下文的一个副本。

copy_decimal(num)

返回 Decimal 实例 num 的一个副本。

create_decimal(num)

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

This method implements the to-number operation of the IBM specification. If the argument is a string, no leading or trailing whitespace is permitted.

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 新版功能.

Etiny()

返回一个等于 Emin - prec + 1 的值即次正规化结果中的最小指数值。 当发生向下溢出时,指数会设为 Etiny

Etop()

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

使用 decimal 的通常方式是创建 Decimal 实例然后对其应用算术运算,这些运算发生在活动线程的当前上下文中。 一种替代方式则是使用上下文的方法在特定上下文中进行计算。 这些方法类似于 Decimal 类的方法,在此仅简单地重新列出。

abs(x)

返回 x 的绝对值。

add(x, y)

返回 xy 的和。

canonical(x)

返回相同的 Decimal 对象 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)

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)

在操作数的每个数码间应用逻辑运算 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 中的单目前缀取正运算符执行取正操作。 此操作将应用上下文精度和舍入,因此它 不是 标识运算。

power(x, y[, modulo])

返回 xy 次方,如果给出了模数 modulo 则取其余数。

With two arguments, compute x**y. If x is negative then y must be integral. The result will be inexact unless y is integral and the result is finite and can be expressed exactly in ‘precision’ digits. The result should always be correctly rounded, using the rounding mode of the current thread’s context.

带有三个参数时,计算 (x**y) % modulo。 对于三个参数的形式,参数将会应用以下限制:

  • 三个参数必须都是整数

  • y 必须是非负数

  • xy 至少有一个不为零

  • modulo 必须不为零且至多有 ‘precision’ 位

来自 Context.power(x, y, modulo) 的结果值等于使用无限精度计算 (x**y) % modulo 所得到的值,但其计算过程更高效。 结果的指数为零,无论 x, ymodulo 的指数是多少。 结果值总是完全精确的。

在 2.6 版更改: y may now be nonintegral in x**y. Stricter requirements for the three-argument version.

quantize(x, y)

返回的值等于 x (舍入后),并且指数为 y

radix()

恰好返回 10,因为这是 Decimal 对象 :)

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)

返回第一个操作数对第二个值添加其指数后的结果。

shift(x, y)

返回 x 变换 y 次的副本。

sqrt(x)

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

subtract(x, y)

返回 xy 的差。

to_eng_string(x)

转换为字符串,如果需要指数则会使用工程标注法。

工程标注法的指数是 3 的倍数。 这会在十进制位的左边保留至多 3 个数码,并可能要求添加一至两个末尾零。

to_integral_exact(x)

舍入到一个整数。

to_sci_string(x)

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

9.4.4. 信号

信号代表在计算期间引发的条件。 每个信号对应于一个上下文旗标和一个上下文陷阱启用器。

上下文旗标将在遇到特定条件时被设定。 在完成计算之后,将为了获得信息而检测旗标(例如确定计算是否精确)。 在检测旗标后,请确保在开始下一次计算之前清除所有旗标。

如果为信号设定了上下文的陷阱启用器,则条件会导致特定的 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
x._rescale( non-integer )
sqrt(-x) and x > 0
0 ** 0
x ** (non-integer)
x ** Infinity
class decimal.Overflow

数值的溢出。

表明在发生舍入之后的指数大于 Emax。 如果未被陷阱捕获,则结果将取决于舍入模式,或者向下舍入为最大的可表示有限数,或者向上舍入为 Infinity。 无论哪种情况,都将引发 InexactRounded 信号。

class decimal.Rounded

发生了舍入,但或许并没有信息丢失。

一旦舍入丢弃了数位就会发出此信号;即使被丢弃的数位是零 (例如将 5.00 舍入为 5.0)。 如果未被陷阱捕获,则不经修改地返回结果。 此信号用于检测有效位数的丢弃。

class decimal.Subnormal

在舍入之前指数低于 Emin

当操作结果是次标准数(即指数过小)时就会发出此信号。 如果未被陷阱捕获,则不经修改过返回结果。

class decimal.Underflow

数字向下溢出导致结果舍入到零。

当一个次标准数结果通过舍入转为零时就会发出此信号。 同时还将引发 InexactSubnormal 信号。

以下表格总结了信号的层级结构:

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

9.4.5. 浮点数说明

9.4.5.1. 通过提升精度来缓解舍入误差

使用十进制浮点数可以消除十进制表示错误(即能够完全精确地表示 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')

9.4.5.2. 特殊的值

decimal 模块的数字系统提供了一些特殊的值,包括 NaN, sNaN, -Infinity, Infinity 以及两种零值 +0-0

无穷大可以使用 Decimal('Infinity') 来构建。 它们也可以在不捕获 DivisionByZero 信号捕获时通过除以零来产生。 类似地,当不捕获 Overflow 信号时,也可以通过舍入到超出最大可表示数字限制的方式产生无穷大的结果。

无穷大是有符号的(仿射)并可用于算术运算,它们会被当作极其巨大的不确定数字来处理。 例如,无穷大加一个常量结果也将为无穷大。

某些不存在有效结果的运算将会返回 NaN,或者如果捕获了 InvalidOperation 信号则会引发一个异常。 例如,0/0 会返回 NaN 表示结果“不是一个数字”。 这样的 NaN 是静默产生的,并且在产生之后参与其它计算时总是会得到 NaN 的结果。 这种行为对于偶而缺少输入的各类计算都很有用处 — 它允许在将特定结果标记为无效的同时让计算继续运行。

另一种变体形式是 sNaN,它在每次运算后会发出信号而不是保持静默。 当对于无效结果需要中断计算进行特别处理时,这是一个很有用的返回值。

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

有符号零值可以由向下溢出的运算产生。 它们保留符号是为了让运算结果能以更高的精度传递。 由于它们的大小为零,正零和负零会被视为相等,且它们的符号具有信息。

在这两个不相同但却相等的有符号零之外,还存在几种零的不同表示形式,它们的精度不同但值也都相等。 这需要一些时间来逐渐适应。 对于习惯了标准浮点表示形式的眼睛来说,以下运算返回等于零的值并不是显而易见的:

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

9.4.6. 使用线程

The getcontext() function accesses a different Context object for each thread. Having separate thread contexts means that threads may make changes (such as getcontext.prec=10) without interfering with other threads.

类似的 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()
 . . .

9.4.7. 例程

以下是一些用作工具函数的例程,它们演示了使用 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 FAQ

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

Q. 当我使用两个有效位的输入时,我要如何在一个应用中保持有效位不变?

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.000, 2E202E+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')]

Q. 有些十进制值总是被打印为指数表示形式。 是否有办法得到一个非指数表示形式?

A. 对于某些值来说,指数表示形式是表示系数中有效位的唯一办法。 例如,将 5.0E+3 表示为 5000 可以让值保持恒定,但是无法显示原本的两位有效数字。

If an application does not care about tracking significance, it is easy to remove the exponent and trailing zeros, losing significance, but keeping the value unchanged:

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

Q. 是否有办法将一个普通浮点数转换为 Decimal

A. 是的,任何二进制浮点数都可以精确地表示为 Decimal 值,但精确的转换可能需要比直觉设想更高的精度:

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

Q. 在一个复杂的计算中,我怎样才能保证不会得到由精度不足和舍入异常所导致的虚假结果。

A. 使用 decimal 模块可以很容易地检测结果。 最好的做法是使用更高的精度和不同的舍入模式重新进行计算。 明显不同的结果表明存在精度不足、舍入模式问题、不符合条件的输入或是结果不稳定的算法。

Q. 我发现上下文精度的应用只针对运算结果而不针对输入。在混合使用不同精度的值时有什么需要注意的吗?

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