为什么 str() 对浮点数进行四舍五入?
当传入带有许多小数的浮点数时,内置的 Python str() 函数会输出一些奇怪的结果。这就是发生的事情:
>>> str(19.9999999999999999)
>>> '20.0'
我期待得到:
>>> '19.9999999999999999'
有人知道为什么吗?也许可以解决它?
谢谢!
The built-in Python str() function outputs some weird results when passing in floats with many decimals. This is what happens:
>>> str(19.9999999999999999)
>>> '20.0'
I'm expecting to get:
>>> '19.9999999999999999'
Does anyone know why? and maybe workaround it?
Thanks!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
并不是
str()
进行了四舍五入,而是您首先使用了浮点数。浮点类型速度快,但精度有限;换句话说,它们的设计不精确。这适用于所有编程语言。有关浮点怪癖的更多详细信息,请阅读“每个程序员应该了解浮点运算的知识”您想要存储
并操作精确的数字,请使用decimal
模块:It's not
str()
that rounds, it's the fact that you're using floats in the first place. Float types are fast, but have limited precision; in other words, they are imprecise by design. This applies to all programming languages. For more details on float quirks, please read "What Every Programmer Should Know About Floating-Point Arithmetic"If you want to store
and operate onprecise numbers, use thedecimal
module:浮点数有 32 位(至少在 C 语言中)。其中一位分配给符号,一些分配给尾数,一些分配给指数。您无法将无限个小数位数放入 32 位中。因此,浮点数在很大程度上基于四舍五入。
如果您尝试
str(19.998)
,它可能会给您至少接近 19.998 的值,因为 32 位有足够的精度来估计该值,但像 19.999999999999999 这样的值太精确,无法在 32 位中估计,因此它四舍五入到最接近的可能值,恰好是 20。A float has 32 bits (in C at least). One of those bits is allocated for the sign, a few allocated for the mantissa, and a few allocated for the exponent. You can't fit every single decimal to an infinite number of digits into 32 bits. Therefore floating point numbers are heavily based on rounding.
If you try
str(19.998)
, it will probably give you something at least close to 19.998 because 32 bits have enough precision to estimate that, but something like 19.999999999999999 is too precise to estimate in 32 bits, so it rounds to the nearest possible value, which happens to be 20.请注意,这是一个理解浮点数(定长)数的问题。大多数语言的功能与 Python 完全相同(或非常相似)。
Python
float
是 IEEE 754 64 位二进制浮点。它的精度限制为 53 位,即略小于 16 位十进制数字的精度。19.9999999999999999
包含18位十进制数字;它不能精确地表示为float
。float("19.9999999999999999")
生成最接近的浮点值,该值恰好与float("20.0")
相同。如果“许多小数”是指“小数点后的许多数字”,请注意,当小数点之前有许多小数位时,会发生相同的“奇怪”结果:
如果您想要完整的
float
精度,不要使用str(),使用repr():更新有趣的是,
decimal
经常被规定为一种治疗方法-全部用于浮动引起的惊讶。这通常伴随着简单的示例,例如0.1 + 0.1 + 0.1 != 0.3
。没有人停下来指出decimal
有其缺陷,例如True,
float
的精度限制为53位二进制数字。默认情况下,decimal
的精度限制为 28 位十进制数字。您可以更改限制,但精度仍然有限。您仍然需要了解数字格式的特征才能有效地使用它,而不会产生“令人惊讶的”结果,并且额外的精度是通过较慢的操作获得的(除非您使用第 3 方
cdecimal
模块)。Please note that this is a problem of understanding floating point (fixed-length) numbers. Most languages do exactly (or very similar to) what Python does.
Python
float
is IEEE 754 64-bit binary floating point. It is limited to 53 bits of precision i.e. slightly less than 16 decimal digits of precision.19.9999999999999999
contains 18 decimal digits; it cannot be represented exactly as afloat
.float("19.9999999999999999")
produces the nearest floating point value, which happens to be the same asfloat("20.0")
.If by "many decimals" you mean "many digits after the decimal point", please be aware that the same "weird" results happen when there are many decimal digits before the decimal point:
If you want the full
float
precision, don't use str(), use repr():Update It's interesting how often
decimal
is prescribed as a cure-all for float-induced astonishment. This is often accompanied by simple examples like0.1 + 0.1 + 0.1 != 0.3
. Nobody stops to point out thatdecimal
has its share of deficiencies e.g.True,
float
is limited to 53 binary digits of precision. By default,decimal
is limited to 28 decimal digits of precision.You can change the limit, but it's still limited precision. You still need to know the characteristics of the number format to use it effectively without "astonishing" results, and the extra precision is bought by slower operation (unless you use the 3rd-party
cdecimal
module).对于任何给定的二进制浮点数,都有一组无限的十进制小数,在输入时,四舍五入为该数字。 Python 的
str
从这个集合中生成最短小数部分遇到了一些麻烦;请参阅 GLS 的论文 http://kurtstephens.com/files/p372-steele.pdf通用算法(IIRC 他们使用了一种改进,在大多数情况下避免了任意精度的数学)。您碰巧输入了一个四舍五入为浮点数(IEEE double)的小数,其最短的可能小数与您输入的不一样。For any given binary floating point number, there is an infinite set of decimal fractions that, on input, round to that number. Python's
str
goes to some trouble to produce the shortest decimal fraction from this set; see GLS's paper http://kurtstephens.com/files/p372-steele.pdf for the general algorithm (IIRC they use a refinement that avoids arbitrary-precision math in most cases). You happened to input a decimal fraction that rounds to a float (IEEE double) whose shortest possible decimal fraction is not the same as the one you entered.