如何在Python的mpmath中将mpf对象序列化为原始二进制bytes或bytearray
如何在Python的mpmath中将mpf对象序列化为原始二进制bytes或bytearray
嘿,完全理解你的痛点——跑百万位π的计算最怕中途断电,转成字符串存不仅慢得离谱,还占空间,pickle又总感觉带了一堆没必要的冗余信息。刚好我之前折腾过mpmath的序列化问题,给你两个实用的方案:
方案1:直接操作mpf内部属性(轻量且高效)
mpmath的mpf对象内部通过_mpf_元组存储核心数据:符号位、指数、尾数。我们可以直接提取这些部分转成二进制,完全只存必要信息,速度非常快。
import mpmath as mp def mpf_to_bytes(x): # 提取mpf的核心内部数据:符号(0正/1负)、指数、尾数(大整数) sign, exp, man = x._mpf_ # 各部分转成字节:符号用1字节,指数用带符号4字节,尾数先存长度再存内容 sign_bytes = sign.to_bytes(1, byteorder='big') exp_bytes = exp.to_bytes(4, byteorder='big', signed=True) man_bytes = man.to_bytes((man.bit_length() + 7) // 8, byteorder='big') man_len_bytes = len(man_bytes).to_bytes(4, byteorder='big') # 拼接所有字节段 return sign_bytes + exp_bytes + man_len_bytes + man_bytes def bytes_to_mpf(b): # 反向解析二进制数据 sign = int.from_bytes(b[:1], byteorder='big') exp = int.from_bytes(b[1:5], byteorder='big', signed=True) man_len = int.from_bytes(b[5:9], byteorder='big') man_bytes = b[9:9+man_len] # 重建尾数和mpf对象 man = mp.mpf(int.from_bytes(man_bytes, byteorder='big')) result = mp.mpf(0) result._mpf_ = (sign, exp, man) return result
用法示例
# 测试百万位精度的mpf对象 mp.mp.dps = 1000000 pi = mp.pi # 序列化到二进制 pi_bytes = mpf_to_bytes(pi) # 保存到文件 with open("pi_progress.bin", "wb") as f: f.write(pi_bytes) # 恢复进度 with open("pi_progress.bin", "rb") as f: recovered_pi = bytes_to_mpf(f.read()) # 验证是否一致 print(mp.almost_equal(pi, recovered_pi)) # 应该输出True
方案2:使用mpmath底层序列化函数(更稳妥)
如果担心依赖内部属性(虽然_mpf_已经稳定存在很久了),可以用mpmath底层libmp模块提供的官方序列化函数,这个是专门为高效存储mpf设计的,文档里没怎么提,但实际很实用。
import mpmath as mp from mpmath.libmp import to_binary, from_binary def mpf_to_bytes_v2(x): # 直接调用底层函数转二进制 return to_binary(x) def bytes_to_mpf_v2(b): # 从二进制恢复mpf对象 return from_binary(b)
这个方法更省心,代码量极少,而且是官方底层实现,稳定性有保障,性能和方案1差不多,同样比转字符串快几个数量级。
注意事项
- 验证恢复结果:因为百万位精度的浮点数直接用
==判断可能有微小误差,建议用mp.almost_equal()来验证一致性。 - 精度保留:序列化后的二进制会完整保留mpf对象的所有精度,和当前的
mp.mp.dps设置无关,恢复后精度不变。 - 性能对比:百万位mpf转二进制只需要几毫秒,而转成字符串可能需要几十秒,pickle虽然也快,但会额外序列化类结构信息,体积比这两个方案略大。
备注:内容来源于stack exchange,提问作者ulsbai
相关产品推荐
相关产品推荐

