Qsort和比较器的奇怪行为。C

4

所以,我在我的C程序中使用了C库中的qsort。它按预期工作,所以我决定尝试比较器。

比较器1(我使用这个):

 int compare (const void * a, const void * b)
{
  if (*(double*)a > *(double*)b) return 1;
  else if (*(double*)a < *(double*)b) return -1;
  else return 0;  
}

比较器2:

int comp (const void *a, const void *b)
{
    const double *ia = (const double *)a; // casting pointer types 
    const double *ib = (const double *)b;
    return *ia  - *ib; 
}

第一个代码按照我的预期工作。第二个代码应该和第一个一样。我想使用第二个代码因为程序运行速度稍快,但问题是它并没有真正排序任何东西!我相信我之前在较小的数组上使用过比较器#2并且它也起作用了。除非我漏掉了什么。

考虑到qsort()函数通过指针调用比较器的开销,除非在极为特定的情况下,否则很难相信这种棘手的“优化”比较器即使被更正以实际工作也能显著提高性能。这个简单函数的性能可能会被那个开销主导。 - Michael Burr
尝试将 return *ia - *ib; 替换为 double diff = *ia - *ib; return *(int*)((char*)&diff+4); 并查看其性能。这非常依赖于具体实现,不完整且不建议使用,但在小端字节序且 sizeof(int) = 4 的情况下应该可以工作。它只返回 diff 的最高 32 位字,应该设置符号位。还假设该字的位内容对于正差异不为零。 - nnn
1个回答

7
第二个函数应该和第一个函数做相同的事情。
乍一看,它应该可以,但仔细检查后发现不行。
例如,考虑比较5.3和4.9。很明显,第一个数字大于第二个数字;然而,将一个数字减去另一个数字会产生0.4,这会在转换为int时向下舍入为零,告诉qsort 5.3和4.9彼此相等。
你需要做的是对两个参数的差值应用符号函数。不幸的是,C标准没有定义这个函数;请参见这个Q&A,其中有几个好的解决方法

@Mpr.Moe 没错,你不能返回一个 double 代替一个 int,因为 qsort 已经被编译成假设你给它的函数指针将返回一个 int。打破这个假设会产生未定义的行为。不过,链接的 Q&A 中的技巧可以解决这个问题。 - Sergey Kalinichenko
1
我读过了。无论如何,会有任何性能提升吗?我想避免比较,现在我们又回到了这个话题。我错过了什么吗? - Mpr. Moe
@Mpr.Moe 比较并不像条件执行那样糟糕。你的第一个代码片段使用了三元运算符 ? :,它具有条件执行功能。比较和减法不使用条件执行,因此性能略好一些。尽管如此,这都是理论上的,因为在实践中你几乎看不到任何差异。 - Sergey Kalinichenko
天哪,我不敢相信他们没有正确地实现这个。事实上,符号函数的方法比第一个比较器稍微慢一点(大约慢了5%)。现在,如果我像在我的 OP 中使用第二个比较器,收益是惊人的。从11秒降到3秒!太遗憾它不起作用。 - Mpr. Moe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接