在 python 中编码的 Decimal 对象究竟是如何编码的?

TMo*_*TMo 9 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
Run Code Online (Sandbox Code Playgroud)

为了理解上述行为,我阅读了 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的定义,当系数延续改变时,指数延续也改变,总位数也应该改变。)

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

我感谢您的帮助。

Ama*_*dan 6

对于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
Run Code Online (Sandbox Code Playgroud)

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


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

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

  • 实际上,我似乎是在一种非常不同的 Python 上进行测试,其中“Decimal”是用 Python 而不是 C 实现的。我将更新我的答案。 (2认同)