Sometimes, Activerecord data types confuse me. Err, often. One of my eternal questions is, for a given case,
Should I use :decimal or :float?
I've often come across this link, ActiveRecord: :decimal vs :float?, but the answers aren't quite clear enough for me to be certain:
I've seen many threads where people recommend flat out to never use float and always use decimal. I've also seen suggestions by some people to use float for scientific applications only.
Here are some example cases:
Geolocation/latitude/longitude: -45.756688, 120.5777777, ...
Ratio/percentage: 0.9, 1.25, 1.333, 1.4143, ...
I have used :decimal
in the past, but I found dealing with BigDecimal
objects in Ruby was unnecessarily awkward as compared to a float. I also know I can use :integer
to represent money/cents, for example, but it doesn't quite fit for other cases, for example when quantities in which precision could change over time.
What are the advantages/disadvantages of using each?
What would be some good rules of thumb to know which type to use?
I remember my CompSci professor saying never to use floats for currency.
The reason for that is how the IEEE specification defines floats in binary format. Basically, it stores sign, fraction and exponent to represent a Float. It's like a scientific notation for binary (something like +1.43*10^2
). Because of that, it is impossible to store fractions and decimals in Float exactly.
That's why there is a Decimal format. If you do this:
irb:001:0> "%.47f" % (1.0/10)
=> "0.10000000000000000555111512312578270211815834045" # not "0.1"!
whereas if you just do
irb:002:0> (1.0/10).to_s
=> "0.1" # the interprer rounds the number for you
So if you are dealing with small fractions, like compounding interests, or maybe even geolocation, I would highly recommend Decimal format, since in decimal format 1.0/10
is exactly 0.1.
However, it should be noted that despite being less accurate, floats are processed faster. Here's a benchmark:
require "benchmark"
require "bigdecimal"
d = BigDecimal.new(3)
f = Float(3)
time_decimal = Benchmark.measure{ (1..10000000).each { |i| d * d } }
time_float = Benchmark.measure{ (1..10000000).each { |i| f * f } }
puts time_decimal
#=> 6.770960 seconds
puts time_float
#=> 0.988070 seconds
Answer
Use float when you don't care about precision too much. For example, some scientific simulations and calculations only need up to 3 or 4 significant digits. This is useful in trading off accuracy for speed. Since they don't need precision as much as speed, they would use float.
Use decimal if you are dealing with numbers that need to be precise and sum up to correct number (like compounding interests and money-related things). Remember: if you need precision, then you should always use decimal.
In Rails 3.2.18, :decimal turns into :integer when using SQLServer, but it works fine in SQLite. Switching to :float solved this issue for us.
The lesson learned is "always use homogeneous development and deployment databases!"
In Rails 4.1.0, I have faced problem with saving latitude and longitude to MySql database. It can't save large fraction number with float data type. And I change the data type to decimal and working for me.
def change change_column :cities, :latitude, :decimal, :precision => 15, :scale => 13 change_column :cities, :longitude, :decimal, :precision => 15, :scale => 13 end
decimal(13,9)
is sufficient for latitude and longitude. @ScottW: I don't recall, but if Postgres uses IEEE floats, it only "works fine" because you haven't run into problems... YET. It is an insufficient format for latitude and longitude. Yo will eventually have errors in the least significant digits.
Success story sharing
+1.43*2^10
rather than+1.43*10^2
?