This compare function is quite odd.
To understand how it works, let's start considering what qsort expects: it expects a function that accepts 2 numbers, a
and b
, and which
returns an integer less than, equal to, or greater than 0, if the first argument is considered respectively less than, equal to, or greater than the second.
In this case, as we will see, it actually returns the opposite result, that is, it returns an integer less than, equal to, or greater than 0, if the second argument is considered respectively less than, equal to, or greater than the first. This is just because the author of this code wants to sort in descending order and not in ascending order.
That said, this compare function is a special one: let's see what it does. Although it works with numbers (it accepts void*
, which is a generic pointer, but then it casts both parameters to int*
and dereferences them), it doesn't compare them numerically, and not even in the classic alphanumeric way.
Rather, it indicates in which order the two numbers, once converted to string, have to be concatenated to generate the sequence of digits that, interpreted as a number, yields the largest possible result (this is hinted at by the name of the other function, largestNumber
). It does so by indicating as largest number the one that must be taken first. It sounds complicated, but some examples will clarify.
Let's take the numbers 3 and 4. If you treat them as strings ("3"
and "4"
) you can concatenate them in two different orders, getting either "34"
or "43"
. Now, convert these strings back to numbers: which one is larger? Clearly, 34 < 43, and if you want the largest number (43) you must take 4 as first number, and 3 as second. In this case the function will tell you that the largest number is 4. It does so by returning a positive number (1, in this case).
Let's take 30 and 4 instead. If you concatenate them you get either "304"
or "430"
. Since 304 < 430, the function tells you that the largest number is 4. So, from this point of view, 4 > 30, which is the opposite of the numeric sort.
If you take 3 and 30, the two possible concatenations are "330"
and "303"
, and since 330 > 303 you have to take 3 first, and the function tells you that 3 > 30. Again, it's the opposite of the numeric sort.
If you take 30 and 30 you have to choose between "3030"
and "3030"
, which are identical, and in this case the function returns 0.
Now a tricky case: if you compare 3 and 33, the two "stringified" numbers are identical ("333"
and "333"
), so we'd expect 0. Actually in this case the function tells you that the largest number is the second one... But it doesn't matter, as the result would be identical.
And how does the comparison work, exactly? First, the numbers are converted to string using sprintf
. Consider that the 2 strings (let's call them "ab"
and "ba"
) must have the same length, so it checks the length of one with strlen
, and then the loop checks every digit, starting from the first. If the digit is the same between ab
and ba
, it does nothing and just goes to the next one; if it is different, it compares them and returns the digit of brr
minus the digit of arr
. For example, when comparing 3 and 30, arr
would contain 330 and brr
303. At the first iteration (when i==0
) it would check the first digit: it's 3 for both, so you can't choose which number is higher yet. Move to the second digit: 3 > 0, so arr
> brr
, so the function must return a negative number, and indeed it does: it returns 0 - 3 = -3
.
So, the function compares a
and b
and returns:
- a negative number if they yield the largest number by concatenating them in this order (a, b);
- 0, if the numbers are the same, so that it doesn't matter in which order they are taken;
- a positive number if they have to be concatenated in the opposite order (b, a) to yield the largest number.
This table shows the result of the classic numeric sort, the classic alphanumeric sort, and this custom sort (which I've called "largest stringified number sort")
a b Numeric sort Alphanumeric sort Largest stringified number sort
3 4 3 < 4 3 < 4 34 < 43 --> return 1 > 0 --> 3 < 4
30 4 30 > 4 30 < 4 304 < 430 --> return 1 > 0 --> 30 < 4
3 30 3 < 30 3 < 30 330 > 303 --> return -3 < 0 --> 3 > 30
3 31 3 < 31 3 < 31 331 > 313 --> return -2 < 0 --> 3 > 31
3 32 3 < 32 3 < 32 332 > 323 --> return -1 < 0 --> 3 > 32
3 33 3 < 33 3 < 33 333 == 333 --> return 30 > 0 --> 3 < 33
3 34 3 < 34 3 < 34 334 < 343 --> return 1 > 0 --> 3 < 34
30 30 30 == 30 30 == 30 3030 == 3030 --> return 0 == 0 --> 30 == 30
As to why they are printed in decreasing order, it's simply because of this line:
return brr[i] - arr[i];
If you want to see them in increasing order, change it to:
return arr[i] - brr[i];