C(++)中双打的排序/比较不稳定?

时间:2012-07-04 13:57:13

标签: c++ floating-point

我遇到了一个非常奇怪的双打问题。我有一个浮点数(双)列表,按降序排序。后来在我的程序中,我发现它们不再完全排序了。例如:

0.65801139819
0.6545651031    <-- a
0.65456513001   <-- b
0.64422968678

中间的两个数字被翻转。有人可能会认为这个问题存在于数字的表示中,而且它们只是打印错了。但是我使用我用来对它们进行排序的相同运算符将每个数字与前一个数字进行比较 - 没有转换为基数10或类似情况:

double last_pt = 0;
for (int i = 0; i < npoints; i++) {
  if (last_pt && last_pt < track[i]->Pt()) {
    cout << "ERROR: new value " << track[i]->Pt()
         << " is higher than previous value " << last_pt << endl;
  }
  last_pt = track[i]->Pt();
}

在排序过程中比较值

bool moreThan(const Track& a, const Track& b) {
  return a.Pt() > b.Pt();
}

我确保它们总是加倍,而不是转换为浮点数。 Pt()返回一个双精度数。列表中没有NaN,排序后我不会触摸列表。

为什么这些,这些数字有什么问题,以及(如何)我可以对数字进行排序以使它们保持排序?

2 个答案:

答案 0 :(得分:7)

您确定在某个时间没有将double转换为float吗?让我们看看这两个数字的二进制表示:

0 01111111110 0100111100100011001010000011110111010101101100010101
0 01111111110 0100111100100011001010010010010011111101011010001001

double中,我们得到1位符号,11位指数和53位尾数,而在float中有1位符号,8位指数和23位尾数。请注意,两个数字中的尾数在前23位是相同的。

根据舍入方法,会有不同的行为。在刚刚修整了比特> 23的情况下,这两个数字float是相同的:

0 011111110 01001111001000110010100 (trim: 00011110111010101101100010101)
0 011111110 01001111001000110010100 (trim: 10010010011111101011010001001)

答案 1 :(得分:1)

您正在比较函数的返回值。浮点返回 值在浮点寄存器中返回,该寄存器具有更高的值 精度比双倍。比较两个这样的值(例如a.Pt() > b.Pt())时,编译器将调用其中一个函数,存储返回值 类型double的未命名临时值中的值(从而舍入 结果到double),然后调用另一个函数,并比较它 结果(仍然在浮点寄存器中,而不是四舍五入到 double)使用存储的值。这意味着你最终可以得到 a.Pt() > b.Pt()b.Pt() > a.Pt()a.Pt() > a.Pt()的情况。这将导致sort变得更加困惑。 (正式地说,如果我们在这里谈论std::sort,这会导致 未定义的行为,我听说过它确实导致核心的情况 倾倒。)

另一方面,你说Pt()“只返回一个双字段”。 如果Pt()没有计算那么多的东西;如果它只是:

double Pt() const { return someDouble; }

,那么这不应该是一个问题(前提是someDouble有类型 double)。扩展精度可以表示所有可能的双精度 价值确切。