内置的Python str()函数在传入许多小数位数的浮点数时会输出一些奇怪的结果。具体如下:
>>> str(19.9999999999999999)
>>> '20.0'
我希望能获得:
>>> '19.9999999999999999'
有人知道为什么吗?也许有解决方法吗?
谢谢!
不是 str()
函数进行了四舍五入,而是你在首次使用浮点数时就存在精度限制;换句话说,它们是有意设计成不精确的。这适用于所有编程语言。要了解更多关于浮点数的细节,请阅读 "What Every Programmer Should Know About Floating-Point Arithmetic"。
如果你想存储和操作精确数字,请使用 decimal
模块:
>>> from decimal import Decimal
>>> str(Decimal('19.9999999999999999'))
'19.9999999999999999'
str(19.998)
,它可能会给出至少接近于19.998的值,因为32位具有足够的精度来估计它,但像19.999999999999999这样的值过于精确,无法用32位来估算,因此会四舍五入到最接近的可能值,这恰好是20。float
Тў»C double
№╝їжђџтИИСИ║64СйЇсђѓ - zwolfloat
是IEEE 754 64位二进制浮点数。它仅限于53位精度,即略少于16位十进制精度。19.9999999999999999
包含18个十进制数字;它无法准确地表示为float
。 float("19.9999999999999999")
产生最接近的浮点值,恰好与float("20.0")
相同。>>> float("19.9999999999999999") == float("20.0")
True
如果你说“许多小数位”指的是“小数点后面的许多数字”,请注意,当小数点前面有许多小数位时,同样会出现“奇怪”的结果:
>>> float("199999999999999999")
2e+17
如果你想要完整的 float
精度,不要使用 str(),而是使用 repr():
>>> x = 1. / 3.
>>> str(x)
'0.333333333333'
>>> str(x).count('3')
12
>>> repr(x)
'0.3333333333333333'
>>> repr(x).count('3')
16
>>>
更新 有趣的是,人们经常将decimal
描述为解决浮点数问题的灵丹妙药。这通常伴随着简单的例子,比如0.1 + 0.1 + 0.1 != 0.3
。但是没有人停下来指出decimal
也有其自身的缺陷。
>>> (1.0 / 3.0) * 3.0
1.0
>>> (Decimal('1.0') / Decimal('3.0')) * Decimal('3.0')
Decimal('0.9999999999999999999999999999')
>>>
确实,float
只有53位二进制数字的精度。默认情况下,decimal
的精度被限制在28个十进制数字。
>>> Decimal(2) / Decimal(3)
Decimal('0.6666666666666666666666666667')
>>>
你可以更改限制,但仍然有有限的精度。如果要有效地使用它而不产生“惊人”的结果,你仍需要了解数字格式的特性,而额外的精度是通过更慢的操作购买的(除非你使用第三方cdecimal
模块)。
str
会尽可能地从这个集合中生成 最短 的十进制小数;有关通用算法,请参阅 GLS 的论文 http://kurtstephens.com/files/p372-steele.pdf(如果我没有记错,它们使用了一种避免在大多数情况下使用任意精度数学的优化)。你恰好输入了一种舍入为 IEEE 双精度浮点数的小数分数,其最短可能的十进制小数与你输入的不同。
Decimal
也有限制精度(默认为28个小数位)。它并不是万能的解决方案。请查看我的更新答案。 - John Machinstr
也会四舍五入(在REPL中比较0.9999999999999999
和str(0.9999999999999999)
)。这可以通过使用repr
来避免(尽管它无法解决由于浮点数不精确而导致的四舍五入问题)。 - Nathaniel Verhaaren