Let’s see an example of numerical validation of very large numbers.

class Score
  validates :total, numericality: { less_than_or_equal_to: 10_000_000_000_000_000 }
end


> score = Score.new(total: (10_000_000_000_000_000 + 1).to_s)
> score.total
#=> 1.0e+16

> score.invalid?
#=> false

Here, we have added numerical validation on total column of Score model that it should be less than 10_000_000_000_000_000.

After that we have created one instance of this model with total greater than allowed value. This should result in an invalid object as it violates the numericality criteria.

But it is still valid. We can also see that value of total has been converted into floating number instead of integer. This happens because Rails used to convert the input to float if it was not already numeric.

The real problem is here is that the floating point comparison of the Ruby language itself which is not accurate for very large numbers.

>> a = (10_000_000_000_000_000 + 1).to_s
=> "10000000000000001"
>> b = Kernel.Float a
=> 1.0e+16
>> c = b + 1
=> 1.0e+16
>> c < b
=> false
>> c > b
=> false
>>

This issue has been fixed in Rails here. Now, if the given string input can be treated as integer, then an integer value is returned instead of float. This makes sure that the comparison works correctly.

# Rails 5.2

> score = Score.new(total: (10_000_000_000_000_000 + 1).to_s)
> score.total
#=> 10000000000000001

> score.invalid?
#=> true

This change is present in Rails 5.2 and above. It also backported to Rails 5.1 and Rails 5.0 branches.