9.4. decimal
- 十进制定点和浮点运算¶
源代码: Lib / decimal.py
decimal
模块支持快速正确舍入的十进制浮点运算。它比float
数据类型提供了几个优点:
十进制“是基于一个浮点模型,该模型是以人为本设计的,并且必须有一个最重要的指导原则 - 计算机必须提供一种与人们在学校学习的算术相同的算法。” - 摘自十进制算术规范。
十进制数字可以精确表示。相比之下,像
1.1
和2.2
这样的数字在二进制浮点中没有精确的表示。End users typically would not expect1.1 + 2.2
to display as3.3000000000000003
as it does with binary floating point.精确性被转化为算术。在十进制浮点数中,
0.1 + 0.1 + 0.1 - 0.3
正好等于零。在二进制浮点中,结果是5.5511151231257827e-017
。尽管接近于零,但差异阻止了可靠的平等测试,并且可能会积累差异。由于这个原因,在具有严格的等式不变量的会计应用中,小数是优选的。The decimal module incorporates a notion of significant places so that
1.30 + 1.20
is2.50
. 尾随零保持显示重要性。这是货币应用的惯例。对于乘法,“教科书”方法使用被乘数中的所有数字。For instance,1.3 * 1.2
gives1.56
while1.30 * 1.20
gives1.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类型只展示了它的功能中的一小部分,但十进制模块公开了标准的所有必需部分。在需要时,程序员可以完全控制舍入和信号处理。这包括通过使用例外来阻止任何不精确操作来强制执行精确算术的选项。
十进制模块被设计为支持“没有偏见,精确的未被占用的十进制算术(有时称为定点算术)和四舍五入的浮点算术。” - 摘自十进制算术规范。
模块设计以三个概念为中心:十进制数,算术环境和信号。
十进制数是不可变的。它有一个符号,系数数字和一个指数。为了保留重要性,系数数字不会截断尾随零。Decimals also include special values such as Infinity
, -Infinity
, and NaN
. 该标准还将-0
与+0
区分开来。
算术环境是指定精度的环境,舍入规则,指数限制,指示操作结果的标志,以及确定信号是否被视为例外的陷阱启动器。Rounding options include ROUND_CEILING
, ROUND_DOWN
, ROUND_FLOOR
, ROUND_HALF_DOWN
, ROUND_HALF_EVEN
, ROUND_HALF_UP
, ROUND_UP
, and ROUND_05UP
.
信号是在计算过程中出现的一组特殊情况。根据应用的需要,信号可能被忽略,被视为信息性的,或被视为例外。The signals in the decimal module are: Clamped
, InvalidOperation
, DivisionByZero
, Inexact
, Rounded
, Subnormal
, Overflow
, Underflow
and FloatOperation
.
每个信号都有一个标志和一个陷阱启动器。遇到信号时,其标志被设置为1,然后,如果陷阱启用码设置为1,则会引发异常。标志是粘性的,所以用户在监控计算之前需要重置它们。
也可以看看
- IBM’s General Decimal Arithmetic Specification, The General Decimal Arithmetic Specification.
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
十进制实例可以用整数,字符串,浮点数或元组构造。从整数或浮点构造执行该整数或浮点的值的精确转换。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.4142135623730951')
>>> Decimal(2) ** Decimal('0.5')
Decimal('1.414213562373095048801688724')
>>> Decimal('NaN')
Decimal('NaN')
>>> Decimal('-Infinity')
Decimal('-Infinity')
如果FloatOperation
信号被捕获,则在构造函数中意外混合小数点和浮点数或进行排序比较会引发异常:
>>> c = getcontext()
>>> c.traps[FloatOperation] = True
>>> Decimal(3.14)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
decimal.FloatOperation: [<class 'decimal.FloatOperation'>]
>>> Decimal('3.5') < 3.7
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
decimal.FloatOperation: [<class 'decimal.FloatOperation'>]
>>> Decimal('3.5') == 3.5
True
版本3.3中的新功能。
新Decimal的意义仅由输入的位数决定。上下文精度和舍入仅在算术运算中发挥作用。
>>> getcontext().prec = 6
>>> Decimal('3.0')
Decimal('3.0')
>>> Decimal('3.1415926535')
Decimal('3.1415926535')
>>> Decimal('3.1415926535') + Decimal('2.7182818285')
Decimal('5.85987')
>>> getcontext().rounding = ROUND_UP
>>> Decimal('3.1415926535') + Decimal('2.7182818285')
Decimal('5.85988')
如果C版本的内部限制被超过,构造一个小数将引发InvalidOperation
:
>>> Decimal("1e9999999999999999999")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
decimal.InvalidOperation: [<class 'decimal.InvalidOperation'>]
在版本3.3中更改
小数与Python的其余部分很好地交互。这里有一个小小数浮点飞行马戏团:
>>> data = list(map(Decimal, '1.34 1.87 3.45 2.35 1.00 0.03 9.25'.split()))
>>> max(data)
Decimal('9.25')
>>> min(data)
Decimal('0.03')
>>> sorted(data)
[Decimal('0.03'), Decimal('1.00'), Decimal('1.34'), Decimal('1.87'),
Decimal('2.35'), Decimal('3.45'), Decimal('9.25')]
>>> sum(data)
Decimal('19.29')
>>> a,b,c = data[:3]
>>> str(a)
'1.34'
>>> float(a)
1.34
>>> round(a, 1)
Decimal('1.3')
>>> int(a)
1
>>> a * 5
Decimal('6.70')
>>> a * b
Decimal('2.5058')
>>> c % a
Decimal('0.77')
还有一些数学函数也可用于Decimal:
>>> getcontext().prec = 28
>>> Decimal(2).sqrt()
Decimal('1.414213562373095048801688724')
>>> Decimal(1).exp()
Decimal('2.718281828459045235360287471')
>>> Decimal('10').ln()
Decimal('2.302585092994045684017991455')
>>> Decimal('10').log10()
Decimal('1')
quantize()
方法将数字四舍五入为固定的指数。这种方法对于货币应用程序非常有用,这些应用程序通常会将结果转换为固定数量的地方
>>> Decimal('7.325').quantize(Decimal('.01'), rounding=ROUND_DOWN)
Decimal('7.32')
>>> Decimal('7.325').quantize(Decimal('1.'), rounding=ROUND_UP)
Decimal('8')
如上所示,getcontext()
函数访问当前上下文并允许更改设置。这种方法可以满足大多数应用的需求。
对于更高级的工作,使用Context()构造函数创建备用上下文可能很有用。要使备用活动起作用,请使用setcontext()
函数。
根据标准,decimal
模块提供了两个准备使用的标准上下文:BasicContext
和ExtendedContext
。前者对于调试特别有用,因为许多陷阱都已启用:
>>> myothercontext = Context(prec=60, rounding=ROUND_HALF_DOWN)
>>> setcontext(myothercontext)
>>> Decimal(1) / Decimal(7)
Decimal('0.142857142857142857142857142857142857142857142857142857142857')
>>> ExtendedContext
Context(prec=9, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999,
capitals=1, clamp=0, flags=[], traps=[])
>>> setcontext(ExtendedContext)
>>> Decimal(1) / Decimal(7)
Decimal('0.142857143')
>>> Decimal(42) / Decimal(0)
Decimal('Infinity')
>>> setcontext(BasicContext)
>>> Decimal(42) / Decimal(0)
Traceback (most recent call last):
File "<pyshell#143>", line 1, in -toplevel-
Decimal(42) / Decimal(0)
DivisionByZero: x / 0
上下文还有信号标志用于监控计算过程中遇到的异常情况。这些标志保持设置直到明确清除,因此最好在每组监视计算之前使用clear_flags()
方法清除标志。
>>> setcontext(ExtendedContext)
>>> getcontext().clear_flags()
>>> Decimal(355) / Decimal(113)
Decimal('3.14159292')
>>> getcontext()
Context(prec=9, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999,
capitals=1, clamp=0, flags=[Inexact, Rounded], traps=[])
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
对象。值可以是整数,字符串,元组,
float
或其他Decimal
对象。如果未给出值,则返回Decimal('0')
。如果值是一个字符串,则在删除前导和尾随空白字符后,它应符合十进制数字字符串语法:sign ::= '+' | '-' digit ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' indicator ::= 'e' | 'E' digits ::= digit [digit]... decimal-part ::= digits '.' [digits] | ['.'] digits exponent-part ::= indicator [sign] digits infinity ::= 'Infinity' | 'Inf' nan ::= 'NaN' [digits] | 'sNaN' [digits] numeric-value ::= decimal-part [exponent-part] | infinity numeric-string ::= [sign] numeric-value | [sign] nan
其他Unicode十进制数字也可以在
digit
出现在上面。这些包括来自各种其他字母(例如,阿拉伯 - 印度和德瓦加尔加数字)的十进制数字以及全角数字'\uff10'
到'\uff19'
。If value is a
tuple
, it should have three components, a sign (0
for positive or1
for negative), atuple
of digits, and an integer exponent. For example,Decimal((0, (1, 4, 1, 4), -3))
returnsDecimal('1.414')
.如果值是
float
,则二进制浮点值将无损地转换为精确的十进制等值。这种转换通常需要53位或更多的精度。例如,Decimal(float('1.1'))
转换为Decimal('1.100000000000000088817841970012523233890533447265625')
。上下文精度不影响存储多少位数。这完全取决于值中的位数。For example,
Decimal('3.00000')
records all five zeros even if the context precision is only three.context参数的用途是确定如果值是格式错误的字符串该怎么做。如果上下文陷阱
InvalidOperation
,则会引发异常;否则,构造函数返回一个新的Decimal,其值为NaN
。Once constructed,
Decimal
objects are immutable.在版本3.2中更改:现在允许构造函数的参数为
float
实例。在版本3.3中更改:如果设置了
FloatOperation
陷阱,则float
参数会引发异常。默认情况下,陷阱关闭。十进制浮点对象与其他内置数值类型(如
float
和int
)共享许多属性。所有常用的数学运算和特殊方法都适用。同样,小数对象可以被复制,腌制,打印,用作字典键,用作设置元素,比较,排序和强制到另一种类型(例如float
或int
在小数对象上的算术和整数和浮点数上的算术之间有一些小的差异。当余数运算符
%
应用于小数对象时,结果的符号是除数的符号而不是除数的符号:>>> (-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 identityx == (x // y) * y + x % y
:>>> -7 // 4 -2 >>> Decimal(-7) // Decimal(4) Decimal('-1')
%
和//
运算符分别实现了remainder
和divide-integer
运算。Decimal objects cannot generally be combined with floats or instances of
fractions.Fraction
in arithmetic operations: an attempt to add aDecimal
to afloat
, for example, will raise aTypeError
. 但是,可以使用Python的比较运算符将Decimal
实例x
与另一个数字y
进行比较。这样可以避免在不同类型的数字之间进行相等比较时出现令人困惑的结果。在版本3.2中更改:现在完全支持
Decimal
实例和其他数字类型之间的混合类型比较。除了标准的数字属性外,十进制浮点对象还有许多特殊的方法:
adjusted
()¶移出系数最右边的数字后,返回调整后的指数,直到只剩下前导数字:
Decimal('321e+5').adjusted()
返回7。用于确定最重要数字相对于小数点的位置。
as_tuple
()¶Return a named tuple representation of the number:
DecimalTuple(sign, digits, exponent)
.
compare
(other, context=None)¶比较两个Decimal实例的值。
compare()
返回一个Decimal实例,如果其中一个操作数是NaN,那么结果是NaN:a or b is a NaN ==> Decimal('NaN') a < b ==> Decimal('-1') a == b ==> Decimal('0') a > b ==> Decimal('1')
compare_signal
(other, context=None)¶该操作与
compare()
方法相同,不同之处在于所有的NaN信号。也就是说,如果两个操作数都不是一个NaN信号,那么任何安静的NaN操作数都被视为一个信号NaN。
compare_total
(other, context=None)¶使用它们的抽象表示比较两个操作数,而不是它们的数值。类似于
compare()
方法,但结果给出了Decimal
实例的总体排序。具有相同数值但不同表示的Decimal
实例在此排序中比较不等:>>> Decimal('12.0').compare_total(Decimal('12')) Decimal('-1')
安全和信令NaNs也包括在总的顺序中。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, andDecimal('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
()¶只是返回自我,这种方法只符合十进制规范。
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转换为十进制数的Classmethod。
注意Decimal.from_float(0.1)不同于Decimal('0.1')。由于0.1在二进制浮点中不能完全表示,因此该值将存储为最接近的可表示值,即0x1.999999999999ap-4。That equivalent value in decimal is 0.1000000000000000055511151231257827021181583404541015625.
>>> Decimal.from_float(0.1) Decimal('0.1000000000000000055511151231257827021181583404541015625') >>> Decimal.from_float(float('nan')) Decimal('NaN') >>> Decimal.from_float(float('inf')) Decimal('Infinity') >>> Decimal.from_float(float('-inf')) Decimal('-Infinity')
版本3.1中的新功能。
fma
(other, third, context=None)¶融合乘加。返回自己*其他+第三没有舍去中间产品自己*其他。
>>> Decimal(2).fma(3, 5) Decimal('11')
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()
is a logical operation which takes two logical operands (see Logical operands). 结果是两个操作数的数字方式and
。
logical_invert
(context=None)¶logical_invert()
is a logical operation. 结果是操作数的数字反转。
logical_or
(other, context=None)¶logical_or()
is a logical operation which takes two logical operands (see Logical operands). 结果是两个操作数的数字方式or
。
logical_xor
(other, context=None)¶logical_xor()
is a logical operation which takes two logical operands (see Logical operands). 结果是数字独占或两个操作数。
max
(other, context=None)¶Like
max(self, other)
except that the context rounding rule is applied before returning and thatNaN
values are either signaled or ignored (depending on the context and whether they are signaling or quiet).
min
(other, context=None)¶Like
min(self, other)
except that the context rounding rule is applied before returning and thatNaN
values are either signaled or ignored (depending on the context and whether they are signaling or quiet).
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
,就会返回错误。
remainder_near
(other, context=None)¶将self除以other得到的余数。这与
self % 其他
的不同之处在于,余数的符号被选择为使其绝对值最小化。More precisely, the return value isself - n * other
wheren
is the integer nearest to the exact value ofself / other
, and if two integers are equally near then the even one is chosen.如果结果为零,那么它的符号将是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)¶将第一个操作数的数字旋转一个由第二个操作数指定的量的结果。第二个操作数必须是精度范围-precision中的整数。第二个操作数的绝对值给出了要旋转的地方的数量。如果第二个操作数是正的,那么旋转是在左边;否则旋转是正确的。如有必要,第一个操作数的系数在左侧填充为零以达到长度精度。第一个操作数的符号和指数保持不变。
same_quantum
(other, context=None)¶Test whether self and other have the same exponent or whether both are
NaN
.该操作不受上下文影响,并且安静:没有标志被改变,也没有执行舍入。作为例外,如果第二个操作数无法完全转换,则C版本可能会引发InvalidOperation。
scaleb
(other, context=None)¶返回第一个操作数,指数由第二个调整。等价地,返回第一个操作数乘以
10**other
。第二个操作数必须是整数。
shift
(other, context=None)¶返回将第一个操作数的数位移位第二个操作数指定的数值的结果。第二个操作数必须是精度范围-precision中的整数。第二个操作数的绝对值给出了要移位的位数。如果第二个操作数是正的,那么这个移位是在左边;否则这个转变是在右边。移入系数的数字是零。第一个操作数的符号和指数保持不变。
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
名称与旧版本保持兼容。
9.4.3.上下文对象¶
上下文是算术运算的环境。它们控制精度,设置舍入规则,确定哪些信号被视为例外,并限制指数的范围。
每个线程都有自己的当前上下文,它可以使用getcontext()
和setcontext()
函数进行访问或更改:
decimal.
getcontext
()¶返回活动线程的当前上下文。
decimal.
setcontext
(c)¶将活动线程的当前上下文设置为c。
您还可以使用with
语句和localcontext()
函数临时更改活动上下文。
decimal.
localcontext
(ctx=None)¶返回一个上下文管理器,它将活动线程的当前上下文设置为ctx的副本,并在退出with-statement时恢复前一个上下文。如果没有指定上下文,则使用当前上下文的副本。
例如,以下代码将当前小数精度设置为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
。所有标志都被清除。除Inexact
,Rounded
和Subnormal
之外,所有陷阱均被启用(作为例外处理)。由于许多陷阱都已启用,因此此上下文对调试很有用。
- class
decimal.
ExtendedContext
¶ 这是通用十进制算术规范定义的标准上下文。精度设置为9。舍入设置为
ROUND_HALF_EVEN
。所有标志都被清除。没有陷阱被启用(以便在计算期间不会引发异常)。因为陷阱是禁用的,所以这个上下文对于喜欢
NaN
或Infinity
的结果值而不是引发异常的应用程序非常有用。这允许应用程序在存在条件的情况下完成运行,否则该条件会暂停程序。
- class
decimal.
DefaultContext
¶ 该上下文被
Context
构造函数用作新上下文的原型。更改一个字段(如精度)会改变由Context
构造函数创建的新上下文的默认值。这个上下文在多线程环境中非常有用。在启动线程之前更改其中一个字段具有设置系统范围内默认值的效果。不建议在线程启动后更改字段,因为它需要线程同步来防止竞争条件。
在单线程环境中,最好不要使用这个上下文。相反,只需按照下面的描述直接创建上下文。
The default values are
prec
=28
,rounding
=ROUND_HALF_EVEN
, and enabled traps forOverflow
,InvalidOperation
, andDivisionByZero
.
除了三个提供的上下文之外,还可以使用Context
构造函数创建新的上下文。
- class
decimal.
Context
(prec=None, rounding=None, Emin=None, Emax=None, capitals=None, clamp=None, flags=None, traps=None)¶ 创建一个新的上下文。如果未指定字段或
None
,则默认值将从DefaultContext
中复制。如果未指定标志字段或者None
,则清除所有标志。prec是一个范围为[
1
,MAX_PREC
]的整数,用于设置上下文中算术运算的精度。舍入选项是舍入模式部分中列出的常量之一。
陷阱和标记字段列出了要设置的任何信号。通常情况下,新的上下文应该只设置陷阱并且清除标志。
Emin和Emax字段是指定允许指数的外部限制的整数。Emin must be in the range [
MIN_EMIN
,0
], Emax in the range [0
,MAX_EMAX
].首字母字段是
0
或1
(默认值)。如果设置为1
,指数将用大写字母E
打印;否则,使用小写字母e
:Decimal('6.02e+23')
。clamp字段是
0
(默认)或1
。If set to1
, the exponente
of aDecimal
instance representable in this context is strictly limited to the rangeEmin - prec + 1 <= e <= Emax - prec + 1
. If clamp is0
then a weaker condition holds: the adjusted exponent of theDecimal
instance is at mostEmax
. 当clamp是1
时,一个大的正常数将在可能的情况下减小其指数并将相应数量的零加到其系数上,以便适合指数约束;这保留了数字的值,但丢失了关于重要尾随零的信息。例如:>>> Context(prec=6, Emax=999, clamp=1).create_decimal('1.23e999') Decimal('1.23000E+999')
1
的clamp值允许与IEEE 754中指定的固定宽度十进制交换格式兼容。Context
类定义了几种通用的方法,以及大量用于在给定上下文中直接进行算术的方法。另外,对于上述每个Decimal
方法(除adjusted()
和as_tuple()
方法外)都有一个对应的Context
方法。For example, for aContext
instanceC
andDecimal
instancex
,C.exp(x)
is equivalent tox.exp(context=C)
. 每个Context
方法接受一个Python整数(int
的一个实例),接受Decimal实例的任何地方。clear_flags
()¶将所有标志重置为
0
。
clear_traps
()¶将所有陷阱重置为
0
。版本3.3中的新功能。
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')
此方法实现了IBM规范的定数操作。如果参数是一个字符串,则不允许前导或尾随空格。
create_decimal_from_float
(f)¶从float f创建新的Decimal实例,但使用self作为上下文进行舍入。与
Decimal.from_float()
类方法不同,上下文精度,舍入方法,标志和陷阱应用于转换。>>> context = Context(prec=5, rounding=ROUND_DOWN) >>> context.create_decimal_from_float(math.pi) Decimal('3.1415') >>> context = Context(prec=5, traps=[Inexact]) >>> context.create_decimal_from_float(math.pi) Traceback (most recent call last): ... decimal.Inexact: None
版本3.1中的新功能。
Etop
()¶返回值等于
Emax - prec + T0>。
处理小数的常用方法是创建
Decimal
实例,然后应用在活动线程的当前上下文中进行的算术运算。另一种方法是在上下文中使用上下文方法进行计算。这些方法与Decimal
类的方法类似,在此仅作简要介绍。abs
(x)¶返回x的绝对值。
add
(x, y)¶返回x和y的总和。
canonical
(x)¶返回相同的十进制对象x。
compare
(x, y)¶以数字方式比较x和y。
compare_signal
(x, y)¶以数字方式比较两个操作数的值。
compare_total
(x, y)¶使用抽象表示法比较两个操作数。
compare_total_mag
(x, y)¶使用它们的抽象表示比较两个操作数,忽略符号。
copy_abs
(x)¶返回符号设置为0的x的副本。
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)¶将数值与它们的符号忽略进行比较。
min
(x, y)¶数字比较两个值并返回最小值。
min_mag
(x, y)¶将数值与它们的符号忽略进行比较。
minus
(x)¶减号对应于Python中的一元前缀减运算符。
multiply
(x, y)¶返回x和y的乘积。
next_minus
(x)¶返回小于x的最大可表示数。
next_plus
(x)¶返回大于x的最小可表示数。
next_toward
(x, y)¶返回最接近x的方向,朝y的方向。
normalize
(x)¶将x减少到最简单的形式。
number_class
(x)¶返回x类的指示。
plus
(x)¶Plus对应于Python中的一元前缀加运算符。This operation applies the context precision and rounding, so it is not an identity operation.
power
(x, y, modulo=None)¶Return
x
to the power ofy
, reduced modulomodulo
if given.有两个参数,计算
x**y
。如果x
是负数,那么y
必须是整数。除非y
是积分并且结果是有限的,并且可以精确地用'精度'数字表示,结果将是不精确的。使用上下文的舍入模式。结果总是在Python版本中正确舍入。Changed in version 3.3: The C module computes
power()
in terms of the correctly-roundedexp()
andln()
functions. 结果是明确的,但只有“几乎总是正确的圆整”。有三个参数,计算
(x ** y) % modulo
。对于三个参数表单,参数的以下限制保留:- all three arguments must be integral
y
must be nonnegative- at least one of
x
ory
must be nonzero modulo
must be nonzero and have at most ‘precision’ digits
The value resulting from
Context.power(x, y, modulo)
is equal to the value that would be obtained by computing(x**y) % modulo
with unbounded precision, but is computed more efficiently. 无论x
,y
和modulo
的指数如何,结果的指数均为零。结果总是确切的。
quantize
(x, y)¶返回一个等于x(四舍五入)的值,指数为y。
radix
()¶只需返回10,因为这是Decimal,:)
remainder
(x, y)¶返回整数除法的余数。
结果的符号(如果非零)与原始股息的符号相同。
remainder_near
(x, y)¶Returns
x - y * n
, where n is the integer nearest the exact value ofx / y
(if the result is 0 then its sign will be the sign of 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.常量¶
本节中的常量仅与C模块相关。它们也包含在纯Python版本中以兼容。
32位 | 64位 | |
---|---|---|
| 425000000 | 999999999999999999 |
| 425000000 | 999999999999999999 |
| -425000000 | -999999999999999999 |
| -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.信号¶ T0>
信号代表计算过程中出现的条件。每个对应于一个上下文标志和一个上下文陷阱启用器。
上下文标志在遇到条件时设置。在计算之后,可以检查标志以用于信息目的(例如,以确定计算是否准确)。检查标志后,确保在开始下一次计算之前清除所有标志。
如果为信号设置了上下文的陷阱启用码,则该条件会引发Python异常。例如,如果设置了DivisionByZero
陷阱,则遇到条件时会引发DivisionByZero
异常。
- class
decimal.
Clamped
¶ 改变了指数以适应表示限制。
Typically, clamping occurs when an exponent falls outside the context’s
Emin
andEmax
limits. 如果可能的话,通过给系数加零来使指数减小到合适的值。
- 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
。If not trapped, the result depends on the rounding mode, either pulling inward to the largest representable finite number or rounding outward toInfinity
. 在任何一种情况下,Inexact
和Rounded
也会发出信号。
- class
decimal.
Rounded
¶ 尽管可能没有信息丢失,但发生了舍入。
每当四舍五入舍弃数字时发信号通知;即使这些数字是零(例如舍入
5.00
到5.0
)。如果未被捕获,则返回结果不变。该信号用于检测有效数字的丢失。
- class
decimal.
Subnormal
¶ Exponent was lower than
Emin
prior to rounding.当运算结果不正常时(指数太小)发生。如果未被捕获,则返回结果不变。
- class
decimal.
FloatOperation
¶ 为混合浮点数和小数点启用更严格的语义。
如果信号未被捕获(默认),则在
Decimal
构造函数,create_decimal()
和所有比较运算符中允许混合浮点数和小数点。转换和比较都是确切的。通过在上下文标志中设置FloatOperation
,可以静默记录任何混合操作的发生。使用from_float()
或create_decimal_from_float()
的显式转换不会设置标志。否则(信号被捕获),只有平等比较和显式转换是无声的。All other mixed operations raise
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.特殊值¶
The number system for the decimal
module provides special values including NaN
, sNaN
, -Infinity
, Infinity
, and two zeros, +0
and -0
.
无穷可以直接使用:Decimal('Infinity')
。此外,当DivisionByZero
信号未被捕获时,它们可能会因除以零而产生。同样,当Overflow
信号未被捕获时,无限大可能因四舍五入超出最大可表示数的限制而产生。
无穷大被签名(仿射),可用于算术运算中,它们被视为非常大,不确定的数字。例如,将一个常数加到无穷大给出了另一个无限的结果。
某些操作不确定并返回NaN
,或者如果InvalidOperation
信号被捕获,则引发异常。例如,0/0
返回NaN
,这意味着“不是一个数字”。This variety of NaN
is quiet and, once created, will flow through other computations always resulting in another NaN
. 这种行为对于偶尔缺少输入的一系列计算很有用 - 它允许在将特定结果标记为无效时继续进行计算。
A variant is sNaN
which signals rather than remaining quiet after every operation. 当无效结果需要中断特殊处理的计算时,这是一个有用的返回值。
涉及NaN
时,Python的比较运算符的行为可能有点令人惊讶。A test for equality where one of the operands is a quiet or signaling NaN
always returns False
(even when doing Decimal('NaN')==Decimal('NaN')
), while a test for inequality always returns True
. An 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. Note that the General Decimal Arithmetic specification does not specify the behavior of direct comparisons; these rules for comparisons involving a NaN
were taken from the IEEE 854 standard (see Table 3 in section 5.7). 为确保遵守严格的标准,请使用compare()
和compare-signal()
方法。
有符号的零可能源自下溢的计算结果。如果计算过程更加精确,它们会保留原来的符号。由于它们的大小为零,所以正和负零都被视为相等并且它们的符号是信息性的。
除了两个不同而又相等的有符号零点之外,零点的各种表示还有不同的精度,但其价值相同。这需要一些习惯。对于习惯于归一化浮点表示的眼睛来说,下面的计算返回等于零的值并不明显:
>>> 1 / Decimal('Infinity')
Decimal('0E-1000026')
9.4.8.使用线程¶
getcontext()
函数为每个线程访问不同的Context
对象。分离线程上下文意味着线程可以在不干扰其他线程的情况下进行更改(如getcontext().prec=10
)。
同样,setcontext()
函数自动将其目标分配给当前线程。
如果setcontext()
尚未在getcontext()
之前调用,那么getcontext()
会自动创建一个新的上下文以供当前线程使用。
新的上下文是从称为DefaultContext的原型上下文中复制的。要控制默认值,以便每个线程在整个应用程序中使用相同的值,请直接修改DefaultContext对象。这应该在开始之前完成,以便在调用getcontext()
的线程之间不存在争用条件。例如:
# Set applicationwide defaults for all threads about to be launched
DefaultContext.prec = 12
DefaultContext.rounding = ROUND_DOWN
DefaultContext.traps = ExtendedContext.traps.copy()
DefaultContext.traps[InvalidOperation] = 1
setcontext(DefaultContext)
# Afterwards, the threads can be started
t1.start()
t2.start()
t3.start()
. . .
9.4.9.配方¶ T0>
下面是一些作为实用功能的食谱,并演示了如何使用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.有很多方法来表达相同的价值。The numbers 200
, 200.000
, 2E2
, and 02E+4
all have the same value at various precisions. 有没有办法将它们转换为单一可识别的规范值?
一个。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')