1

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

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,排序后我没有触摸列表。

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

4

2 回答 2

7

你确定你没有转换doublefloat某个时间?让我们看一下这两个数字的二进制表示:

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)
于 2012-07-04T14:13:40.243 回答
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有 type double)。扩展精度可以精确地表示所有可能的双精度值。

于 2012-07-04T14:49:46.867 回答