6

I have this piece of script :

#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;

my @arr = (
    {
        name  => 'foo',
        value => 123,
    },
    {
        name  => 'bar',
        value => 'nan',
    },
    {
        name  => 'foobar',
        value => 456,
    },
);

@arr = sort {$a->{value} <=> $b->{value} } @arr;

print Dumper(\@arr);

I don't have any problems under Windows XP / Strawberry Perl 5.10.1

either Linux 2.6.12-1 i386 / Perl v5.8.5 built for i386-linux-thread-multi,

but under Linux 2.6.18-53 / Perl v5.8.8 built for x86_64-linux-thread-multi, i got the error message :

Sort subroutine didn't return a numeric value at testsort.pl line 21.

What's going wrong and how can i fix it ?

Toto
  • 89,455
  • 62
  • 89
  • 125
  • Does the same thing happen if you simplify it like this? @arr = sort { $a <=> $b } (123, 'nan', 456); – wdebeaum Jul 09 '10 at 14:10
  • @wdebeaum: no errors in one liner but doesn't sort anything under x86_64 whilst it's ok under win XP and i386 – Toto Jul 09 '10 at 14:22

2 Answers2

6

In some builds, 'nan' is coerced to the number 0 for a <=> comparison and the sort succeeds. In other builds, nan is treated as "not a number" and the return value from <=> is undefined.

For maximum portability, test a value for whether it is a good number of not:

(isnan subroutine from How do I create or test for NaN or infinity in Perl?):

sub isnan { ! defined( $_[0] <=> 9**9**9 ) }

@arr = sort { isnan($a->{value}) ? 0 : $a->{value}
                         <=>  
              isnan($b->{value}) ? 0 : $b->{value} }  @arr;
Community
  • 1
  • 1
mob
  • 117,087
  • 18
  • 149
  • 283
4

2 Solutions

  1. mobrule's solution:

    sub isnan { ! defined( $_[0] <=> 9**9**9 ) }
    @arr = sort { isnan($a->{value}) ? 0 : $a->{value}
                                  <=>  
                  isnan($b->{value}) ? 0 : $b->{value} }  @arr;
    
  2. Perldoc's solution:

    @result = sort { $a <=> $b } grep { $_ == $_ } @input;
    

  1. Gives your NaN a 0 value, which should push it to the top of the list.
  2. Takes advantage that NaN != NaN, to eliminate any NaNs from the input list.

As mobrule stated, this is caused by the comparison of NaN between builds.

Community
  • 1
  • 1
vol7ron
  • 40,809
  • 21
  • 119
  • 172