【问题标题】:Why does Python's float raise ValueError for some very long inputs?为什么 Python 的 float 会为一些很长的输入引发 ValueError?
【发布时间】:2016-06-21 02:52:04
【问题描述】:

在 x64 上的 Python 2.7.9 上,我看到以下行为:

>>> float("10"*(2**28))
inf
>>> float("10"*(2**29))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: could not convert string to float: 10101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010
>>> float("0"*(2**33))
0.0
>>> float("0." + "0"*(2**32))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: could not convert string to float: 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

除非有一些更深层次的理由,否则我错过了这违反了最少的惊喜。当我在 "10"*(2**29) 上得到 ValueError 时,我认为这只是对非常长的字符串的限制,但随后 "0"*(2**33) 起作用了。这是怎么回事?谁能证明为什么这种行为不是 POLA 错误(如果可能是一个相对无关的错误)?

【问题讨论】:

  • 可能是因为解析器在尝试将剩余数字转换为浮点值之前忽略了所有前导零?
  • 我无法重现这个,但那是因为我的系统转储核心试图创建 512MiB '10101010...' 字符串。您是否尝试过将字符串创建为单独的步骤(s = '10' * (2 ** 29) 或其他)并然后转换为float(s)len(s) 的输出也可能提供信息。
  • 顺便说一句:since Python 3.2,“如果参数超出 Python 浮点数的范围,将引发 OverflowError。” Python 2 和更早版本的 Python 3 没有解决这个问题,只是说,“当传入字符串时,可能会返回 NaN 和 Infinity 的值,具体取决于底层 C 库。”

标签: python


【解决方案1】:

因为在推断基数时会跳过零

I like to look to my favourite reference implementation 这样的问题。


证明

Casevh 对 cme​​ts 有很好的直觉。 Here's the relevant code:

for (bits_per_char = -1; n; ++bits_per_char)
    n >>= 1;

/* n <- total # of bits needed, while setting p to end-of-string */
while (_PyLong_DigitValue[Py_CHARMASK(*p)] < base)
    ++p;
*str = p;

/* n <- # of Python digits needed, = ceiling(n/PyLong_SHIFT). */
n = (p - start) * bits_per_char + PyLong_SHIFT - 1;
if (n / bits_per_char < p - start) {
    PyErr_SetString(PyExc_ValueError,"long string too large to convert");
    return NULL;

p 最初设置为指向您的字符串的指针。如果我们查看PyLongDigitValue 表,我们会看到 0 显式映射到 0。

Python 做了很多额外的工作来优化特定碱基的转换 (there's a fun 200 line comment about converting binary!),这就是为什么它首先要做很多工作来推断正确的碱基。在这种情况下;我们可以在推断基数时跳过零,因此它们不计入溢出计算。

确实,我们正在检查存储这个浮点数需要多少位,但是 python 足够聪明,可以从这个计算中删除前导零。我在 float 函数的文档中没有看到任何东西可以保证跨实现的这种行为。他们不祥地声明

如果可能,将字符串或数字转换为浮点数。


什么时候不起作用

当你写作时

   float("0." + "0"*(2**32))

它会提前停止解析基数 - 所有其余的零都被考虑在位长计算中,并有助于提高ValueError


类似的解析技巧

Here's a similar case 在 float 类中,我们发现空白被忽略(以及作者对他们的这种设计选择意图的有趣评论)

while (Py_ISSPACE(*s))    
    s++;

/* We don't care about overflow or underflow.  If the platform
 * supports them, infinities and signed zeroes (on underflow) are    
 * fine. */

【讨论】:

  • 很好的例子,很好的解释。您应该修复int("0x0"+"0"*int(1e1000),16) 的示例,因为它在int(1e1000) 部分上给出了OverflowError,因为1e1000 是inf。 (即使没有,它也比宇宙中的内存要多。)
  • 您可能想在两个 C 代码块之前添加 language: comments,因为 Stack Overflow 试图像 Python 一样对它们进行语法高亮显示。 &lt;!-- language: lang-c --&gt;(向左对齐 --- 不缩进),然后是空行,然后是代码块。
  • @user3047059 我刚刚删除了它 - 我不知道为什么我仍然使用该示例,当手动指定基础时,它完全是一组不同的函数调用。如果您愿意,我可以将其添加回来并提供解释,否则我希望它保持原样
【解决方案2】:

对于 float("10"*(2**29)) 的情况,您将字符串转换为一个浮点值,该值很可能超过 Python 中浮点数的最大值。

而对于 float("0"*(2**33)) 的情况,您将字符串转换为 0.0 的浮点值,而不管您将其乘以多少次。

这个错误不是因为对很长的字符串的限制,而是由于对float的最大值的限制。

请随意查看 What is the maximum float in Python?

【讨论】:

  • 我也是这么想的(见我的 cmets),但是 Python 3.2+ 引发了一个不同的异常,早期版本可以悄悄地返回一个虚假的 float 值,具体取决于给定的字符串.
  • 底壳呢?它也遵守 python 的最大浮动规则,但仍然引发错误..
  • @en_Knight 嗯,错过了那个案子。感谢您对底壳的解释!
猜你喜欢
  • 2021-09-25
  • 1970-01-01
  • 2022-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-11
  • 2015-08-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多