浮点精度——python中浮点数的底层数据结构



有一个关于Python中float(和precision)的底层数据结构的问题:

>>> b = 1.4 + 2.3
>>> b
3.6999999999999997
>>> c = 3.7
>>> c
3.7000000000000002
>>> print b, c
3.7  3.7
>>> b == c
False

看起来b和c的值是机器相关的,它们是最接近目标值的数字,但不是完全相同的数字。有人监督我们使用"Print"来得到"正确"的数字,有人告诉我这是因为Print"谎言",而Python选择告诉我们真相,即准确显示它们存储的内容。

我的问题是:

1. 如何说谎?例如,在一个函数中,我们取两个值并返回,如果它们是相同的,我怎么能有一个最好的猜测,如果小数(精度)是未知的?就像上面提到的b和c一样?是否有一个定义良好的算法来做到这一点?有人告诉我,如果我们涉及浮点计算,每种语言(C/c++)都会有这种问题,但他们如何"解决"这个问题?

2. 为什么我们不能只存储实际的数字而要存储最接近的数字呢?这是一种限制还是效率交易?

多谢John

对于第一个问题的答案,请查看Python源代码中的以下代码(稍微压缩):

#define PREC_REPR       17
#define PREC_STR        12
void PyFloat_AsString(char *buf, PyFloatObject *v) {
    format_float(buf, 100, v, PREC_STR);
}
void PyFloat_AsReprString(char *buf, PyFloatObject *v) {
    format_float(buf, 100, v, PREC_REPR);
}

所以基本上,repr(float)将返回一个精度为17位的字符串,str(float)将返回一个精度为12位的字符串。您可能已经猜到,print使用str(),在解释器中输入变量名使用repr()。只有12位的精度,看起来你得到了"正确"的答案,但这只是因为你的期望和实际值在12位以内是相同的。

下面是一个简单的例子:

>>> str(.1234567890123)
'0.123456789012'
>>> repr(.1234567890123)
'0.12345678901230001'

关于你的第二个问题,我建议你阅读Python教程的以下部分:浮点算术:问题和限制

它归结为效率,更少的内存和更快的浮点运算,当你存储以2为基数的10小数比任何其他表示,但你确实需要处理不精确。

正如JBernardo在评论中指出的那样,这种行为在Python 2.7及以上版本中是不同的,以下引用自上述教程链接描述了差异(以0.1为例):

在Python 2.7和Python 3.1之前的版本中,Python四舍五入值为17位有效数字,给出' 0.10000000000000001 '。在在当前版本中,Python会根据最短值显示一个值正确舍入回真实二进制值的十进制小数,结果就是' 0.1 '。

你应该看看那篇臭名昭著的论文:

每个计算机科学家都应该知道的关于浮点运算的知识

点击"缓存"链接下载PDF格式的论文。

您在计算中会得到不同的结果,因为数字1.4和2.3也没有得到精确的表示。在添加它们时,也累积了它们的精度限制。

所有浮点数的精度都是有限的,而且由于浮点数通常在内部表示的方式(使用以2为基数而不是以10为基数),这些限制适用于我们人类认为容易精确表示的数字。

有限的精度对于计算来说很少是一个问题,因为精度对于大多数应用来说仍然是足够的。另一方面,在比较浮点数时,必须考虑有限的精度。

这通常是通过减去数字来完成的,并检查与数字相比差异是否足够小。

因此,例如,if:

abs(b - c) < abs(b) / 1000000000000

则可以认为它们相等。你想要考虑多少位数字取决于浮点数的精度,即,如果你使用单精度或双精度数字,以及你做了什么计算来达到这些数字。由于精度限制随着每次计算而累积,因此当它们被认为相等时,可能需要降低阈值。

显示浮点数时,根据其精度对其进行舍入。例如,如果它能够准确地表示15位数字,则可以在显示之前将其四舍五入到13位。

浮点数用于快速计算。还有其他数据类型,比如Decimal,可以精确地存储数字。例如,它们用于存储货币值。

浮点数是不精确的;这是表示法的一个方面。有很多关于为什么会这样的信息;我只想说,在几乎所有提供浮点数的平台上,这都是一个问题。

处理不精确的最好方法是有一个置信区间;也就是说,比较两个计算的浮点数是否相等可能会有问题,因为表示可能会有很小的差异,所以处理这个问题的方法是将它们两个相减,并确保差异不超过一个小的量。许多库已经为浮点数内置了这种功能,但是在有疑问的时候自己实现它并不难。

这个讲座很好地了解了变量是如何存储在内存中的,教授还提供了一个示例,该示例将给出您所看到的意想不到的结果。
http://www.youtube.com/watch?v=jTSvthW34GU如果您需要比较这两个数字,首先将它们转换为整数,如果执行测试,您将注意到它们确实相等。

所有数字都存储在有限的位数上,因此不能只存储实际数字,而必须使用存储最接近的数字(想象一个分数1/3,如果你想用十进制数字将其存储在纸上,你将耗尽世界上的树木资源)。另一种选择是符号表示,你可以在Mathematica中找到,它只是将1/3存储为13,但它离机器很远,使计算更慢,更复杂。

看看人们在这里发布的一些链接,阅读有关浮点数的内容…这有点可怕,你不会再相信机器了。

最新更新