decimal
decimal — Decimal fixed point and floating point arithmetic
2.4版本中的新功能。
该decimal
模块提供对十进制浮点运算的支持。它比float
数据类型提供了几个优点:
- 十进制“是基于一个浮点模型,该模型是以人为本设计的,并且必须有一个最重要的指导原则 - 计算机必须提供一种与人们在学校学习的算术相同的算法。” - 摘自十进制算术规范。
- 十进制数字可以精确表示。相比之下,数字在二进制浮点中像
1.1
和2.2
没有精确的表示。最终用户通常不会像使用二进制浮点一样1.1 + 2.2
显示3.3000000000000003
。
- 精确性被转化为算术。在十进制浮点数中,
0.1 + 0.1 + 0.1 - 0.3
正好等于零。在二进制浮点中,结果是5.5511151231257827e-017
。尽管接近于零,但差异阻止了可靠的平等测试,并且可能会积累差异。由于这个原因,在具有严格的等式不变量的会计应用中,小数是优选的。
- 十进制模块包含了重要位置的概念,因此
1.30 + 1.20
是2.50
。尾随零保持显示重要性。这是货币应用的惯例。对于乘法,“教科书”方法使用被乘数中的所有数字。举例来说,1.3 * 1.2
给人1.56
的同时1.30 * 1.20
给人1.5600
。
- 与基于硬件的二进制浮点不同,十进制模块具有用户可更改的精度(默认为28个位置),对于给定的问题,该精度可以与需要的一样大:
from decimal import * >>> getcontext().prec = 6 >>> Decimal(1) / Decimal(7) Decimal('0.142857') >>> getcontext().prec = 28 >>> Decimal(1) / Decimal(7) Decimal('0.1428571428571428571428571429')
- 二进制和十进制浮点都是按照已发布的标准实现的。尽管内置的float类型只展示了它的功能中的一小部分,但十进制模块公开了标准的所有必需部分。在需要时,程序员可以完全控制舍入和信号处理。这包括通过使用例外来阻止任何不精确操作来强制执行精确算术的选项。
- 十进制模块被设计为支持“没有偏见,精确的未被占用的十进制算术(有时称为定点算术)和四舍五入的浮点算术。” - 摘自十进制算术规范。
模块设计以三个概念为中心:十进制数,算术环境和信号。
十进制数是不可变的。它有一个符号,系数数字和一个指数。为了保留重要性,系数数字不会截断尾随零。小数也包括特殊值,如Infinity
,-Infinity
和NaN
。该标准还区分-0
的+0
。
算术环境是指定精度的环境,舍入规则,指数限制,指示操作结果的标志,以及确定信号是否被视为例外的陷阱启动器。舍入选项包括ROUND_CEILING
,ROUND_DOWN
,ROUND_FLOOR
,ROUND_HALF_DOWN
,ROUND_HALF_EVEN
,ROUND_HALF_UP
,ROUND_UP
,和ROUND_05UP
。
信号是在计算过程中出现的一组特殊情况。根据应用的需要,信号可能被忽略,被视为信息性的,或被视为例外。十进制模块中的信号是:Clamped
,InvalidOperation
,DivisionByZero
,Inexact
,Rounded
,Subnormal
,Overflow
,和Underflow
。
每个信号都有一个标志和一个陷阱启动器。遇到信号时,其标志被设置为1,然后,如果陷阱启用码设置为1,则会引发异常。标志是粘性的,所以用户在监控计算之前需要重置它们。
"-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
发出an 信号。这保证了,除非有错误条件,否则量化指数总是等于右侧操作数的指数。
与其他操作不同,即使结果是次正常且不精确,量化也不会发出下溢信号。
如果第二个操作数的指数大于第一个操作数的指数,则可能需要舍入。在这种情况下,舍入模式由rounding
参数确定,如果给出,则由给定context
参数确定; 如果没有给出参数,则使用当前线程上下文的舍入模式。
如果watchexp
被设置(默认),那么只要结果指数大于Emax
或小于,就会返回一个错误Etiny
。
radix()
返回Decimal(10)
,Decimal
类中所有算术的基数(基数)。包括与规范的兼容性。
2.6版本中的新功能。
remainder_near(other[, context])
从分返回,其余自
通过其他
。这不同于self % other
其余部分的符号被选择为使其绝对值最小化。更确切地说,返回值是self - n * other
其中n
最接近的精确值的整数self / other
,如果两个整数都同样接近,则即使一个选择。
如果结果为零,那么它的符号将是自我
的标志。
>>> 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中的整数。第二个操作数的绝对值给出了要旋转的地方的数量。如果第二个操作数是正的,那么旋转是在左边; 否则旋转是正确的。如有必要,第一个操作数的系数在左侧填充为零以达到长度精度。第一个操作数的符号和指数保持不变。
2.6版本中的新功能。
same_quantum(other[, context])
测试自我和其他人是否有相同的指数或两者是否都是NaN
。
scaleb(other[, context])
返回第一个操作数,指数由第二个调整。等价地,返回第一个操作数乘以10**other
。第二个操作数必须是整数。
2.6版本中的新功能。
shift(other[, context])
返回将第一个操作数的数位移位第二个操作数指定的数值的结果。第二个操作数必须是精度范围-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]])
四舍五入
到最接近的整数,无需信号Inexact
或Rounded
。如果给出,则适用四舍五入
; 否则,在提供的上下文
或当前上下文
中使用舍入方法。
在版本2.6中更改:从重命名to_integral
为to_integral_value
。旧名称对于兼容性仍然有效。
2.1。逻辑操作数
在logical_and()
,logical_invert()
,logical_or()
,和logical_xor()
方法,希望他们的论点是合乎逻辑的操作数
。一个合乎逻辑的操作数
是一个Decimal
实例,其指数和符号均为零,而其数字都是要么0
或1
。
3.上下文对象
上下文是算术运算的环境。它们控制精度,设置舍入规则,确定哪些信号被视为例外,并限制指数的范围。
每个线程都有自己的当前上下文,可以使用getcontext()
和setcontext()
函数访问或更改它们:
decimal.getcontext()
返回活动线程的当前上下文。
decimal.setcontext(c)
将活动线程的当前上下文设置为c
。
从Python 2.5开始,您还可以使用with
语句和localcontext()
函数临时更改活动上下文。
decimal.localcontext([c])
返回一个上下文管理器,它将活动线程的当前上下文设置为进入with-statement时的c
副本,并在退出with-statement时恢复上一个上下文。如果没有指定上下文,则使用当前上下文的副本。
2.5版本中的新功能。
例如,以下代码将当前小数精度设置为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
这是通用十进制算术规范定义的标准上下文。精度设置为9。舍入被设置为ROUND_HALF_UP
。所有标志都被清除。所有的陷阱启用(例外处理)除外Inexact
,Rounded
以及Subnormal
。
由于许多陷阱都已启用,因此此上下文对调试很有用。
class decimal.ExtendedContext
这是通用十进制算术规范定义的标准上下文。精度设置为9。舍入被设置为ROUND_HALF_EVEN
。所有标志都被清除。没有陷阱被启用(以便在计算期间不会引发异常)。
由于陷阱被禁用,因此此上下文对于偏好具有结果值NaN
或Infinity
不是引发异常的应用程序非常有用。这允许应用程序在存在条件的情况下完成运行,否则该条件会暂停程序。
class decimal.DefaultContext
这个上下文被Context
构造函数用作新上下文的原型。更改一个字段(如精度)会改变Context
构造函数创建的新上下文的默认值。
这个上下文在多线程环境中非常有用。在启动线程之前更改其中一个字段具有设置系统范围内默认值的效果。不建议在线程启动后更改字段,因为它需要线程同步来防止竞争条件。
在单线程环境中,最好不要使用这个上下文。相反,只需按照下面的描述直接创建上下文。
默认值为precision = 28,四舍五入= ROUND_HALF_EVEN,并为溢出,InvalidOperation和DivisionByZero启用陷阱。
除了三个提供的上下文之外,还可以使用Context
构造函数创建新的上下文。
class decimal.Context(prec=None, rounding=None, traps=None, flags=None, Emin=None, Emax=None, capitals=1)
创建一个新的上下文。如果一个字段没有被指定或者是None
,那么缺省值将被复制DefaultContext
。如果标志
字段没有被指定或者是None
,则所有标志
都被清除。
该PREC
场是一个正整数,用于设置在上下文算术运算的精度。
该舍入
选项之一:
ROUND_CEILING
(towardsInfinity
),
ROUND_DOWN
(towards zero),
ROUND_FLOOR
(towards-Infinity
),
ROUND_HALF_DOWN
(最接近零的关系),
ROUND_HALF_EVEN
(到连接最近的偶数到最近的连接),
ROUND_HALF_UP
(距离零距离的关系最近),
ROUND_UP
(远离零)。
ROUND_05UP
(如果在舍入到零后的最后一位数字将为0或5,则为零,否则为零)
该陷阱
和标志
字段列出任何信号进行设置。通常情况下,新的上下文应该只设置陷阱
并且清除标志
。
的额敏
和的Emax
字段指定允许的指数外部界限整数。
的首都
字段是0
或1
(缺省值)。如果设置为1
,指数用大写打印E
; 否则,使用小写字母e
:Decimal('6.02e+23')
。
在版本2.6中更改:ROUND_05UP
添加了舍入模式。
在Context
类定义了几个通用的方法以及大量直接在特定情况下做算术的方法。另外,对于每个Decimal
上述(具有的异常的方法adjusted()
和as_tuple()
方法)有相应的Context
方法。例如,对于一个Context
实例C
和Decimal
实例x
,C.exp(x)
相当于x.exp(context=C)
。每个Context
方法接受一个Python整数(一个int
或一个实例long
),接受一个Decimal
实例。
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')
此方法实现了IBM规范的定数操作。如果参数是一个字符串,则不允许前导或尾随空格。
create_decimal_from_float(f)
从f
loat 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
班级的方法类似,只是在这里简要叙述。
abs(x)
返回x
的绝对值。
add(x, y)
返回x
和y的
和。
canonical(x)
返回相同的Decimal对象x
。
compare(x, y)
数字比较x
和y
。
compare_signal(x, y)
以数字方式比较两个操作数的值。
compare_total(x, y)
使用抽象表示法比较两个操作数。
compare_total_mag(x, y)
使用它们的抽象表示比较两个操作数,忽略符号。
copy_abs(x)
返回符号设置为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)
True
如果x
是有限的,则返回; 否则返回False
。
is_infinite(x)
True
如果x
是无限的,则返回; 否则返回False
。
is_nan(x)
True
如果x
是qNaN或sNaN,则返回; 否则返回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)
返回True
如果X
是低于正常; 否则返回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
向,方向ÿ
。
normalize(x)
将x
简化为最简单的形式。
number_class(x)
返回x
的类的指示。
plus(x)
Plus对应于Python中的一元前缀加运算符。此操作应用上下文精度和舍入,因此它不是
身份验证操作。
power(x, y[, modulo])
如果给定,则返回x
到y
模的减少模modulo
。
有两个参数,计算x**y
。如果x
是负数,那么y
必须是不可分割的 除非y
是积分并且结果是有限的,并且可以精确地用“精确”数字表示,结果将是不精确的。结果应该始终使用当前线程上下文的舍入模式正确舍入。
有三个参数,计算(x**y) % modulo
。对于三个参数表单,参数的以下限制保留:
- 所有三个参数都必须是不可分割
y
必须是非负的
- 至少有一个
x
或y
必须是非零
modulo
必须非零,并且至多有'精度'数字
由此产生的值Context.power(x, y, modulo)
等于通过(x**y) % modulo
以无限精度进行计算所获得的值,但计算效率更高。结果的指数为零,不管指数是多少x
,y
和modulo
。结果总是确切的。
在版本2.6中更改:y
现在可能是非整数x**y
。对三参数版本有更严格的要求。
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)
在添加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)
使用科学记数法将数字转换为字符串。
4.信号
信号代表计算过程中出现的条件。每个对应于一个上下文标志和一个上下文陷阱启用器。
上下文标志在遇到条件时设置。在计算之后,可以检查标志以用于信息目的(例如,以确定计算是否准确)。检查标志后,确保在开始下一次计算之前清除所有标志。
如果为信号设置了上下文的陷阱启用码,则该条件会引发Python异常。例如,如果DivisionByZero
设置了陷阱,则DivisionByZero
在遇到该情况时会引发异常。
class decimal.Clamped
改变了指数以适应表示限制。
通常,当指数超出上下文Emin
和Emax
限制范围时,会发生钳位。如果可能的话,通过给系数加零来使指数减小到合适的值。
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
。在这两种情况下,Inexact
并Rounded
也发出信号。
class decimal.Rounded
尽管可能没有信息丢失,但发生了舍入。
每当四舍五入舍弃数字时发信号通知; 即使这些位是零(如舍入5.00
到5.0
)。如果未被捕获,则返回结果不变。该信号用于检测有效数字的丢失。
class decimal.Subnormal
指数比Emin
四舍五入之前更低。
当运算结果不正常时(指数太小)发生。如果未被捕获,则返回结果不变。
class decimal.Underflow
数值下溢,结果舍入为零。
通过舍入将低于正常值的结果推到零时发生。Inexact
并Subnormal
发出信号。
下表总结了信号的层次结构:
exceptions.ArithmeticError(exceptions.StandardError)
DecimalException
Clamped
DivisionByZero(DecimalException, exceptions.ZeroDivisionError)
Inexact
Overflow(Inexact, Rounded)
Underflow(Inexact, Rounded, Subnormal)
InvalidOperation
Rounded
Subnormal
5.浮点注释
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')
5.2。特殊的价值
对于数字系统decimal
模块提供了特殊的值,包括NaN
,sNaN
,-Infinity
,Infinity
,和两个零,+0
和-0
。
无穷可以直接用:Decimal('Infinity')
。另外,当DivisionByZero
信号未被捕获时,它们可以通过除以零而产生。同样,当Overflow
信号未被捕获时,无限大可能因四舍五入而超出最大可表示数的极限。
无穷大被签名(仿射),可用于算术运算中,它们被视为非常大,不确定的数字。例如,将一个常数加到无穷大给出了另一个无限的结果。
有些操作是不确定的并返回NaN
,或者如果InvalidOperation
信号被困住,引发异常。例如,0/0
返回NaN
意味着“不是数字”。这种多样性NaN
是安静的,一旦创建,将会流过其他计算总是导致另一个计算NaN
。这种行为对于偶尔缺少输入的一系列计算很有用 - 它允许在将特定结果标记为无效时继续进行计算。
一个变体是sNaN
每个操作后信号而不是保持安静。当无效结果需要中断特殊处理的计算时,这是一个有用的返回值。
NaN涉及到a的时候,Python的比较运算符的行为可能有点令人惊讶。一个操作数是安静的或信号NaN总是返回的False(即使是这样Decimal('NaN')==Decimal('NaN'))的平等测试,而不平等测试总是返回True。尝试使用任何比较两个小数<,<=,>或>=运营商将提高InvalidOperation信号如果操作数是一个NaN,并返回False如果这个信号没有被限制。请注意,通用十进制算术规范并未指定直接比较的行为; 这些涉及a的比较规则NaN取自IEEE 854标准(参见5.7节中的表3)。为确保遵守严格的标准,请使用compare()和compare-signal()方法。
有符号的零可能源自下溢的计算结果。如果计算过程更加精确,它们会保留原来的符号。由于它们的大小为零,所以正和负零都被视为相等并且它们的符号是信息性的。
除了两个不同而又相等的有符号零点之外,零点的各种表示还有不同的精度,但其价值相同。这需要一些习惯。对于习惯于归一化浮点表示的眼睛来说,下面的计算返回等于零的值并不明显:
>>> 1 / Decimal('Infinity')
Decimal('0E-1000000026')
6.使用线程
该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()
. . .
7. Recipes
以下是一些用作实用功能的食谱,并演示了如何与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
8.小数常见问题
问:输入很麻烦decimal.Decimal('1234.5')
。在使用交互式解释器时,是否有办法减少键入?
答:有些用户将构造函数缩写为一个字母:
>>> D = decimal.Decimal
>>> D('1.23') + D('3.45')
Decimal('4.68')
问:在具有两位小数的定点应用程序中,某些输入有许多地方,需要四舍五入。其他人不应该有过多的数字,需要验证。应该使用什么方法?
A.该quantize()
方法舍入到固定的小数位数。如果Inexact
设置了陷阱,它对验证也很有用:
>>> TWOPLACES = Decimal(10) ** -2 # same as Decimal('0.01')
>>> # Round to two places
>>> Decimal('3.214').quantize(TWOPLACES)
Decimal('3.21')
>>> # Validate that a number does not exceed two places
>>> Decimal('3.21').quantize(TWOPLACES, context=Context(traps=[Inexact]))
Decimal('3.21')
>>> Decimal('3.214').quantize(TWOPLACES, context=Context(traps=[Inexact]))
Traceback (most recent call last):
...
Inexact: None
问:一旦我有有效的两个位置输入,我如何在整个应用程序中保持不变?
答:一些操作,如加,减,乘整数将自动保留固定点。其他的操作,如分割和非整数乘法,将改变小数位数,需要后续quantize()
步骤:
>>> a = Decimal('102.72') # Initial fixed-point values
>>> b = Decimal('3.17')
>>> a + b # Addition preserves fixed-point
Decimal('105.89')
>>> a - b
Decimal('99.55')
>>> a * 42 # So does integer multiplication
Decimal('4314.24')
>>> (a * b).quantize(TWOPLACES) # Must quantize non-integer multiplication
Decimal('325.62')
>>> (b / a).quantize(TWOPLACES) # And quantize division
Decimal('0.03')
在开发定点应用程序时,定义处理该quantize()
步骤的函数很方便:
>>> def mul(x, y, fp=TWOPLACES):
... return (x * y).quantize(fp)
>>> def div(x, y, fp=TWOPLACES):
... return (x / y).quantize(fp)
>>> mul(a, b) # Automatically preserve fixed-point
Decimal('325.62')
>>> div(b, a)
Decimal('0.03')
问:表达相同价值的方法很多。这些数字200
,200.000
,2E2
,并且02E+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')]
问:一些小数值总是以指数表示法打印。有没有办法获得非指数表示?
答:对于某些值,指数符号是表示系数中重要位置数的唯一方式。例如,表达5.0E+3
为5000
保持值不变,但不能显示原始的两位意义。
如果应用程序不关心跟踪重要性,则很容易去除指数和尾随零,失去重要性,但保持值不变:
def remove_exponent(d):
'''Remove exponent and trailing zeros.
>>> remove_exponent(Decimal('5E+3'))
Decimal('5000')
'''
return d.quantize(Decimal(1)) if d == d.to_integral() else d.normalize()
问:是否有方法将常规浮点数转换为Decimal
?
答:是的,任何二进制浮点数都可以精确地表示为十进制,虽然精确转换可能比直觉所建议的要精确得多:
>>> Decimal(math.pi)
Decimal('3.141592653589793115997963468544185161590576171875')
问:在复杂的计算过程中,我如何确保由于精度不足或舍入异常导致我没有得到虚假结果。
答:十进制模块可以很容易地测试结果。最佳做法是使用更高精度和各种舍入模式重新运行计算。广泛不同的结果表明精度不足,舍入模式问题,病态输入或数字不稳定算法。
问:我注意到上下文的精确度适用于操作的结果,但不适用于输入。混合不同精度的数值时有什么需要注意的吗?
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')