user3497957
user3497957

Reputation: 31

PHP sort seems to malfunction

### Result of sort 

sort($keys_arranged, SORT_NUMERIC);
var_dump($keys_arranged);

{ ...
[51]=> float(11.903327742296) 
[52]=> int(5) 
[53]=> float(13.165002546636) 
[54]=> float(14.478273306964) 
[55]=> float(4.6264742674547) 
[56]=> float(13.290508819344) 
[57]=> float(15.686809055276) } 

### Result or rsort

rsort($keys_arranged, SORT_NUMERIC);
var_dump($keys_arranged);

{
[0]=> float(15.686809055276) 
[1]=> float(14.478273306964) 
[2]=> float(13.290508819344)
[3]=> float(13.165002546636) 
[4]=> float(11.903327742296) 
[5]=> int(5) 
[6]=> float(4.6264742674547)
... }

echo var_export($keys_arranged);

array ( 0 => 3.142678516658294, 1 => 1.0, 2 => 1.0, 3 => 14.478273306963985, 4 => 13.165002546635966, 5 => 1.0, 6 => 1.0005037081114851, 7 => 1.0, 8 => 4.6264742674547001, 9 => 15.686809055275578, 10 => 1.0, 11 => 11.903327742295504, 12 => 13.29050881934397, 13 => 1.0, 14 => 1.0, 15 => 3.5421134937189365, 16 => 1.0, 17 => 0.010999999999999999, 18 => 3.2999566681750605, 19 => 5, 20 => 1.2282984802843129, 21 => 1.0, 22 => 2.9748253120971184, 23 => 0.44855992975075798, 24 => 0.99999999999999989, 25 => 3.8350475954623371, 26 => 1.0625975061426283, 27 => 1.0000072792091179, 28 => 0.99999987785487132, 29 => 1, 30 => 0.0, 31 => 1.0, 32 => 1.0, 33 => 1.0, 34 => 0.0, 35 => 1.0972568578553616, 36 => 1.0, 37 => 1.4077661823957415, 38 => 1.0, 39 => 0.0, 40 => 3.6038030347555705, 41 => 1.0, 42 => 1.0, 43 => 1.0636876768842174, 44 => 1.0, 45 => NAN, 46 => 1.0, 47 => NAN, 48 => NAN, 49 => NAN, 50 => NAN, 51 => 1.0, 52 => 1.0, 53 => NAN, 54 => 0.99958680716631509, 55 => 1.0, 56 => NAN, 57 => 1.0, )

I got some incorrect result from array_multisort. It has 8 different keys and all the keys but this key(say A) works fine.

To figure out why that happened, I played with A in the array. And I could see that not only in the function of array_multisort, but also in 'sort' function, an incorrect result prompted as well.

As you can see the result of rsort seems okay, the result of sort works strangely when it comes to 13.16 < 14.47 < 4.62 < 13.29 < 15.68

Does anyone know why this occurred?

// Data in readible format

array (
0 => 3.142678516658294, 
1 => 1.0, 
2 => 1.0, 
3 => 14.478273306963985, 
4 => 13.165002546635966, 
5 => 1.0, 
6 => 1.0005037081114851, 
7 => 1.0, 
8 => 4.6264742674547001, 
9 => 15.686809055275578, 
10 => 1.0, 
11 => 11.903327742295504, 
12 => 13.29050881934397, 
13 => 1.0, 
14 => 1.0, 
15 => 3.5421134937189365, 
16 => 1.0, 
17 => 0.010999999999999999, 
18 => 3.2999566681750605, 
19 => 5, 
20 => 1.2282984802843129, 
21 => 1.0, 
22 => 2.9748253120971184, 
23 => 0.44855992975075798, 
24 => 0.99999999999999989, 
25 => 3.8350475954623371, 
26 => 1.0625975061426283, 
27 => 1.0000072792091179, 
28 => 0.99999987785487132, 
29 => 1, 
30 => 0.0, 
31 => 1.0, 
32 => 1.0, 
33 => 1.0, 
34 => 0.0, 
35 => 1.0972568578553616, 
36 => 1.0, 
37 => 1.4077661823957415, 
38 => 1.0, 
39 => 0.0, 
40 => 3.6038030347555705, 
41 => 1.0, 
42 => 1.0, 
43 => 1.0636876768842174, 
44 => 1.0, 
45 => NAN, 
46 => 1.0, 
47 => NAN, 
48 => NAN, 
49 => NAN, 
50 => NAN, 
51 => 1.0, 
52 => 1.0, 
53 => NAN, 
54 => 0.99958680716631509, 
55 => 1.0, 
56 => NAN, 
57 => 1.0, )

Upvotes: 2

Views: 77

Answers (1)

Pinke Helga
Pinke Helga

Reputation: 6692

It seems as you actually do have discovered a bug. On large arrays containing NaN values, represented by the NAN constant in PHP, the built-in function sort fails.

The comparision with NaN should always result as non-ordered as mentioned by kuh-chan in the question comments. However, as soon as there is at least one operand NaN, in recent (March 2019) versions of PHP the spaceship operator <=> returns 1 (first operand greater than second one) and -1 (first operand less than second one) in some other versions.

echo var_dump( NAN   <=> 1.23  );
echo var_dump( 1.23  <=> NAN   );
echo var_dump( NAN   <=> -1.23 );
echo var_dump( -1.23 <=> NAN   );
echo var_dump( NAN   <=> 0     );
echo var_dump( NAN   <=> NAN   );

I'm not very famimilar with the internals of the Zend engine. However, I guess the sort algorithm terminates too soon on large arrays with several NaN values, likely in order to the order comparision algorithm alway returns "greater (or less) than" which probably leads to multiple exchanges of the same elements.

I experienced that calling sort multiple times seems to continue the sorting. However, that would not be a proper workaround.

You can use a custom comparision algorithm with usort instead. If you want to order NaN at the start of the array, return -1 when the first operand is NaN, 1 when the second operand is NaN and 0 (equal) if both are. Otherwise return the comparision result of the spaceship operator.

usort($keys_arranged,
  function(&$v1, &$v2)
  {
    switch( (is_nan($v1) ? 1 : 0) | (is_nan($v2) ? 2 : 0) )
    {
      case 0: return $v1 <=> $v2;
      case 1: return -1;
      case 2: return 1;
      case 3: return 0;
    }
  }
);

Similar bitwise logic as above, but in a more compressive and direct form:

usort( $keys_arranged,
       function(&$v1, &$v2){ return -2 === ($b = (is_nan($v1) ? -1 : -2) ^ (is_nan($v2) ? -1 : 0)) ? $v1 <=> $v2 : $b; }
     );

Upvotes: 2

Related Questions