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:
- Use
validates_numericality_of
. That way you leave the conversion completely to Rails, and just check whether the amount is within a given range. - Use
validate_each
method and code your validation logic yourself (e.g. check whether the value has more than 2 decimal digits). - 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 withKernel.Float
(ifonly_integer
is false) or applying it to the regular expression/\A[\+\-]?\d+\z/
(ifonly_integer
is set totrue
).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 String
s 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
Upsert Multiple Records with Mongodb
Accessing the Child Instance in a Rabl Template
What's the Difference Between the Ruby Irb Prompt Modes
Overriding Model in Gem, Adding Callback and Methods
How to Build Ruby 2.1.3 on Osx 10.10 Gm 3.0 with Rbenv
State MAChine, Model Validations and Rspec
Grit's Clone Method Is Undefined
Typeerror: Object Doesn't Support This Property or Method
Why Is the Splat Used Inside an Array Definition Here
Problem Running Thinking Sphinx with Rails 2.3.5
How to Access Current_User Object in Model
How to Execute 2 or More Commands in the Same Ssh Session
Include Erb Delimiters Inside of a String in an Erb Block
Can't All or Most Cases of 'Each' Be Replaced with 'Map'
Vagrant Ssh Not Working in Mobaxterm on Windows
Should I Be Using Rails or Ruby for This Website Application? How