Sorting an array of floats with possibly NaN values

Hi,
I get an error in this case:

Comparison of ... failed

What is the idiomatic way to deal with this ?

Replacing values by a valid number is an option, but not easy to estimate without browsing the whole array for the lowest (or highest) values.

NaNs are not comparable to numbers, so what I would do is:

  • Decide (based on my own opinion) where NaNs should go in the sorted list (say, the beginning?)
  • Sort a list without the NaNs
  • Add NaNs where I want them in the sorted list until it’s the same length as the original one

Or sort using a block where NaNs are always considered smaller/larger

If you prefer to replace the NaNs with a number that’s always smallest, you can always use Float64("-inf")

One way is using sort_by (or unstable_sort_by) combined with Float64::MIN or Float64::MAX:

ary = [5.0, 4.0, 3.0, Float64::NAN, 2.0, Float64::NAN, 1.0]
ary.sort_by { |i| i.nan? ? Float64::MIN : i }
# => [NaN, NaN, 1.0, 2.0, 3.0, 4.0, 5.0]

Alternatively, you can do similar with a block passed to sort

There is also the IEEE 754 totalOrder if all the numbers have the same type:

struct Float64
  def self.total_order(x : self, y : self) : Int32
    xi = x.unsafe_as(Int64)
    yi = y.unsafe_as(Int64)
    xi ^= Int64::MAX if xi < 0
    yi ^= Int64::MAX if yi < 0
    xi <=> yi
  end
end

arr = [1.0 / 0, -0.0, 0.0 / 0, -1.0 / 0, 0.0]
arr.sort { |x, y| Float64.total_order(x, y) } # => [NaN, -Infinity, -0.0, 0.0, Infinity]
arr.sort_by {|x|
  xi = x.unsafe_as(Int64)
  xi < 0 ? xi ^ Int64::MAX : xi
} # => [NaN, -Infinity, -0.0, 0.0, Infinity]

Thank you all for your suggestions.
I opted for the block solution, which work perfectly.

To be complete, here’s how I did it:
Ascending sort on irr field:

sort { |a, b|
  if a.irr.nan?
    +1
  elsif b.irr.nan?
    -1
  else
    a.irr <=> b.irr
  end
}

Descending sort on irr field:

sort { |a, b|
  if a.irr.nan?
    +1
  elsif b.irr.nan?
    -1
  else
    b.irr <=> a.irr
  end
}

In both cases, records with an irr value of nan are relegated to the end of the list.