Hi,
So, I came up with a solution, and would be grateful for Ruby
style tips. It seemed a lot harder to do than it needed to be.
Any help appreciated.
Thanks,
Bob
The Tests:
class ByteReaderTest < Test::Unit::TestCase
def test_read_signed4_negative
negative = "" << 0xF2 << 0x34 << 0x56 << 0x78
assert_equal -231451016, signed4(StringIO.new(negative))
end
def test_read_signed4_positive
positive = "" << 0x02 << 0x34 << 0x56 << 0x78
assert_equal 36984440, signed4(StringIO.new(positive))
end
end
The Solution:
def signed4(file)
bits = (file.read(4).unpack('B32'))[0]
if bits[0..0].eql? "0" # sign bit is positive
bits.to_i(2)
else # sign bit is negative
compute_negative_number_from(bits)
end
end
def compute_negative_number_from(bits)
twos_complement = flip(bits).join.to_i(2) + 1
-1 * twos_complement
end
def flip(bits)
bits.scan(/\w/).collect { |bit| (bit.eql?("1")) ? "0" : "1" }
end
harp:~ > cat a.rb
require "test/unit"
class ByteReaderTest < Test::Unit::TestCase
MAX_POS = 2 ** 31 -1
BIG_ENDIAN = [42].pack('N') == [42].pack('i')
def test_read_signed4_positive
positive = [0x02, 0x34, 0x56, 0x78].pack "c*"
assert_equal 36984440, signed4(positive)
end
def test_read_signed4_negative
negative = [0xF2, 0x34, 0x56, 0x78].pack "c*"
assert_equal -231451016, signed4(negative)
end
def signed4 buf
buf = buf.read 4 if buf.respond_to? "read"
raise RangeError unless buf.size == 4
n = buf.unpack('N').first
if n > MAX_POS
(BIG_ENDIAN ? buf : buf.reverse).unpack('l').first
else
n
end
end
end
harp:~ > ruby a.rb
Loaded suite a
Started
..
Finished in 0.000583 seconds.
2 tests, 2 assertions, 0 failures, 0 errors
-a
--
======================================================================
=========
| ara [dot] t [dot] howard [at] gmail [dot] com
| all happiness comes from the desire for others to be happy. all
misery
| comes from the desire for oneself to be happy.
| -- bodhicaryavatara
======================================================================
=========