python: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.31.2给出1.56,而1.301.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')

二进制和 decimal 浮点数都是根据已发布的标准实现的。 虽然内置浮点类型只公开其功能的一小部分,但 decimal 模块公开了标准的所有必需部分。 在需要时,程序员可以完全控制舍入和信号处理。 这包括通过使用异常来阻止任何不精确操作来强制执行精确算术的选项。

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

该模块的设计以三个概念为中心:decimal 数值,算术上下文和信号。
十进制数是不可变的。它有符号、系数数字和指数。为了保持有效性,系数位数不截断尾随零。小数还包括特殊值,如无穷大、无穷大和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 以及 FloatOperation 。

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

参见
IBM’s General Decimal Arithmetic Specification, The General Decimal Arithmetic Specification.

快速入门教程

通常使用 decimal 的方式是先导入该模块,通过 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(表示“非数字”)、正负无穷大和-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 版本的内部限制,则构造一个 decimal 将引发 InvalidOperation

>>>
Decimal("1e9999999999999999999")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
decimal.InvalidOperation: [<class 'decimal.InvalidOperation'>]

在 3.3 版更改.

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

>>>
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')
The quantize() method rounds a number to a fixed exponent. This method is useful for monetary applications that often round results to a fixed number of places:

>>>
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 数字类型没有区别。

Decimal 对象

class decimal.Decimal(value=‘0’, context=None)
根据 value 构造一个新的 Decimal 对象。

value 可以是整数,字符串,元组,float ,或另一个 Decimal 对象。 如果没有给出 value,则返回 Decimal(‘0’)。 如果 value 是一个字符串,它应该在前导和尾随空格字符以及下划线被删除之后符合十进制数字字符串语法:

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

当上面出现 digit 时也允许其他十进制数码。 其中包括来自各种其他语言系统的十进制数码(例如阿拉伯-印地语和天城文的数码)以及全宽数码 ‘\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. For example, Decimal((0, (1, 4, 1, 4), -3)) returns Decimal(‘1.414’).

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

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

The purpose of the context argument is determining what to do if value is a malformed string. If the context traps InvalidOperation, an exception is raised; otherwise, the constructor returns a new Decimal with the value of NaN.

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

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

在 3.3 版更改: float 参数在设置 FloatOperation 陷阱时引发异常。 默认情况下,陷阱已关闭。

在 3.6 版更改: 允许下划线进行分组,就像代码中的整数和浮点文字一样。

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

实战大师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值