6

有一个关于 Python 中浮点数(和精度)的底层数据结构的问题:

>>> 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 的值取决于机器,它们是最接近目标值但不完全相同的数字。我受到监督,我们通过“打印”获得了“正确”的数字,有人告诉我这是因为打印“谎言”,而 Python 选择告诉我们真相,即准确显示它们存储的内容。

我的问题是:

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

2. 为什么我们不能只存储实际数字而不是存储最接近的数字?是限制还是换取效率?

非常感谢约翰

4

6 回答 6

7

对于第一个问题的答案,请查看 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”。

于 2011-07-18T23:20:18.637 回答
2

您应该阅读臭名昭著的论文:

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

单击“缓存”链接以下载 PDF 格式的论文。

于 2011-07-18T23:31:42.063 回答
1

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

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

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

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

因此,例如,如果:

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

那么你可以认为它们是平等的。您要考虑多少位数取决于浮点数的精度,即您使用的是单精度数还是双精度数,以及您为达到这些数字所做的计算。由于每次计算都会累积精度限制,因此您可能需要降低阈值以使其相等。

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

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

于 2011-07-18T23:29:04.213 回答
0

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

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

于 2011-07-18T23:05:14.913 回答
0

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

于 2011-07-18T23:21:27.453 回答
0

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

看看人们在此处发布的一些链接并阅读有关浮点数的信息……不过这有点吓人,您将不再信任机器。

于 2011-07-18T23:31:37.710 回答