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. |
|
@olbrich It sure does, thanks! |
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: