为什么 str() 对浮点数进行四舍五入?

发布于 2024-11-01 13:17:32 字数 259 浏览 1 评论 0原文

当传入带有许多小数的浮点数时,内置的 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

月依秋水 2024-11-08 13:17:32

并不是 str() 进行了四舍五入,而是您首先使用了浮点数。浮点类型速度快,但精度有限;换句话说,它们的设计不精确。这适用于所有编程语言。有关浮点怪癖的更多详细信息,请阅读“每个程序员应该了解浮点运算的知识

”您想要存储并操作精确的数字,请使用decimal模块:

>>> from decimal import Decimal
>>> str(Decimal('19.9999999999999999'))
'19.9999999999999999'

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 on precise numbers, use the decimal module:

>>> from decimal import Decimal
>>> str(Decimal('19.9999999999999999'))
'19.9999999999999999'
莫相离 2024-11-08 13:17:32

浮点数有 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.

甚是思念 2024-11-08 13:17:32

请注意,这是一个理解浮点数(定长)数的问题。大多数语言的功能与 Python 完全相同(或非常相似)。

Python floatIEEE 754 64 位二进制浮点。它的精度限制为 53 位,即略小于 16 位十进制数字的精度。 19.9999999999999999包含18位十进制数字;它不能精确地表示为floatfloat("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')
>>>

True,float的精度限制为53位二进制数字。默认情况下,decimal 的精度限制为 28 位十进制数字。

>>> Decimal(2) / Decimal(3)
Decimal('0.6666666666666666666666666667')
>>>

您可以更改限制,但精度仍然有限。您仍然需要了解数字格式的特征才能有效地使用它,而不会产生“令人惊讶的”结果,并且额外的精度是通过较慢的操作获得的(除非您使用第 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 a float. float("19.9999999999999999") produces the nearest floating point value, which happens to be the same as float("20.0").

>>> float("19.9999999999999999") == float("20.0")
True

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:

>>> float("199999999999999999")
2e+17

If you want the full float precision, don't use str(), use repr():

>>> x = 1. / 3.
>>> str(x)
'0.333333333333'
>>> str(x).count('3')
12
>>> repr(x)
'0.3333333333333333'
>>> repr(x).count('3')
16
>>>

Update It's interesting how often decimal is prescribed as a cure-all for float-induced astonishment. This is often accompanied by simple examples like 0.1 + 0.1 + 0.1 != 0.3. Nobody stops to point out that decimal has its share of deficiencies e.g.

>>> (1.0 / 3.0) * 3.0
1.0
>>> (Decimal('1.0') / Decimal('3.0'))  * Decimal('3.0')
Decimal('0.9999999999999999999999999999')
>>>

True, float is limited to 53 binary digits of precision. By default, decimal is limited to 28 decimal digits of precision.

>>> Decimal(2) / Decimal(3)
Decimal('0.6666666666666666666666666667')
>>>

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).

极致的悲 2024-11-08 13:17:32

对于任何给定的二进制浮点数,都有一组无限的十进制小数,在输入时,四舍五入为该数字。 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文