为什么str()会对浮点数进行向上取整?
Python里面有个内置的str()函数,当你把很多小数位的浮点数传进去时,它会输出一些奇怪的结果。下面是发生的情况:
>>> str(19.9999999999999999)
>>> '20.0'
我本来期待得到的是:
>>> '19.9999999999999999'
有没有人知道这是为什么?有没有什么解决办法?
谢谢!
4 个回答
请注意,这个问题是关于理解浮点数(固定长度数字)的。大多数编程语言的处理方式和Python非常相似。
Python中的float
是遵循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
模块)。
在计算机中,一个浮点数占用32个比特位(至少在C语言中是这样)。其中有一个比特位用来表示符号(正负),一些比特位用来表示尾数,还有一些比特位用来表示指数。由于32个比特位的限制,不能把每一个小数都精确地表示出来,所以浮点数的表示方式通常会涉及到四舍五入。
比如你试着运行 str(19.998)
,它可能会给你一个接近19.998的结果,因为32个比特位足够精确来估算这个值。但是像19.999999999999999这样的数字就太精确了,32个比特位无法准确表示,所以它会四舍五入到最接近的值,也就是20。
其实不是 str()
这个函数在进行四舍五入,而是因为你使用了浮点数。浮点数的运算速度很快,但它们的精确度有限;换句话说,浮点数本身就是设计成不够精确的。这种情况在所有编程语言中都是一样的。如果你想了解更多关于浮点数的奇怪之处,可以看看这篇文章:"每个程序员都应该知道的浮点运算知识"
如果你想存储和处理精确的数字,建议使用 decimal
模块:
>>> from decimal import Decimal
>>> str(Decimal('19.9999999999999999'))
'19.9999999999999999'