# Reading a signed byte in network byte order

Discussion in 'Ruby' started by Robert Evans, Nov 14, 2005.

1. ### Robert EvansGuest

Hi,

In trying to read 4 bytes as a signed integer from an IO in big-
endian order, is there already a utility to do this in Ruby? I notice
unpack has lots of combinations already, but seemingly not one for
this. Maybe I am just missing it?

Thanks,
Bob Evans

Robert Evans, Nov 14, 2005

2. ### Robert EvansGuest

Solution Comments on Re: Reading a signed byte in network byte order

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:

negative = "" << 0xF2 << 0x34 << 0x56 << 0x78
assert_equal -231451016, signed4(StringIO.new(negative))
end

positive = "" << 0x02 << 0x34 << 0x56 << 0x78
assert_equal 36984440, signed4(StringIO.new(positive))
end

end

The Solution:

def signed4(file)
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

On Nov 14, 2005, at 3:07 PM, Robert Evans wrote:

> Hi,
>
> In trying to read 4 bytes as a signed integer from an IO in big-
> endian order, is there already a utility to do this in Ruby? I
> notice unpack has lots of combinations already, but seemingly not
> one for this. Maybe I am just missing it?
>
> Thanks,
> Bob Evans
>

Robert Evans, Nov 15, 2005

3. ### David BalmainGuest

Re: Solution Comments on Re: Reading a signed byte in network byte order

Hi Robert,

def signed4(file)
end

Cheers,
Dave

On 11/16/05, Robert Evans <> wrote:
> 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:
>
>
> negative =3D "" << 0xF2 << 0x34 << 0x56 << 0x78
> assert_equal -231451016, signed4(StringIO.new(negative))
> end
>
> positive =3D "" << 0x02 << 0x34 << 0x56 << 0x78
> assert_equal 36984440, signed4(StringIO.new(positive))
> end
>
> end
>
> The Solution:
>
> def signed4(file)
> 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 =3D 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
>
>
> On Nov 14, 2005, at 3:07 PM, Robert Evans wrote:
>
> > Hi,
> >
> > In trying to read 4 bytes as a signed integer from an IO in big-
> > endian order, is there already a utility to do this in Ruby? I
> > notice unpack has lots of combinations already, but seemingly not
> > one for this. Maybe I am just missing it?
> >
> > Thanks,
> > Bob Evans
> >

>
>
>

David Balmain, Nov 15, 2005
4. ### Ara.T.HowardGuest

Re: Solution Comments on Re: Reading a signed byte in network byteorder

On Wed, 16 Nov 2005, Robert Evans wrote:

> 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:
>
>
> negative = "" << 0xF2 << 0x34 << 0x56 << 0x78
> assert_equal -231451016, signed4(StringIO.new(negative))
> end
>
> positive = "" << 0x02 << 0x34 << 0x56 << 0x78
> assert_equal 36984440, signed4(StringIO.new(positive))
> end
>
> end
>
> The Solution:
>
> def signed4(file)
> 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"
MAX_POS = 2 ** 31 -1
BIG_ENDIAN = [42].pack('N') == [42].pack('i')
positive = [0x02, 0x34, 0x56, 0x78].pack "c*"
assert_equal 36984440, signed4(positive)
end
negative = [0xF2, 0x34, 0x56, 0x78].pack "c*"
assert_equal -231451016, signed4(negative)
end
def signed4 buf
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
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
===============================================================================

Ara.T.Howard, Nov 15, 2005
5. ### Robert EvansGuest

Re: Solution Comments on Re: Reading a signed byte in network byte order

Hi David,

The only thing I worried about that was according to the PickAxe v.2
book, I is in native order. I thought that would be a problem since I
am running this on unix boxes and windows boxes. Is that a true concern?

Bob

On Nov 15, 2005, at 9:40 AM, David Balmain wrote:

> Hi Robert,
>
>
> def signed4(file)
> end
>
> Cheers,
> Dave
>
> On 11/16/05, Robert Evans <> wrote:
>> 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:
>>
>>
>> negative = "" << 0xF2 << 0x34 << 0x56 << 0x78
>> assert_equal -231451016, signed4(StringIO.new(negative))
>> end
>>
>> positive = "" << 0x02 << 0x34 << 0x56 << 0x78
>> assert_equal 36984440, signed4(StringIO.new(positive))
>> end
>>
>> end
>>
>> The Solution:
>>
>> def signed4(file)
>> 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
>>
>>
>> On Nov 14, 2005, at 3:07 PM, Robert Evans wrote:
>>
>>> Hi,
>>>
>>> In trying to read 4 bytes as a signed integer from an IO in big-
>>> endian order, is there already a utility to do this in Ruby? I
>>> notice unpack has lots of combinations already, but seemingly not
>>> one for this. Maybe I am just missing it?
>>>
>>> Thanks,
>>> Bob Evans
>>>

>>
>>
>>

>

Robert Evans, Nov 15, 2005
6. ### Robert EvansGuest

Re: Solution Comments on Re: Reading a signed byte in network byte order

Sweet. That's a very slick answer. Thanks.

Bob

On Nov 15, 2005, at 9:56 AM, Ara.T.Howard wrote:

> On Wed, 16 Nov 2005, Robert Evans wrote:
>
>> 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:
>>
>>
>> negative = "" << 0xF2 << 0x34 << 0x56 << 0x78
>> assert_equal -231451016, signed4(StringIO.new(negative))
>> end
>>
>> positive = "" << 0x02 << 0x34 << 0x56 << 0x78
>> assert_equal 36984440, signed4(StringIO.new(positive))
>> end
>>
>> end
>>
>> The Solution:
>>
>> def signed4(file)
>> 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"
> MAX_POS = 2 ** 31 -1
> BIG_ENDIAN = [42].pack('N') == [42].pack('i')
> positive = [0x02, 0x34, 0x56, 0x78].pack "c*"
> assert_equal 36984440, signed4(positive)
> end
> negative = [0xF2, 0x34, 0x56, 0x78].pack "c*"
> assert_equal -231451016, signed4(negative)
> end
> def signed4 buf
> 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
> 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
> ======================================================================
> =========
>
>

Robert Evans, Nov 15, 2005
7. ### Joel VanderWerfGuest

Robert Evans wrote:
> Hi,
>
> In trying to read 4 bytes as a signed integer from an IO in big- endian
> order, is there already a utility to do this in Ruby? I notice unpack
> has lots of combinations already, but seemingly not one for this. Maybe
> I am just missing it?
>
> Thanks,
> Bob Evans

You can unpack with "N" and then use the following to interpret that
positive Integer as a signed number in 32 bit two's complement
representation and convert it to a positive or negative Integer:

length = 32 # bits
max = 2**length-1
mid = 2**(length-1)
to_signed = proc {|n| (n>=mid) ? -((n ^ max) + 1) : n}

For example:

irb(main):012:0> to_signed[4294967295]
=> -1

This is a snippet from the implementation of my bit-struct lib (see
RAA). A BitStruct is basically a string with some extra accessors and
convenience methods. Currently it handles fields that are either
multiple bytes or 1-7 bits within a byte. (Eventually, longer odd-size
bit fields would be nice.) Also supports fields for: fixed length char
strings, null-terminated strings, hex octets, decimal octets, floats,
nested BitStructs, and "rest"--the rest of the string after defined
fields. It's *really* useful for playing with net protocols in pure
ruby. (Someday, I'll probably write a C extension for efficiency.)

Example:

require 'bit-struct'

class C < BitStruct
signed :foo, 32, "Something signed"
unsigned :bar, 32, "Something UNsigned"
end

c = C.new

c.foo = -12345678
c.bar = 12345678

puts "-"*40
p c

puts "-"*40
p c.to_s

puts "-"*40
puts c.inspect_detailed

puts "-"*40
p c.to_h

puts "-"*40
puts C.describe

__END__

----------------------------------------
#<C foo=-12345678, bar=12345678>
----------------------------------------
"\377C\236\262\000\274aN"
----------------------------------------
C:
Something signed = -12345678
Something UNsigned = 12345678
----------------------------------------
{:bar=>12345678, :foo=>-12345678}
----------------------------------------
byte: type name [size] description
----------------------------------------------------------------------
@0: signed foo [ 32b] Something signed
@4: unsigned bar [ 32b] Something UNsigned

--
vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

Joel VanderWerf, Nov 15, 2005
8. ### Joel VanderWerfGuest

Oh, yeah, I forgot to show that you can use a BitStruct to parse strings

Joel VanderWerf wrote:
> require 'bit-struct'
>
> class C < BitStruct
> signed :foo, 32, "Something signed"
> unsigned :bar, 32, "Something UNsigned"
> end

c = C.new(socket.recv(...))

p c.foo

and so on.

--
vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

Joel VanderWerf, Nov 15, 2005