BUG: qsort reorders elements in already sorted array

J. Johnston jjohnstn@redhat.com
Wed Jun 4 16:24:00 GMT 2003


Corinna Vinschen wrote:
> Hi,
> 
> I found that there's a bug in newlib's implementation of qsort, which
> results in more or less unreliable reordering of things.
> 
> The problem seem to occur in border cases only.  The border case I
> have is tin, the news reader.  I've build the latest developer version
> of tin, 1.5.18, and found that the ordering of threads in a newsgroup
> was correct, except to my surprise that the first and the last thread
> had exchanged their place.  As a figure, the order wasn't
> 
>   0 1 2 3 4 5 6 7 8 9
> 
> but
> 
>   9 1 2 3 4 5 6 7 8 0
> 
> This happened in all newsgroups, except for one, which had only 7 articles.
> 

This is not a bug.

ANSI specifies the following for qsort:

   "If two elements compare as equal, their order in the sorted array is unspecified."

If you need a particular ordering of two elements, then the compare function has to
identify them as unequal.

I believe the 7 article case is caused by the fact that the code has if logic for < 7
and > 7, but not for == 7.

I am not seeing the extraneous values you are seeing.  This requires further investigation.

-- Jeff J.

> But what's the border case here?  By default, the threads are ordered
> by "score", a function of the state of the articles in the thread.
> Unfortunately it's very typical, that the score of all threads is 0.
> As a result, qsort's compare function returns 0 for all comparisons
> between elements in the array.  Even worse, since the array of articles
> is already ordered by date before, it's very likely that the threads are
> pretty much already in the right order before calling qsort on them.
> 
> To test that phenomenon, I created a very simple test case:
> 
>     #include <stdio.h>
>     #include <stdlib.h>
> 
>     int
>     cmp (const void *p1, const void *p2)
>     {
>       return 0;
>     }
> 
>     int
>     main (int argc, char **argv)
>     {
>       int i, *a, size = 10;
> 
>       if (argc > 1 && atoi(argv[1]) > 0)
> 	size = atoi(argv[1]);
>       a = (int *) malloc (sizeof(int) * size);
>       for (i = 0; i < size; ++i)
> 	a[i] = i;
>       qsort (a, size, sizeof(int), cmp);
>       for (i = 0; i < size; ++i)
> 	printf ("%d ", a[i]);
>       printf ("\n");
>       return 0;
>     }
> 
> 
> which allows to reproduce the problem with a minimum of code.  Let's
> have a look into the results (here on Cygwin):
> 
>     $ gcc -g sort.c -o sort
> 
>     $ ./sort 5
>     0 1 2 3 4
> 
>     $ ./sort 6
>     0 1 2 3 4 5 
> 
>     $ ./sort 7
>     0 1 2 3 4 5 6 
> 
>     $ ./sort 8
>     2945 1 2 3 4 5 6 7 
> 
> Huh?
> 
>     $ ./sort 9
>     2945 1 2 3 4 5 6 7 8 
> 
> What's that?
> 
>     $ ./sort 10
>     9 1 2 3 4 5 6 7 8 0 
> 
> Ah, now the effect described above is visible...
> 
>     $ ./sort 11
>     10 1 2 3 4 5 6 7 8 9 0
> 
>     $ ./sort 50
>     49 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 0 
> 
> ...and reproducible for any number of elements >= 10.
> 
> Unfortunately I haven't worked out what the actual problem in the
> qsort implementation is.  I'm still debugging but I thought, somebody
> with a more intimate relation to qsort might have a clue.  Btw., I'm
> suspecting the med3 function to scamble things when the compare function
> returns 0 for all tested elements pl, pm, pn.
> 
> 
> Corinna
> 




More information about the Newlib mailing list