I have an array of such structs
typedef union value_
{
double d;
int i;
} value;
typedef struct number_
{
char type;
value val;
}number;
type can be 'i'
or 'd'
, depending on either integer or double value is stored in union field.
The array looks as following:
Integer value: -3
Double value: 4.84
Integer value: -4
Double value: 1.65
Integer value: 1
Double value: 2.54
Integer value: 2
Double value: -0.09
The main thing, is that integers and doubles go one by one. I need to sort this array using qsort in next way: integers should be sorted from lower to higher and doubles vice versa. I use the following function to compare:
int compare(const void *A, const void *B){
number *a = (number*)A;
number *b = (number*)B;
if (a->type == b->type) {
if (a->type =='i') {
return a->val.i - b->val.i;
} else {
if (a->val.d > b->val.d) return -1;
if (a->val.d < b->val.d) return 1;
else return 0;
}
} else {
return 0;
}
}
It is assumed, that if the values in unions have different types, than it returns 0 without comparing, so such sequence, as int,double,int,double should be saved. But the result is following:
Integer value: -1
Integer value: -5
Double value: 0.20
Integer value: 2
Double value: -0.04
Double value: -2.69
Integer value: 4
Integer value: 5
Double value: 2.04
Integer value: 3
Why does it change doubles with integers, if it should return 0 in that case?
If you need to preserve the original list's 'interlocked pattern' of int
and double
types, yet sort each set of int
and double
values, then you can't use the standard qsort
function as is on the complete list.
However, what you can do is to separate the int
and double
entries into new lists, sort those separately, then merge the two sorted lists back into the original, taking either an int
or double
entry from the relevant sorted list, depending on the type of the entry in the original list.
Here's a possible implementation (using the compare
function from your code unchanged – though you'd get better performance for large lists if you made two, type-specific, comparison functions for use with the separated lists):
void printlist(number* list, size_t n)
{
for (size_t i = 0; i < n; ++i) {
if (list[i].type == 'i') printf("%d ", list[i].val.i);
else printf("%lf ", list[i].val.d);
}
printf("\n");
}
int main()
{
number list[] = {
{ 'i', { .i = -3} }, { 'd', { .d = 1.65} },
{ 'i', { .i = -4} }, { 'd', { .d = 4.85} },
{ 'i', { .i = 1} }, { 'd', { .d = -.09} },
{ 'i', { .i = 2} }, { 'd', { .d = 2.54} },
};
size_t Tcount = sizeof(list) / sizeof(*list), Icount = 0, Dcount = 0; // Counts for total, ints and doubles
for (size_t i = 0; i < Tcount; ++i) {
if (list[i].type == 'i') ++Icount;
else ++Dcount;
}
// Display original list ...
printlist(list, Tcount);
number* Ilist = malloc(sizeof(number) * Icount);
number* Dlist = malloc(sizeof(number) * Dcount);
size_t Iindex = 0, Dindex = 0;
// Separate lists ...
for (size_t i = 0; i < Tcount; ++i) {
if (list[i].type == 'i') Ilist[Iindex++] = list[i];
else Dlist[Dindex++] = list[i];
}
// Sort each list separately ...
qsort(Ilist, Icount, sizeof(number), compare);
qsort(Dlist, Dcount, sizeof(number), compare);
// Merge sorted lists ...
Iindex = 0, Dindex = 0;
for (size_t i = 0; i < Tcount; ++i) {
if (list[i].type == 'i') list[i] = Ilist[Iindex++];
else list[i] = Dlist[Dindex++];
}
// Display sorted list ...
printlist(list, 8);
// Clean up ...
free(Ilist);
free(Dlist);
return 0;
}