Floating Point Precision in Ruby on Rails Model Validations

floating point precision in ruby on rails model validations

I presume your dollar amount is of decimal type. So, any value user enters in the field is being cast from string to appropriate type before saving to the database. Validation applies to the values already converted to numeric types, so regex is not really a suitable validation filter in your case.

You have couple of possibilities to solve this, though:

  1. Use validates_numericality_of. That way you leave the conversion completely to Rails, and just check whether the amount is within a given range.
  2. Use validate_each method and code your validation logic yourself (e.g. check whether the value has more than 2 decimal digits).
  3. Validate the attribute before it's been typecasted:

This is especially useful in
validation situations where the user
might supply a string for an integer
field and you want to display the
original string back in an error
message. Accessing the attribute
normally would typecast the string to
0, which isn‘t what you want.

So, in your case, you should be able to use:

validates_format_of :amount_before_type_cast, :with => /^[0-9]+\.[0-9]{2}$/, :message => "must contain dollars and cents, seperated by a period"

Note, however, that users might find it tedious to follow your rigid entry rules (I would really prefer being able to type 500 instead 500.00, for example), and that in some locales period is not a decimal separator (if you ever plan to internationalize your app).

Float validation on Rails

Well your precision and scale are off. Based on your description it should be (9,3) rather than (8,2).

Then the following should work:

class MyClass < ApplicationRecord
validates :my_float, numericality: {less_than: 1_000_000.0, greater_than_or_equal_to: 0.001}
end

This will validate that the number is less than 1 Million (6 digits to the left of the decimal) and greater than or equal to 0.001 three digits to the right of the decimal. If you need to handle negative numbers this could get a bit more interesting.

You could also possibly go with something like:

class MyClass < ApplicationRecord
validates :my_float, format: { with: /-?\d{1,6}\.\d{1,3}/}
end

Validate optional negative followed by 1-6 digits followed by a period followed by 1-3 digits.

Price Validation for Rails 4

I dont know which database you are using but you can define precision in your migration like this,

add_column :pieces, :price, :decimal, precision: 8, scale: 2

It will give you a total of 8 digits, with 2 after the decimal point.

About the validation,

If you want that the :price should always have two decimal place (i.e: 4.99) you can try this,

 validates :price, presence: true, format: { with: /\A\d+(?:\.\d{2})?\z/ }, numericality: { greater_than: 0, less_than: 1000000 }

If you want that the :price should have at most two decimal or less (i.e: 4, 4.9, 4.99) you can try this,

validates :price, presence: true, format: { with: /\A\d+(?:\.\d{0,2})?\z/ }, numericality: { greater_than: 0, less_than: 1000000 }

Or if you dont want to validate precision and just want to round up the precision before you save it to the database you can use round_with_precision.

How do I validate float values so that nothing but a float can be saved before submitting a form in ruby on rails 4?

Try a custom validator rather than a regex?

validate :valid_price_format

def valid_price_format
unless price.split('.')[1].try(:length) == 2
self.errors.add(:price, I18n.t('.invalid_format') )
end
end

Edited based on comments.

You can scope your translation if it's looking in the wrong place:

I18n.t('en.my.translation.location.invalid_format')

or

I18n.t('invalid_format', scope: 'en.my.translation.location')

What's the maximum number for float on Rails validation?

As per the docs (which are not in https://api.rubyonrails.org/ pages, but in the code in master):

Validates whether the value of the specified attribute is numeric by
trying to convert it to a float with Kernel.Float (if only_integer
is false) or applying it to the regular expression /\A[\+\-]?\d+\z/
(if only_integer is set to true).

Precision of Kernel.Float values are guaranteed up to 15 digits.

The answer you're looking for is maybe here: "Precision of Kernel.Float values are guaranteed up to 15 digits" - as there's not a maximum number, then you can use whatever you want, just having in mind that greather numbers will loose precision.

Mongoid puts float values on fields when string is given

You get no validation error because the method new doesn't trigger the validation, to see it you should execute:

User.create!(height:"hi", weight:"try")
# .../mongoid-3.1.0/lib/mongoid/persistence.rb:335:in `fail_validate!': (Mongoid::Errors::Validations)
# Problem:
# Validation of User failed.
# Summary:
# The following errors were found: Height is invalid, Weight is invalid
# ...

Given that, the fields height and weight are filled with 0.0 because the Strings are converted to Float using the to_f method, that behaves like this:

'foo'.to_f
# => 0.0

Moreover, it is useless to validate a floating point field using a regular expression because the conversion to Float is performed before the validation, so the validation always fails because of the behaviour of =~:

1.2 =~ /any_regexp/
# => nil


Update To validates that, given as string, the field is a valid number you can use the numericality option:

class User
# ...
validates :height, presence: true, numericality: true
# ...
end

User.create!(height: '0.0')
# => #<User ... >

User.create!(height: 'foo')
# Problem:
# Validation of User failed.
# ...

Javascript and Rails number validation

One thing to be aware of is that you are doing floating-point comparisons. Because computers can only accurately represent rational numbers whose denominator is a power of 2, you will get small rounding errors. You should instead check whether the absolute value of the difference between expected and true value is very small.



Related Topics



Leave a reply



Submit