Skip to content

Instantly share code, notes, and snippets.

@nmk
Created December 5, 2011 14:25
Show Gist options
  • Save nmk/1433735 to your computer and use it in GitHub Desktop.
Save nmk/1433735 to your computer and use it in GitHub Desktop.
Testing percent summation using BigDecimal values
require 'bigdecimal'
require 'benchmark'
require 'rspec/autorun'
module Calculator
ONE = BigDecimal('1')
ONE_HUNDRED = BigDecimal('100')
DECIMAL_ROUNDING_SCALE = 6 # number of fractional digits to retain
# no rounding
def self.sum_percent_deltas_procedural_no_rounding(pds)
result = ONE
pds.each { |pd|
result = (result * (1 + pd / ONE_HUNDRED))
}
(result - 1) * ONE_HUNDRED
end
# rounding each intermediate result
def self.sum_percent_deltas_procedural_intermediate_rounding(pds)
result = ONE
pds.each { |pd|
# There is no need to call #round explicitly.
# BigDecimal#mult supports # inherent rounding to a specified scale.
#
# See http://ruby-doc.org/stdlib-1.8.7/libdoc/bigdecimal/rdoc/BigDecimal.html#method-i-mult
result = result.mult(ONE + pd / ONE_HUNDRED, DECIMAL_ROUNDING_SCALE)
}
(result - 1) * ONE_HUNDRED
end
end
NR_OF_ELEMENTS = 2_000
PERCENT_DELTAS = Array.new(NR_OF_ELEMENTS) { i = rand * 100.0 - 50.0; BigDecimal.new(i.to_s, 6) }
Benchmark.bm(60) do |x|
Calculator.singleton_methods.sort.each do |meth|
x.report(meth.to_s) { Calculator.send(meth, PERCENT_DELTAS) }
end
end
describe Calculator do
GOLD_STANDARD = Calculator.sum_percent_deltas_procedural_no_rounding(PERCENT_DELTAS)
context 'comparing non-rounded results' do
result = Calculator::sum_percent_deltas_procedural_intermediate_rounding(PERCENT_DELTAS)
specify { result.should_not == GOLD_STANDARD }
specify { result.should be_within(BigDecimal('0.00001')).of(GOLD_STANDARD) }
end
context 'comparing rounded results' do
result = Calculator::sum_percent_deltas_procedural_intermediate_rounding(PERCENT_DELTAS).round(Calculator::DECIMAL_ROUNDING_SCALE)
rounded_gold_standard = GOLD_STANDARD.round(Calculator::DECIMAL_ROUNDING_SCALE)
specify { result.should_not == GOLD_STANDARD }
specify { result.should be_within(BigDecimal('0.00001')).of(rounded_gold_standard) }
end
end
~ $ rvm 1.9.3,1.9.2,1.6.5@us exec ruby -rrubygems big-decimal-test.rb -- INSERT --
user system total real
sum_percent_deltas_procedural_intermediate_rounding 0.010000 0.000000 0.010000 ( 0.011952)
sum_percent_deltas_procedural_no_rounding 0.150000 0.010000 0.160000 ( 0.156943)
....
Finished in 0.49579 seconds
4 examples, 0 failures
user system total real
sum_percent_deltas_procedural_intermediate_rounding 0.010000 0.000000 0.010000 ( 0.004836)
sum_percent_deltas_procedural_no_rounding 0.180000 0.010000 0.190000 ( 0.189087)
....
Finished in 0.66567 seconds
4 examples, 0 failures
user system total real
sum_percent_deltas_procedural_intermediate_rounding 0.728000 0.000000 0.728000 ( 0.728000)
sum_percent_deltas_procedural_no_rounding 17.135000 0.000000 17.135000 ( 17.136000)
....
Finished in 38.6 seconds
4 examples, 0 failures
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment