我目前正在使用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的定义,当系数延续改变时,指数延续也改变,总位数也应该改变。)
我自己是一名通常在机械工程领域学习的学生,我是一个完整的信息学初学者。如果我原来的理解有任何部分是错误的或者有任何奇怪的逻辑发展,请告诉我。
我感谢您的帮助。
只考虑直接归因于对象的内存消耗,而不是它所引用的对象的内存消耗。
由于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。
| 归档时间: |
|
| 查看次数: |
210 次 |
| 最近记录: |