Rails adds accurate numerical validation

Sushant Mittal

Sushant Mittal

October 23, 2017

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

1class Score
2  validates :total, numericality: { less_than_or_equal_to: 10_000_000_000_000_000 }
3end
4
5
6> score = Score.new(total: (10_000_000_000_000_000 + 1).to_s)
7> score.total
8#=> 1.0e+16
9
10> score.invalid?
11#=> 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.

1>> a = (10_000_000_000_000_000 + 1).to_s
2=> "10000000000000001"
3>> b = Kernel.Float a
4=> 1.0e+16
5>> c = b + 1
6=> 1.0e+16
7>> c < b
8=> false
9>> c > b
10=> false
11>>

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.

1# Rails 5.2
2
3> score = Score.new(total: (10_000_000_000_000_000 + 1).to_s)
4> score.total
5#=> 10000000000000001
6
7> score.invalid?
8#=> true

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

If this blog was helpful, check out our full blog archive.

Stay up to date with our blogs.

Subscribe to receive email notifications for new blog posts.