python - Python:浮点数到十进制的转换和随后的移位可能会给出错误的结果
问题描述
当我在 Python 中转换 afloat
并decimal.Decimal
随后调用Decimal.shift
它时,它可能会给我完全错误和意外的结果,具体取决于浮点数。为什么会这样?
转换 123.5 并移动它:
from decimal import Decimal
a = Decimal(123.5)
print(a.shift(1)) # Gives expected result
上面的代码打印了预期的结果1235.0
。
如果我改为转换并转换 123.4:
from decimal import Decimal
a = Decimal(123.4)
print(a.shift(1)) # Gives UNexpected result
它给了我3.418860808014869689941406250E-18
(大约 0),这是完全出乎意料和错误的。
为什么会这样?
注意:由于浮点数在内存中的表示,我理解浮点不精确。但是,我无法解释为什么这会给我这样一个完全错误的结果。
编辑:是的,通常最好不要将浮点数转换为小数,而是将字符串转换为小数。然而,这不是我的问题的重点。我想了解为什么浮点转换后的移位会产生如此完全错误的结果。因此,如果我在换档后print(Decimal(123.4))
给出123.40000000000000568434188608080148696899414062
这样的结果,我希望它1234.0000000000000568434188608080148696899414062
不会接近于零。
解决方案
您需要更改 Decimal 构造函数输入以使用字符串而不是浮点数。
a = Decimal('123.5')
print(a.shift(1))
a = Decimal('123.4')
print(a.shift(1))
或者
a = Decimal(str(123.5))
print(a.shift(1))
a = Decimal(str(123.4))
print(a.shift(1))
输出将如预期的那样。
>>> 1235.0
>>> 1234.0
十进制实例可以从整数、字符串、浮点数或元组构造。从整数或浮点数构造执行该整数或浮点数的值的精确转换。
对于浮点数、小数调用Decimal.from_float()
请注意,Decimal.from_float(0.1) 与 Decimal('0.1') 不同。由于 0.1 在二进制浮点中不能精确表示,因此该值存储为最接近的可表示值,即 0x1.999999999999ap-4。十进制值的精确等值是 0.1000000000000000055511151231257827021181583404541015625。
在内部,Python 十进制库将浮点数转换为两个整数,表示产生浮点数的分数的分子和分母。
n, d = abs(123.4).as_integer_ratio()
然后它计算分母的位长,即表示二进制数所需的位数。
k = d.bit_length() - 1
然后从那里k
通过将分子 * 5 乘以分母的位长的幂来使用位长来记录十进制数的系数。
coeff = str(n*5**k)
结果值用于创建一个新的 Decimal 对象,其构造函数参数为sign
, coefficient
,并exponent
使用此值。
对于浮点数,123.5
这些值为
>>> 1 1235 -1
对于浮点数,123.4
这些值是
1 123400000000000005684341886080801486968994140625 -45
到目前为止,没有任何问题。
但是,当您调用 shift 时,Decimal 库必须根据您指定的班次计算用零填充数字的程度。要在内部执行此操作,它需要用系数长度减去精度。
amount_to_pad = context.prec - len(coeff)
默认精度仅为 28,并且像123.4
系数这样的浮点数变得比上面提到的默认精度长得多。这会创建一个用零填充的负数,并使数字非常小,正如您所指出的。
解决此问题的一种方法是将精度提高到指数长度 + 以 (45 + 4) 开头的数字长度。
from decimal import Decimal, getcontext
getcontext().prec = 49
a = Decimal(123.4)
print(a)
print(a.shift(1))
>>> 123.400000000000005684341886080801486968994140625
>>> 1234.000000000000056843418860808014869689941406250
shift
提示精度对此计算很重要的文档:
第二个操作数必须是 -precision 到 precision 范围内的整数。
然而,它并没有解释这个对内存限制不好的浮点数的警告。
我希望这会引发某种错误并提示您更改输入或提高精度,但至少您知道!
@MarkDickinson 在上面的评论中指出,您可以查看此 Python 错误跟踪器以获取更多信息:https ://bugs.python.org/issue7233
推荐阅读
- swift - 滑动动作延迟
- azure-resource-manager - 如何防止 Microsoft.Automation/automationAccounts/Compilationjobs 始终在 ARM 部署中运行?
- java - 在约束布局中使用线性/相对布局是否不好?
- c# - C# 正则表达式去除包含在 XML 元素中的值
- swagger - 招摇定义占位符?
- python - 获取错误参数项的类型无效,值:类型:
, 有效类型: 在将 S3 加载到 DynamoDB 时 - sqlite - 我们如何将 ORDER BY 和 LIMIT,OFFSET 子句应用于 cn1-data-access 中的 DAO.fetch(query)?
- apache - Apache2,更改虚拟主机中的端口
- java - 将大量键映射到一小组值 - 键不连续
- php - Laravel Group & Count by Eloquent 关系