我目前正在使用python (v3.8.5)中的decimal.Decimal
编写代码。
我想知道是否有人知道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类的源代码和附带的文档。
- https://github.com/python/cpython/blob/main/Lib/_pydecimal.py
- http://speleotrove.com/decimal/decarith.html
- http://speleotrove.com/decimal/decbits.pdf
根据文档,Python的Decimal对象是基于IEEE 754-2008实现的,并使用DPD(密包十进制)编码将系数延续的十进制数转换为二进制数。
因此,根据DPD算法,我们可以计算出系数延拓的十进制数编码为二进制数时的位数。
由于符号、指数延拓和组合字段都简单地用二进制表示,因此编码时的位数可以很容易地计算出来。
那么,我们可以用下面的公式计算十进制对象编码时的位数。位=(符号)+ (exp) +(梳子)+(压缩后的coeff)
这里,符号和组合分别固定为1bit和5bit(根据IEEE 754-2008的定义)。https://en.wikipedia.org/wiki/Decimal_floating_point)。
因此,我编写了上面的代码,使用Decimal对象的as_tuple()
来检查{sign, exponent, coefficient}的列表,并计算内存中的实际位数。
然而,如上所述,Decimal对象的内存大小根本没有改变,即使系数中的位数应该发生了变化。(我知道Decimal对象不仅是十进制编码,而且是列表和其他对象。)
出现了以下两个问题。
(1)我对python中Decimal对象的编码算法理解错了吗?(python3.8.5是否使用比IEEE 754-2008更有效的编码算法?)
(2)假设我对算法的理解是正确的,为什么即使改变了系数,Decimal对象的内存大小仍然保持不变?(根据IEEE754-2008的定义,当系数延拓改变时,指数延拓也随之改变,总位数也要改变)
我自己是一个通常在机械工程领域学习的学生,在信息学方面我是一个完全的初学者。如果我原来的理解有什么地方是错误的,或者有什么奇怪的逻辑发展,请告诉我。
我很感激你的帮助。Forsys.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
代替。