-
Notifications
You must be signed in to change notification settings - Fork 113
Prefer BigDecimal over Float #372
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
IEEE754 floats often have rounding errors when doing math with them. We want to use BigDecimal as a way to correct this.
|
@jgaskins I'm looking into adding this as a configuration option as making this a default behavior may have negative performance impacts. FYI, it is currently possible to do this... irb(main):012> Unit.new(BigDecimal(3.5), 'g').convert_to('mg').scalar
=> 0.35e4 |
|
Click for benchmark code# frozen_string_literal: true
$LOAD_PATH << "lib"
require "ruby-units"
require "bigdecimal"
require "bigdecimal/util"
require "benchmark/ips" # Also had to install this gem
a = [
[2.025, "gal"],
[5.575, "gal"],
[8.975, "gal"],
[1.5, "gal"],
[9, "gal"],
[1.85, "gal"],
[2.25, "gal"],
[1.05, "gal"],
[4.725, "gal"],
[3.55, "gal"],
[4.725, "gal"],
[3.75, "gal"],
[6.275, "gal"],
[0.525, "gal"],
[3.475, "gal"],
[0.85, "gal"]
]
puts "Instantiation"
Benchmark.ips do |x|
ns, nu = a.first
x.report("Float") { Unit.new(ns, nu) }
x.report("BigDecimal") { Unit.new(ns.to_d, nu) }
x.compare!
end
puts
puts "Arithmetic"
Benchmark.ips do |x|
ns1, nu1 = a[1]
ns2, nu2 = a[2]
float1 = Unit.new(ns1, nu1)
float2 = Unit.new(ns2, nu2)
bigdecimal1 = Unit.new(ns1.to_d, nu1)
bigdecimal2 = Unit.new(ns2.to_d, nu2)
x.report("Float") { float1 + float2 }
x.report("BigDecimal") { bigdecimal1 + bigdecimal2 }
x.compare!
end
puts
puts "Conversion"
Benchmark.ips do |x|
ns, nu = a.first
float = Unit.new(ns, nu)
bigdecimal = Unit.new(ns.to_d, nu)
x.report("Float") { float.convert_to("l") }
x.report("BigDecimal") { bigdecimal.convert_to("l") }
x.compare!
end |
|
The other reason I think this PR makes sense, which I forgot to mention above, is that directly multiplying This gem is very powerful and abstracts away a lot of math for us but, in doing so, it appears to be introducing opportunities for floating-point error that may not otherwise occur. |
IEEE754 floats often have rounding errors when doing math with them. Using BigDecimal avoids these rounding errors.
To reproduce:
With this PR, we get precisely 3500: