浮子打印不一致。为什么有时会起作用?

时间:2019-04-17 12:07:04

标签: python python-3.x numpy

使用以下示例(近乎最小):

import numpy as np
for x in np.arange(0,2,0.1):
    print(x)

我们得到:

0.0
0.1
0.2
0.30000000000000004
0.4
0.5
0.6000000000000001
0.7000000000000001
0.8
0.9
1.0
1.1
1.2000000000000002
1.3
1.4000000000000001
1.5
1.6
1.7000000000000002
1.8
1.9000000000000001

作为输出。

我知道X.X000001输出应归咎于“浮点数精度问题”,但我不明白的是有时它是如何工作的。显然,不能以浮点数精确地以0.3表示0.3,而且我看不到数字中没有用一位小数位显示的任何模式。

Python为何知道0.1足以显示数字?什么样的魔术告诉它截断剩余的数字?为什么它有时只能工作?

1 个答案:

答案 0 :(得分:4)

您正在打印numpy.float64对象,而不是使用David Gay's dtoa algorithm的Python内置float类型。

version 1.14开始,numpy使用dragon4 algorithm to print floating point values,其调整后的输出与用于Python float类型的David Gay算法相同:

  

numpy标量在str / repr的“唯一”模式(请参见下文)中使用dragon4算法,以尝试匹配python float输出。

numpy.format_float_positional() function对此进行了详细记录:

  

unique:布尔值,可选

     

如果True,请使用数字生成策略,该策略给出最短的表示形式,通过明​​智的舍入,可以从相同类型的其他值中唯一标识浮点数。如果省略了精度,请打印出所有必要的数字,否则精度数字后将中断数字生成,并将剩余值四舍五入。

因此,0.2仅可以通过打印0.2来唯一显示,但是序列(0.30000000000000004)中的下一个值不能显示,您必须包括多余的数字以唯一表示确切的值。

这的方法实际上涉及很多;您可以在Bungie的 Destiny 游戏工程师Ryan Juckett的Printing Floating-Point Numbers series中阅读有关此内容的完整报告。

但是,基本上,输出字符串的代码需要确定所有十进制数字中存在的最短表示形式,这些聚类围绕可能的浮点数而不能解释为下一个或前面的浮点数:

  

floating point number line for 0.1, with the next and previous possible float values and possible representations

此图片来自The Shortest Decimal String That Round-Trips: ExamplesRick Regan,也涵盖了其他一些情况。 蓝色中的数字可能是float64值,绿色中的数字可能是十进制数字的表示形式。请注意灰色的中点标记,任何适合浮动值的两个中点之间的表示都是公平的游戏,因为所有这些表示都会产生相同的值。

David Gay和Dragon4算法的目标都是找到最短的十进制字符串输出,该输出将再次产生完全相同的浮点值。来自Python 3.1 What's New section on the David Gay approach

  

Python现在使用David Gay的算法来查找不会改变其值的最短浮点表示形式。这应该有助于减轻围绕二进制浮点数的某些困惑。

     

使用1.1这样的数字可以很容易地看出其重要性,该数字在二进制浮点数中没有完全等效的值。由于没有精确的等价项,因此类似float('1.1')的表达式求值到最接近的可表示值,即十六进制0x1.199999999999ap+0或十进制1.100000000000000088817841970012523233890533447265625。该最接近的值曾经且仍在以后的浮点计算中使用。

     

新功能是数字的显示方式。以前,Python使用一种简单的方法。 repr(1.1)的值计算为format(1.1, '.17g'),其值为'1.1000000000000001'。使用17位数字的优点是它依靠IEEE-754保证来确保eval(repr(1.1))会精确地往返于其原始值。缺点是许多人发现输出令人困惑(误解了二进制浮点的固有限制)   表示是Python本身存在的问题)。

     

repr(1.1)的新算法更智能,并且返回'1.1'。有效地,它搜索所有等效的字符串表示形式(使用相同的基础浮点值存储的字符串表示形式),并返回最短的表示形式。

     

新算法倾向于在可能的情况下发出更清晰的表示,但不会更改基础值。因此,1.1 + 2.2 != 3.3仍然是这种情况,即使这些表示可能表示相反。