使用以下示例(近乎最小):
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足以显示数字?什么样的魔术告诉它截断剩余的数字?为什么它有时只能工作?
答案 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中阅读有关此内容的完整报告。
但是,基本上,输出字符串的代码需要确定所有十进制数字中存在的最短表示形式,这些聚类围绕可能的浮点数而不能解释为下一个或前面的浮点数:
此图片来自The Shortest Decimal String That Round-Trips: Examples的Rick 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
仍然是这种情况,即使这些表示可能表示相反。