You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何从float构造Decimal时shift方法结果异常?

Decimal从float构造后shift方法结果异常的原因

问题重现

用字符串构造Decimal(正常情况)

代码:

from decimal import Decimal

d = Decimal("1.23")
print(f"{d = }, {d.shift(1) = }")

执行输出:

d = Decimal('1.23'), d.shift(1) = Decimal('12.30')

此结果符合预期:看起来shift(1)实现了十进制左移1位的效果。

用float构造Decimal(异常情况)

代码:

from decimal import Decimal

d = Decimal(1.23)
print(f"{d = }, {d.shift(1) = }")

执行输出:

d = Decimal('1.229999999999999982236431605997495353221893310546875'), d.shift(1) = Decimal('6.059974953532218933105468750E-24')

这里float转Decimal的精度问题符合预期,但shift(1)的结果并非近似12.3的数值,而是一个极小值,这是核心疑问。

原因解析

Decimal的shift()方法并非实现十进制的位移动,它的实际作用是调整Decimal内部的二进制指数:

  • shift(n)会将Decimal的二进制指数增加n,本质是对数值执行d * (2 ** n)的操作。

两种构造方式的差异

  1. 字符串构造的Decimal:Decimal("1.23")的内部表示基于十进制规则,其二进制指数刚好和十进制位移动的效果对齐,所以shift(1)看起来像是十进制左移1位,但这只是巧合。
  2. float构造的Decimal:float本身是二进制浮点数,1.23无法精确表示,实际存储的二进制值对应的Decimal内部有一个非常大的负二进制指数。此时调用shift(1),只是将这个二进制指数加1,导致数值量级发生非预期变化,最终得到极小值。

正确实现十进制位移动的方式

如果需要实现十进制的左移/右移,应该直接使用十进制的乘除操作:

  • 左移n位:d * Decimal(10) ** n
  • 右移n位:d / Decimal(10) ** n

示例代码:

from decimal import Decimal

d = Decimal(1.23)
print(f"{d * Decimal(10) = }")  # 十进制左移1位

输出:

d * Decimal(10) = Decimal('12.29999999999999982236431606')

这才是预期的近似12.3的结果。

内容的提问来源于stack exchange,提问作者HaroldFinch

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 16:55:02