I hope that this doesn’t offend anyone, but in the spirit of test
infection…
I actually took the various suggestions and wrote test cases. Here’s
my file “next_power_of_2.r b” which defines either methods on Integer,
or a class with a next_power_of_2() class method, following the
various suggestions, I did give these methods a consistent name
following Ruby naming standards.
lass Integer
def denatale_next_power_of_2
trial = 1
loop do
return trial if trial >= self
trial <<= 1
end
end
def klemme_next_power_of_2
x = self
c = 0
until x == 0
x >>= 1
c += 1
end
1 << c
end
end
class Wicham
def self.log2(x)
Math.log(x) / Math.log(2)
end
def self.next_power_of_2(x)
2 ** (log2(x).ceil)
end
end
class Kroeger
def self.next_power_of_2(n)
2 ** (Math.log(n) / Math.log(2)).ceil
end
end
class Lifson
def self.next_power_of_2(n)
if Math.sqrt(n).modulo(1).zero?
n
else
2**Math.sqrt(n).to_i.succ
end
end
end
===== End of next_power_of_2.rb =====
And now the test cases
load ‘next_power_of_2.rb’
require ‘test/unit’
class TestIntegerMethods < Test::Unit::TestCase
def setup
@check_array = [[1, 1], [2,2]]
(2..100).each do | i |
@check_array << [ 2**i - 1, 2**i] << [2**i,
2i] << [2i + 1, 2**(i+1)]
end
end
def test_denatale
@check_array.each do | input, output |
assert_equal(output,
input.denatale_next_power_of_2, “#{input} => #{output}”)
end
end
def test_wicham
@check_array.each do | input, output |
assert_equal(output,
Wicham.next_power_of_2(input))
end
end
def test_kroeger
@check_array.each do | input, output |
assert_equal(output,
Kroeger.next_power_of_2(input))
end
end
def test_lifson
@check_array.each do | input, output |
assert_equal(output,
Lifson.next_power_of_2(input))
end
end
end
=== end of ‘test_powers.rb’ ===
And now the results
rick@bill:~/rubyscripts$ ruby test_powers.rb
Loaded suite test_powers
Started
.FFF
Finished in 0.38841 seconds.
-
Failure:
test_kroeger(TestIntegerMethods)
[test_powers.rb:34:intest_kroeger' test_powers.rb:33:intest_kroeger’]:
<536870912> expected but was
<1073741824>. -
Failure:
test_lifson(TestIntegerMethods)
[test_powers.rb:40:intest_lifson' test_powers.rb:39:intest_lifson’]:
<2> expected but was
<4>. -
Failure:
test_wicham(TestIntegerMethods)
[test_powers.rb:28:intest_wicham' test_powers.rb:27:intest_wicham’]:
<536870912> expected but was
<1073741824>.
4 tests, 471 assertions, 3 failures, 0 errors
rick@bill:~/rubyscripts$
So unless I screwed something up in transcribing the code, which is
entirely possible, only the integer arithmetic solutions actually
worked.
This really isn’t surprising, since floating point arithmetic only
approximates real arithmetic using real numbers.
My original intention was to benchmark the various suggestions, but
since you can get the wrong answers with arbitrarily fast code, I
don’t think that it matters much.
–
Rick DeNatale
IPMS/USA Region 12 Coordinator
http://ipmsr12.denhaven2.com/
Visit the Project Mercury Wiki Site
http://www.mercuryspacecraft.com/
).