首页 > 解决方案 > 用 python 编码的 Decimal 对象到底是如何编码的?

问题描述

我目前正在使用decimal.Decimalpython (v3.8.5) 编写代码。

我想知道是否有人知道 Decimal 对象的实际编码方式。

我不明白为什么即使我改变内存大小也是一样的getcontext().prec,这等于改变十进制浮点中的系数和指数,如下

from decimal import *
from sys import getsizeof

## coefficient bits = 3
getcontext().prec = 3

temp = Decimal('1')/Decimal('3')

print(temp.as_tuple()) >>> DecimalTuple(sign=0, digits=(3, 3, 3), exponent=-3)
print(getsizeof(temp)) >>> 104

## coefficient bits = 30
getcontext().prec = 30

temp = Decimal('1')/Decimal('3')

print(temp.as_tuple()) >>> DecimalTuple(sign=0, digits=(3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3), exponent=-30)
print(getsizeof(temp)) >>> 104

为了理解上述行为,我阅读了 Decimal 类的源代码和附件。

根据文档,Python 的 Decimal 对象是基于 IEEE 754-2008 实现的,系数延续的十进制数字使用 DPD(密集压缩十进制)编码转换为二进制数字。

因此,根据 DPD 算法,我们可以计算将系数连续的十进制数字编码为二进制数字时的位数。

并且由于符号、指数延续和组合字段简单地用二进制表示,编码时的位数可以很容易地计算出来。

因此,我们可以通过以下公式计算 Decimal obejcet 编码时的位数。bits = (sign) + (exp) + (comb) + (compressed coeff)

这里,符号和组合分别固定为 1bit 和 5bits(根据 IEEE 754-2008 的定义。https: //en.wikipedia.org/wiki/Decimal_floating_point )。

因此,我编写了上面的代码来检查as_tuple()Decimal 对象的 {sign, exponent, coefficient} 列表,并计算内存中的实际位数。

但是,如上所述,Decimal 对象的内存大小根本没有改变,即使系数中的位数应该已经改变了。(我了解 Decimal 对象不仅是十进制编码,而且是列表和其他对象。)

出现以下两个问题。

(1)我对python中Decimal对象的编码算法理解有误吗?(python3.8.5 是否使用比 IEEE 754-2008 更高效的编码算法?)

(2) 假设我对算法的理解是正确的,为什么 Decimal 对象的内存大小保持不变,即使改变了系数?(根据 IEEE754-2008 的定义,改变系数接续时,指数接续也随之改变,总位数也应改变。)

我自己是一个通常在机械工程领域学习的学生,我是一个完全的信息学初学者。如果我原来的理解有任何部分是错误的,或者有任何奇怪的逻辑发展,请告诉我。

我感谢您的帮助。

标签: pythondecimal

解决方案


对于sys.getsizeof

仅考虑直接归因于对象的内存消耗,而不考虑它所引用的对象的内存消耗。

由于Decimal它是一个引用了其他几个对象的 Python 类(编辑:见下文),因此您只需获得引用的总大小,它是恒定的 - 不包括引用的值,这些值不是。

getcontext().prec = 3
temp = Decimal(3) / Decimal(1)
print(sys.getsizeof(temp))
print(sys.getsizeof(temp._int))

getcontext().prec = 300
temp = Decimal(3) / Decimal(1)
print(sys.getsizeof(temp))         # same
print(sys.getsizeof(temp._int))    # not same

(请注意,_int我在示例中使用的插槽是 CPython 的内部实现细节Decimal,如前导下划线所暗示的那样;不保证此代码在其他 Python 实现甚至其他版本中也能工作。)


编辑:哎呀,我的第一个答案是在一个旧的 Python 上,它Decimal是用 Python 实现的。您询问的版本已在 C 中实现

C 版本实际上将所有内容存储在对象本身内,但是您的精度差异不足以检测到差异(因为内存是按离散块分配的)。试试吧getcontext().prec = 300


推荐阅读