尝试使用 C qsort 函数时出现问题

Posted

技术标签:

【中文标题】尝试使用 C qsort 函数时出现问题【英文标题】:Problem trying to use the C qsort function 【发布时间】:2011-04-22 15:14:42 【问题描述】:
#include <stdio.h>
#include <stdlib.h>

float values[] =  4, 1, 10, 9, 2, 5, -1, -9, -2,10000,-0.05,-3,-1.1 ;

int compare (const void * a, const void * b)

    return ( (int) (*(float*)a - *(float*)b) );


int main ()


    int i;

    qsort (values, 13, sizeof(float), compare);

    for (i = 0; i < 13; i++)
    
        printf ("%f ",values[ i ]);
    
    putchar('\n');

    return 0;

结果是:

-9.000000 -3.000000 -2.000000 -1.000000 -1.100000 -0.050000 1.000000 2.000000 4.000000 5.000000 9.000000 10.000000 10000.000000

这是错误的,因为-1和-1.1的顺序改变了。 我相信它正在发生,因为我的“比较”功能。

我该如何解决这个问题?

谢谢

【问题讨论】:

qsort 工作正常。您对 qsort 的 调用 已中断。 【参考方案1】:

通过将差值四舍五入为整数,您将失去精度。

编辑:

修改比较函数为

return (*(float*)a &gt;= *(float*)b) ? 1 : -1;

为 AndreyT 编辑:我认为仅返回 1-1 不会导致无限循环或不正确的排序(它只会交换不需要的相等值)。

有一个明确的情况来返回0 将花费额外的浮点兼容性,并且它们很少相等。因此,如果输入数据中的碰撞率很小,则可以省略相等性的比较。

【讨论】:

不会工作。对于相等的值,此函数将返回 -1,这意味着对于相等的 ab 比较 ab 会说 a &lt; b,但比较 ba 会说 @ 987654333@。 qsort 无法正常使用此类比较功能。 你的编辑没有改变任何东西,除了现在相等的值将总是返回1。标准qsort 设计用于作为三值函数的比较器。无论您做什么,通常都不可能将其简化为二值函数。你必须返回-1, 0, +1 qsort 的调试实现检查比较函数的正确性并不罕见。如果您的比较函数将返回1 以进行(a, b) 比较,同时返回1 以进行(b, a) 比较,则此类调试qsort 实现通常会因断言失败而立即中止。非调试实现只会产生未定义的行为。【参考方案2】:

您的比较功能已损坏。例如,它说-1.0 等于(等价于)-1.1,因为(int) ((-1.0) - (-1.1)) 为零。换句话说,您自己告诉qsort -1.0-1.1 的相对顺序无关紧要。为什么您会对结果排序中的这些值没有排序感到惊讶?

一般来说,您应该避免通过将数值相减来比较数值。它只是行不通。对于浮点类型,由于很多不同的原因,它可能会产生不精确的结果,其中之一是您自己观察到的。对于整数类型,它可能会溢出。

比较两个数值ab 的通用习语qsort 看起来像(a &gt; b) - (a &lt; b)。记住它并使用它。在你的情况下,那将是

int compare (const void * a, const void * b)

  float fa = *(const float*) a;
  float fb = *(const float*) b;
  return (fa > fb) - (fa < fb);

在 C 代码中,定义宏可能非常有意义

#define COMPARE(a, b) (((a) > (b)) - ((a) < (b)))

并使用它而不是明确说明比较。

【讨论】:

+1 需要更多的加号,这需要被接受为答案。 return (fa &gt; fb) - (fa &lt; fb) 很优雅,但return (fa &lt; fb) ? -1 : (fa &gt; fb); 可能更快。 YMMV。 @chux:为什么会更快? @chux:只有低质量的编译器在执行(fa &gt; fb) - (fa &lt; fb) 时才会执行两次比较。大多数 CPU 使用设置某些 CPU 状态标志的 CPU 指令来比较值。这些状态标志完全描述了比较的结果。单个fa vs. fb 比较会生成涵盖fafb 之间所有关系比较的标志。 IE。一项比较立即为您提供fa &gt; fbfa &lt; fb 的答案。所需要的只是从 CPU 标志中提取这些结果并执行减法。 @chux:您的(fa &lt; fb) ? -1 : (fa &gt; fb) 可能正在分支。它正在分支的事实表明它最终可能会慢得多。【参考方案3】:

要添加到@AnT 的现有答案,您可以通过SortChecker 自动验证您的qsort 回调:

$ LD_PRELOAD=$HOME/sortcheck-master/bin/libsortcheck.so ./a.out
a.out[7133]: qsort: comparison function is not transitive (comparison function 0x4005cd (/home/iuriig/a.out+0x4005cd), called from 0x400614 (/home/iuriig/a.out+0x400614), cmdline is "./a.out")
-9.000000 -3.000000 -2.000000 -1.000000 -1.100000 -0.050000 1.000000 2.000000 4.000000 5.000000 9.000000 10.000000 10000.000000

此警告表示 compare 报告某些输入的 x &lt; y, y &lt; z 而不是 x &lt; z。要进一步调试此问题,请运行

export SORTCHECK_OPTIONS=raise=1

并检查生成的编码转储。

【讨论】:

以上是关于尝试使用 C qsort 函数时出现问题的主要内容,如果未能解决你的问题,请参考以下文章

使用 qsort 进行错误排序 - C

从 C 调用汇编函数时出现分段错误错误

从 C# 调用 C++ dll 函数时出现问题

当我尝试从 main 调用我的类模板函数时出现错误

将成员函数传递给模板函数时出现语法错误

尝试初始化结构数组时出现段错误