简体   繁体   English

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

[英]Problem trying to use the C qsort function

#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;
}

The result is: 结果是:

-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 -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

It's wrong because the order of -1 and -1.1 is changed. 这是错误的,因为-1和-1.1的顺序被改变了。 I believe it is happening because my "compare" function. 我相信它正在发生,因为我的“比较”功能。

How can I fix this? 我怎样才能解决这个问题?

Thanks 谢谢

Your comparison function is broken. 您的比较功能已损坏。 It says, for example, that -1.0 is equal (equivalent) to -1.1 , since (int) ((-1.0) - (-1.1)) is zero. 例如,它表示-1.0等于(等价) -1.1 ,因为(int) ((-1.0) - (-1.1))为零。 In other words, you yourself told qsort that the relative order of -1.0 and -1.1 does not matter. 换句话说,你自己告诉qsort -1.0-1.1的相对顺序无关紧要。 Why are you surprised that in the resultant ordering these values are not sorted? 为什么你感到惊讶的是,在结果排序中这些值没有排序?

In general, you should avoid comparing numerical values by subtracting one from another. 通常,您应该避免通过从一个减去另一个来比较数值。 It just doesn't work. 它只是不起作用。 For floating-point types it might produce imprecise results for quite a few different reasons, one of which you just observed yourself. 对于浮点类型,由于很多不同的原因,它可能会产生不精确的结果,其中一个原因就是您自己观察到的。 For integer types it might overflow. 对于整数类型,它可能会溢出。

The generic idiom for comparing two numerical values a and b for qsort looks as (a > b) - (a < b) . 用于比较qsort两个数值ab的通用惯用法看起来像(a > b) - (a < b) Remember it and use it. 记住它并使用它。 In your case that would be 在你的情况下,将是

int compare (const void * a, const void * b)
{
  float fa = *(const float*) a;
  float fb = *(const float*) b;
  return (fa > fb) - (fa < fb);
}

In C code it might make perfect sense to define a macro 在C代码中,定义宏可能非常有意义

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

and use it instead of spelling out the comparisons explicitly. 并使用它而不是明确地拼写出比较。

By rounding the difference to the integer you lose the precision. 通过将差异舍入为整数,您将失去精度。

EDIT: 编辑:

Modify the compare function to 修改比较功能

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

Edit for AndreyT: I don't think that returning only 1 or -1 will cause an infinite loop or incorrect ordering (it will just exchange equal values that didn't require it). 为AndreyT编辑:我不认为只返回1-1将导致无限循环或错误排序(它只会交换不需要它的相等值)。

Having an explicit case for returning 0 will cost an additional float compatation, and they are rarely equal. 具有返回0的明确情况将花费额外的浮点数,并且它们很少相等。 So, the comparation for equallity could be omitted if the collision rate in the input data is small. 因此,如果输入数据中的碰撞率很小,则可以省略对等式的比较。

To add to existing answer by @AnT, you can automatically verify your qsort callback via SortChecker : 要通过@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

This warning says that compare reports x < y, y < z and not x < z for some inputs. 此警告表示,对于某些输入, compare报告x < y, y < z而不是x < z To further debug this issue, run with 要进一步调试此问题,请运行

export SORTCHECK_OPTIONS=raise=1

and examine generated codedump. 并检查生成的codedump。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM