Wednesday, September 1, 2010

It's faster to numerically round than to sprintf in ruby

require 'benchmark'

class Float
  def round_to(x)
    (self * 10**x).round.to_f / 10**x
  end

  def ceil_to(x)
    (self * 10**x).ceil.to_f / 10**x
  end

  def floor_to(x)
    (self * 10**x).floor.to_f / 10**x
  end

  def round_to_sprintf_f(x)
    ("%.#{x}f" % x).to_f
  end

  def round_to_sprintf_sprintf(x)
    sprintf("%.#{x}f", x)
  end

  def round_to_sprintf_modulus(x)
    "%.#{x}f" % x
  end

end

num = 34.2342134
place = 3

TIMES = 1000000
Benchmark.bmbm do |r|
  r.report("numeric round_to (float)") { TIMES.times { num.round_to(place) } }
  r.report("numeric round_to (to string)") { TIMES.times { num.round_to(place).to_s } }
  r.report("sprintf (float)") { TIMES.times { num.round_to_sprintf_f(place) } }
  r.report("sprintf (string)") { TIMES.times { num.round_to_sprintf_sprintf(place) } }
  r.report("sprintf modulus (string)") { TIMES.times { num.round_to_sprintf_modulus(place) } }
end
The output:

% ruby -v
ruby 1.9.1p378 (2010-01-10 revision 26273) [x86_64-linux]
...
                                   user     system      total        real
numeric round_to (float)       0.820000   0.000000   0.820000 (  0.819920)
numeric round_to (to string)   1.990000   0.000000   1.990000 (  1.989912)
sprintf (float)                3.620000   0.000000   3.620000 (  3.614758)
sprintf (string)               2.650000   0.000000   2.650000 (  2.654812)
sprintf modulus (string)       3.190000   0.000000   3.190000 (  3.191807)

The conclusion: Regardless of whether you are going to a string or numeric after rounding, it is faster to round numerically than with sprintf. Also, using the sprintf function is faster than '%'. Usually speed doesn't matter, but when it does...

No comments: