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

源代码: Lib / decimal.py

decimal模块支持快速正确舍入的十进制浮点运算。它提供了优于float数据类型的几个优点:

  • 十进制“是基于一个浮点模型,其设计考虑了人们,并且必然有一个最重要的指导原则 - 计算机必须提供一个算术,其工作方式与人们在学校学习的算法相同。” - 摘录自十进制算术规范。

  • 十进制数可以精确表示。相反,诸如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尾随零被保留以指示重要性。这是货币应用的习惯表述。对于乘法,“教科书”方法使用被乘数中的所有数字。For instance, 1.3 * 1.2 gives 1.56 while 1.30 * 1.20 gives 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

信号是在计算过程中出现的一组异常条件。根据应用程序的需要,信号可能会被忽略,被视为信息,或被视为异常。The signals in the decimal module are: Clamped, InvalidOperation, DivisionByZero, Inexact, Rounded, Subnormal, Overflow, Underflow and FloatOperation.

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

也可以看看

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

上下文还具有用于监视计算期间遇到的异常条件的信号标志。标志保持置1直到被明确清零,因此最好使用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数值类型。

9.4.2.小数对象

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

根据构造新的Decimal对象。

value可以是整数,字符串,元组,float或另一个Decimal对象。如果未给出,则返回Decimal('0')如果value是字符串,它应该符合十进制数字字符串语法,前导和尾随空格字符删除后:

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

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

If value is a tuple, it should have three components, a sign (0 for positive or 1 for negative), a tuple of digits, and an integer exponent. 例如,十进制((0, (1, 4, 1, 4 ), -3))返回Decimal('1.414')

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

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

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

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

在版本3.2中更改:现在允许构造函数的参数为​​float实例。

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

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

在十进制对象上的算术和整数和浮点上的算术之间存在一些小的差异。当余数操作符%应用于十进制对象时,结果的符号是被除数的符号,而不是除数的符号:

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

The integer division operator // behaves analogously, returning the integer part of the true quotient (truncating towards zero) rather than its floor, so as to preserve the usual identity x == (x // y) * y + x % y:

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

%//运算符分别实现了规范中描述的remainderdivide-integer

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

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

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

adjusted()

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

as_tuple()

返回named tuple表示形式:DecimalTuple(sign, digits, exponent) t2>。

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也包括在总排序中。The result of this function is Decimal('0') if both operands have the same representation, Decimal('-1') if the first operand is lower in the total order than the second, and Decimal('1') if the first operand is higher in the total order than the second operand. 有关总订单的详细信息,请参阅规格。

此操作不受上下文影响,并且是安静的:不更改标志,不执行舍入。作为例外,如果第二操作数不能被精确地转换,则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)

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

注意Decimal.from_float(0.1)十进制('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)

融合乘法。返回自我*其他+第三,没有舍入的中间产品self * other。

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

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

is_finite()

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

is_infinite()

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

is_nan()

如果参数为(安静或信令)NaN且False,则返回True

is_normal(context=None)

如果参数是正常有限数,则返回True如果参数为零,子正常,无限或NaN,则返回False

is_qnan()

如果参数是安静的NaN,则返回True,否则返回False

is_signed()

如果参数具有负号,则返回True,否则返回False注意零和NaN都可以携带迹象。

is_snan()

如果参数是信号NaN,则返回True,否则返回False

is_subnormal(context=None)

如果参数为subnormal,则返回True,否则返回False

is_zero()

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

ln(context=None)

返回操作数的自然(基本e)对数。结果使用ROUND_HALF_EVEN舍入模式正确舍入。

log10(context=None)

返回操作数的十进制对数。结果使用ROUND_HALF_EVEN舍入模式正确舍入。

logb(context=None)

对于非零数字,将其操作数的经调整的指数作为Decimal实例返回。如果操作数为零,则返回Decimal('-Infinity'),并且提高DivisionByZero标志。如果操作数是无穷大,则返回Decimal('Infinity')

logical_and(other, context=None)

logical_and()是采用两个逻辑操作数(参见Logical operands)的逻辑操作。结果是两个操作数的数字方式and

logical_invert(context=None)

logical_invert()是一个逻辑操作。结果是操作数的数字反转。

logical_or(other, context=None)

logical_or()是一个逻辑操作,它需要两个逻辑操作数(见Logical operands)。结果是两个操作数的数字方式or

logical_xor(other, context=None)

logical_xor()是采用两个逻辑操作数(参见Logical operands)的逻辑操作。结果是数字方式互斥或两个操作数。

max(other, context=None)

max(self, other)除了在返回之前应用上下文舍入规则,并且NaN (根据上下文以及它们是信令还是静默)。

max_mag(other, context=None)

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

min(other, context=None)

除了在返回之前应用上下文舍入规则,并且NaN值是之前,除了min(self, other) (根据上下文以及它们是信令还是静默)。

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)

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

  • "-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除以其他返回余数。这不同于self 其他,因为选择余数的符号以便最小化其绝对值。更精确地,返回值是self - n * other 其中n是最接近self / 其他 / t8>,如果两个整数同样接近,则选择偶数。

如果结果为零,则其符号将是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如果给出,则应用舍入;否则,在提供的上下文或当前上下文中使用舍入方法。

9.4.2.1.逻辑运算符

The logical_and(), logical_invert(), logical_or(), and logical_xor() methods expect their arguments to be logical operands. A logical operand is a Decimal instance whose exponent and sign are both zero, and whose digits are all either 0 or 1.

9.4.3.上下文对象

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

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

decimal.getcontext()

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

decimal.setcontext(c)

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

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

decimal.localcontext(ctx=None)

返回上下文管理器,它将在活动线程的当前上下文设置为with语句的条目中的ctx的副本,并在退出with语句时恢复上一个上下文。如果没有指定上下文,则使用当前上下文的副本。

例如,以下代码将当前小数精度设置为42个位置,执行计算,然后自动恢复上一个上下文:

from decimal import localcontext

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

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

class decimal.BasicContext

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

因为许多陷阱是启用的,所以这个上下文对于调试是有用的。

class decimal.ExtendedContext

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

因为陷阱已禁用,所以此上下文对于喜欢使用结果值NaNInfinity而不是提高异常的应用程序非常有用。这允许应用程序在存在否则将停止程序的条件的情况下完成运行。

class decimal.DefaultContext

此上下文由Context构造函数用作新上下文的原型。更改字段(如精度)会更改由Context构造函数创建的新上下文的默认值。

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

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

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

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

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

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

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

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

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

EminEmax字段是指定指数可允许的外部限制的整数。Emin must be in the range [MIN_EMIN, 0], Emax in the range [0, MAX_EMAX].

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

钳位字段为0(默认值)或1如果设置为1,则在本上下文中可表示的Decimal实例的指数e严格地限于范围 - prec + 1 e &lt; = Emax - prec + 1 如果clamp0,则较弱的条件成立:Decimal实例的经调整的指数最多为Emaxclamp1时,在可能的情况下,大的正常数将使其指数减少,并将相应数量的零添加到其系数,以便拟合指数约束;这保留了数字的值,但丢失了关于显着尾随零的信息。例如:

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

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

Context类定义了几个通用方法以及大量直接在给定上下文中进行算术的方法。此外,对于上述Decimal方法中的每一个(除了adjusted()as_tuple()方法)对应的Context方法。例如,对于Context实例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 t0>其是用于子正规结果的最小指数值。当发生下溢时,指数设置为Etiny

Etop()

返回等于Emax - prec + 1 t0>。

使用小数的通常方法是创建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)

将符号从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)

返回True if x is canonical;否则返回False

is_finite(x)

返回True if x是有限的;否则返回False

is_infinite(x)

如果x为无穷大,则返回True否则返回False

is_nan(x)

如果x是qNaN或sNaN,则返回True;否则返回False

is_normal(x)

如果x是正常数字,则返回True;否则返回False

is_qnan(x)

返回True if x是一个安静的NaN;否则返回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)

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

min(x, y)

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

min_mag(x, y)

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

minus(x)

Minus对应于Python中的一元前缀减操作符。

multiply(x, y)

返回xy的乘积。

next_minus(x)

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

next_plus(x)

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

next_toward(x, y)

y的方向返回最接近x的数字。

normalize(x)

x缩减为其最简单的形式。

number_class(x)

返回x类的指示。

plus(x)

Plus对应于Python中的一元前缀加操作符号。此操作应用上下文精度和舍入,因此不是身份操作。

power(x, y, modulo=None)

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

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

Changed in version 3.3: The C module computes power() in terms of the correctly-rounded exp() and ln() functions. 结果是明确的,但只有“几乎总是正确圆润”。

有三个参数,计算(x ** y) 对于三个参数形式,对参数的以下限制:

  • all three arguments must be integral
  • y must be nonnegative
  • at least one of x or y must be nonzero
  • modulo must be nonzero and have at most ‘precision’ digits

Context.power(x, y, 模)产生的值等于通过以无界精度计算(x ** y) 获得,但是被更有效地计算。无论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)

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

9.4.4.常量

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

32位64位
decimal.MAX_PREC
425000000999999999999999999
decimal.MAX_EMAX
425000000999999999999999999
decimal.MIN_EMIN
-425000000-999999999999999999
decimal.MIN_ETINY
-849999999-1999999999999999997
decimal.HAVE_THREADS

默认值为True如果Python编译没有线程,C版本自动禁用昂贵的线程本地上下文机制。在这种情况下,值为False

9.4.5.舍入模式

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,则从零舍入;否则舍入到零。

9.4.6.信号

信号表示计算期间出现的条件。每个对应于一个上下文标志和一个上下文陷阱使能器。

只要遇到条件,就设置上下文标志。在计算之后,可以检查标志用于信息目的(对于实例,确定计算是否是精确的)。检查标志后,请务必在开始下一个计算之前清除所有标志。

如果为信号设置了上下文的陷阱启用程序,则该条件会导致引发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)

9.4.7.浮点数注意事项

9.4.7.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.7.2.特殊值

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')),而不等式的检验总是返回TrueAn attempt to compare two Decimals using any of the <, <=, > or >= operators will raise the InvalidOperation signal if either operand is a NaN, and return False if this signal is not trapped. 注意,一般小数算术规范没有规定直接比较的行为;这些涉及a NaN的比较规则取自IEEE 854标准(见第5.7节中的表3)。为了确保严格遵守标准,请改用compare()compare-signal()方法。

带符号的零可以由下溢的计算产生。如果计算进行得更精确,它们保留了将会产生的符号。由于它们的大小为零,正和负零都被视为相等,它们的符号是信息性的。

除了两个有区别但相等的有符号零之外,存在具有不同精度但值等价的零的各种表示。这需要一点点习惯。对于习惯于归一化浮点表示的眼睛,以下计算返回等于零的值不是显而易见的:

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

9.4.8.使用线程

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

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

如果在getcontext()之前未调用setcontext(),则getcontext()会自动创建一个新的上下文, 。

新上下文从称为DefaultContext的原型上下文复制。要控制默认值,以使每个线程在整个应用程序中使用相同的值,请直接修改DefaultContext对象。This should be done before any threads are started so that there won’t be a race condition between threads calling 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.9.食谱

这里有几个配方作为效用函数,演示了如何使用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

9.4.10.十进制常见问题

Q.输入decimal.Decimal('1234.5')很麻烦。有使用交互式解释器最小化打字的方法吗?

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

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

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

一个。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.一旦我有有效的两个地方输入,我如何在整个应用程序中保持不变量?

一个。一些操作,如加,减和乘以一个整数将自动保留固定点。其他操作,如除法和非整数乘法,将改变小数位数,并需要跟随一个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.有很多方法来表达相同的价值。在各种精度下,数字200200.0002E202E+4都具有相同的值。有没有办法将它们转换为单一的可识别的规范值?

一个。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.一些十进制值始终以指数符号打印。有没有办法得到非指数表示?

一个。对于一些值,指数符号是表达系数中的有效位置的数量的唯一方式。例如,将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')

Q.有没有办法将普通浮点数转换为Decimal

一个。是的,任何二进制浮点数可以精确表示为十进制,虽然精确的转换可能需要比直觉的建议更精确:

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

Q.在复杂的计算中,我如何确保我没有得到一个虚假的结果,因为精度不足或舍入异常。

一个。十进制模块可以方便地测试结果。最佳实践是使用更高精度和使用各种舍入模式重新运行计算。广泛不同的结果表明精度不足,舍入模式问题,病态输入或数值不稳定算法。

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

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

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