在双精度列表中找到最大一个数字,需要关心精度吗?

find the max one number in a list of double, need to care about precision?

提问人:bugs king 提问时间:10/27/2016 更新时间:10/28/2016 访问量:83

问:

假设有一个向量,需要找出最大数。我的同事告诉我,必须以一种特殊的方式处理双数才能做到这一点,他的代码是这样的:

double max = v[0];
for (int i = 0; i < v.size(); ++i) {
   if (compare(max, v[i]) < 0) max = v[i];
}

int compare(double a, double b) {
    double z = a - b;
    if (z < -0.000001) return -1;
    if (z > 0.000001) return 1;
    return 0;
}

我不认为它需要那么复杂,简单地使用“<”或“>”就可以完成这项工作,它不应该关心平等。但我的同事坚持认为,必须与 epsilon 进行比较才能找出最大数字。这是真的吗?

C++ 算法 浮点精度

评论

4赞 n. m. could be an AI 10/27/2016
您的同事需要被禁止接触浮点计算。你可以给他看这个评论。他的代码可以在他从未注意到的情况下找到一个最小的元素而不是一个最大的元素。
3赞 Some programmer dude 10/27/2016
为了找出数组或向量的最大值,您不需要这样的比较。您甚至可以(并且可能应该)使用 std::max_element 函数。

答:

1赞 Slava 10/27/2016 #1

你的同事错了,在这种情况下,操作结果就足够了。如果您需要找出容器中有多少最大元素等,则可能需要与 epsilon 进行比较。仅仅找到一个最大元素,简单的比较就足够了。<

评论

0赞 kgf3JfUtW 10/27/2016
请问,如果我们需要找出容器中有多少最大元素,为什么需要 epsilon?是因为多个相同的最大数可能无法通过相等性检验吗?谢谢。
1赞 user4581301 10/27/2016
@SamShen 读起来很多,但前十页左右应该回答:每个计算机科学家都应该知道的关于浮点运算的知识
3赞 Jerry Coffin 10/27/2016 #2

为了找到最大的数字,你是对的,一个简单的就足够了。<

你的同事正在考虑的(无论如何,我们希望)是处理浮点数的相等性。例如,如果您以两种不同的方式计算应该相同的值,您可能很容易看到两者之间的一些微小差异 - 偶数与。 可以改变结果(即使从数学角度来看,两者应该是相同的)。(a+b)+ca+(b+c)

但是,当您执行此操作时,您通常希望根据数字的大小来缩放数字之间允许的差异。

例如,让我们考虑一个可以表示大约 15 个有效数字。double

如果您的数字约为 1e+200,那么可以表示的这两个数字之间的最小差异约为 1e+185。询问差值是否小于 0.000001 是没有意义的——要么结果是相同的,要么差值比这大得多

相反,如果你的数字在 1e-200 左右,那么它们之间可以表示的最小差异将在 1e-215 左右。0.000001 的差异(再次)是完全荒谬的——只有当两个计算中的一个错误了 ~196 个数量级1 时,才会发生这种情况。

因此,要做到这一点,你需要在小数点后选择一些需要匹配的位,以便你认为两者相等,并将其乘以数字得到最大增量。例如,如果您决定他们需要同意 7 位小数,并且数字在 1eN 范围内,则最大差异为 1e(N-7)。如果 N 为 100,则最大增量为 1e93。如果 N 为 -150,则最大增量为 1e-157。

这仍然需要非常小心地使用,尤其是在处理数字组时。问题在于,像这样近似相等的不再是传递的。即使这些数字在 0.000001 的 Epsilon 可能有意义的范围内,它也可以说 A == B 和 B == C,但 A != C。委婉地说,这可能会导致相当令人惊讶的结果(在某些情况下,如排序,可能会导致完全失败,因为你违反了严格的弱排序要求)。

就你最初在向量中寻找最大值的问题而言,这基本上意味着只要使用就会找到实际上最大的值。但是,根据舍入误差,可能还有其他值较小,但理论上应该更大。根据这些结果的计算方式以及您要完成的任务,您可能不仅要考虑查找最大的单个值,还要考虑查找在所选最大最大误差范围内的所有其他值。其他值不是那么大,但足够接近,可以代表最大的度量值(或您正在使用的任何值)。<

还有一点可能值得一提:一些浮点格式包含“不是数字”的表示形式。这将产生所有可能的比较(事实上,检测 NaN 的常用方法是 )。因此,如果您的输入值可能包含 NaN,则看似相同的比较可能会给出完全不同的结果。例如,和通常应该相同,但如果 or 是 NaN,则它们不会相同。falseif (x != x) /* it's a NaN */if x < yif not y >= xxy


  1. 为了正确看待 196 个数量级,让我们假设您正在进行计算,试图将中子的大小与质子的大小进行比较。然后你决定检查你得到的这两个大小之间的差异是否大于银河系的直径。

    哦,但这不会是 196 个数量级。这只有大约 36 个数量级。因此,让我们检查一下我们得到的差异是否大于(目前认为的)宇宙直径。这使我们达到了大约 50 个数量级。

    不过,我想我有点失败了——即使我们看一下弦理论归因于“弦”的最小尺寸,并将其与宇宙的(普遍接受的)大小进行比较,两者甚至相距不到196个数量级。更糟糕的是,我怀疑一根弦的大小或已知宇宙的大小是任何人都能真正有意义地想象出来的。一个太小,另一个太大,任何人都无法真正理解,两者之间的差异仍然远远低于我们正在谈论的差异。