Skip to content

Instantly share code, notes, and snippets.

@raggi
Created July 16, 2009 17:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save raggi/148543 to your computer and use it in GitHub Desktop.
Save raggi/148543 to your computer and use it in GitHub Desktop.
bigdecimal char_round
require "benchmark"
require 'bigdecimal'
class BigDecimal
# raggi after hint from apeiros (round 1)
def raggi_round(n = 15)
s = to_s('f')[0,n+1]
i = s.index('.')
dp = n - i
dp = 0 if dp < 0
s = round(dp).to_s('f')[0,n+1]
s = s[n,1].to_i >= 5 ? s[0,n].succ : s[0,n]
if s[-1,1] == '.'
s[0,n-1]
else
s
end
end
# apeiros round 5
def apeiros_round(n)
int, frac = *to_s("f").split(".")
case int.length
when 0..(n - 2)
round(n-int.length-1).to_s("f")
when (n - 1)..n
round(0).to_s("f").split(".").first
else
raise SomeError
end
end
end
def call(meth)
BigDecimal("1234567890.1234567890").send(meth, 10)
BigDecimal("1234567890.1234567890").send(meth, 11)
BigDecimal("1234567890.1234567890").send(meth, 12)
BigDecimal("1234567890.1234567890").send(meth, 13)
BigDecimal("1234567890.9876543210").send(meth, 10)
BigDecimal("1234567890.9876543210").send(meth, 11)
BigDecimal("1234567890.9876543210").send(meth, 12)
BigDecimal("1234567890.9876543210").send(meth, 13)
end
TESTS = 10_000
Benchmark.bmbm do |results|
results.report("raggi:" ) { TESTS.times { call(:raggi_round) } }
results.report("apeiros:") { TESTS.times { call(:apeiros_round) } }
end
# Rehearsal --------------------------------------------
# raggi:     0.780000   0.020000   0.800000 (  0.850318)
# apeiros:   1.240000   0.030000   1.270000 (  1.340230)
# ----------------------------------- total: 2.070000sec
#
#                user     system      total        real
# raggi:     0.770000   0.020000   0.790000 (  0.842681)
# apeiros:   1.240000   0.030000   1.270000 (  1.336721)
require "test/unit"
require "bigdecimal"
# Maybe I'm being dumb, but I can't find a clean way to do this without
# writing a state machine that walks backward over the string. Fork and fixme!
class BigDecimal
# raggi after hint from apeiros (round 1)
def char_round(n = 15)
s = to_s('f')[0,n+1]
i = s.index('.')
dp = n - i
dp = 0 if dp < 0
s = round(dp).to_s('f')[0,n+1]
s = s[n,1].to_i >= 5 ? s[0,n].succ : s[0,n]
if s[-1,1] == '.'
s[0,n-1]
else
s
end
end
# # apeiros round 1
# def char_round(n = 15)
# bd = self
# int, frac = bd.to_s('f').split(".");
# raise if int.length > n;
# return int if int.length >= n-1;
# bd.round(n-1-int.length).to_s("f")
# end
#
# # apeiros round 2
# def char_round(n)
# int, frac = *to_s("f").split(".")
# raise if int.length > n
# int.length == (n-1) ? int : round(n-int.length-1).to_s("f")
# end
# # apeiros round 3
# def char_round(n)
# int, frac = *to_s("f").split(".")
# raise if int.length > n
# return int if int.length == n-1
# int.length == (n-1) ? int : round(n-int.length-1).to_s("f")
# end
# # apeiros round 4
# def char_round(n)
# int, frac = *to_s("f").split(".")
# case int.length
# when 0..(n-2)
# int.length == (n-1) ? int : round(n-int.length-1).to_s("f")
# when n-1
# int
# else
# return int
# end
# end
# # ReinH round 1
# def char_round(n)
# int, frac = *to_s("f").split(".")
# case int.length
# when 0..(n - 2)
# round(n-int.length-1).to_s("f")
# when (n - 1)..n
# int
# else
# raise SomeError
# end
# end
# apeiros round 5
def char_round(n)
int, frac = *to_s("f").split(".")
case int.length
when 0..(n - 2)
round(n-int.length-1).to_s("f")
when (n - 1)..n
round(0).to_s("f").split(".").first
else
raise SomeError
end
end
end
class TestCharRound < Test::Unit::TestCase
def test_char_round_down_int_result
assert_equal "1234567890", BigDecimal("1234567890.1234567890").char_round(10)
end
def test_char_round_down_decimal_skip
assert_equal "1234567890", BigDecimal("1234567890.1234567890").char_round(11)
end
def test_char_round_down_decimal_round
assert_equal "1234567890.1", BigDecimal("1234567890.1234567890").char_round(12)
end
def test_char_round_down_decimal_round_long
assert_equal "1234567890.12", BigDecimal("1234567890.1234567890").char_round(13)
end
def test_char_round_up_int_result
assert_equal "1234567891", BigDecimal("1234567890.9876543210").char_round(10)
end
def test_char_round_up_decimal_skip
assert_equal "1234567891", BigDecimal("1234567890.9876543210").char_round(11)
end
def test_char_round_up_decimal_round
assert_equal "1234567891.0", BigDecimal("1234567890.9876543210").char_round(12)
end
def test_char_round_up_decimal_round_long
assert_equal "1234567890.99", BigDecimal("1234567890.9876543210").char_round(13)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment