python - 用 python 编码的 Decimal 对象到底是如何编码的?
问题描述
我目前正在使用decimal.Decimal
python (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 类的源代码和附件。
- 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 算法,我们可以计算将系数连续的十进制数字编码为二进制数字时的位数。
并且由于符号、指数延续和组合字段简单地用二进制表示,编码时的位数可以很容易地计算出来。
因此,我们可以通过以下公式计算 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
(请注意,_int
我在示例中使用的插槽是 CPython 的内部实现细节Decimal
,如前导下划线所暗示的那样;不保证此代码在其他 Python 实现甚至其他版本中也能工作。)
编辑:哎呀,我的第一个答案是在一个旧的 Python 上,它Decimal
是用 Python 实现的。您询问的版本已在 C 中实现。
C 版本实际上将所有内容存储在对象本身内,但是您的精度差异不足以检测到差异(因为内存是按离散块分配的)。试试吧getcontext().prec = 300
。
推荐阅读
- javascript - Highstock - 多个图表一个容器
- laravel - Laravel Darkaonline/Swagger 验证请求不起作用
- python - 网页抓取:获取 imgur 视频的长度
- docker - 权限被拒绝 docker localhost
- nginx - Websocket 传输关闭(EC2+NGINX+PM2 Cluster+Redis)在一定时间后
- php - 使用没有“updated_at”字段的自定义中间表模型的 Eloquent Belongs-to-Many 关系
- excel - 带有日期和文本的 VBA If 函数
- android - Xamarin.Forms 中是否存在等效于 html 数据列表的机制,该机制允许选择预定义值但也允许选择自由文本条目?
- .htaccess - 如何使用 htaccess 重写虚拟子域以使用其他参数查询参数
- angular - 更新每个同名子集合中的数据。Firebase/AngularFire