Bignum from 2s Complement

For a Ruby program I’m writing, I’m receiving binary data over a
network. For most of the data I need to read, String#unpack works like
a charm. However, some of the data I receive is 64 bit 2s complement
integers, and since String#unpack does not have a mode for 64 bit
numbers I’m having trouble figuring out how to get a Bignum from the
binary data.

As I’ve mentioned, the data is in 2s complement representation[1] such
that 1 is encoded as “\001\000\000\000\000\000\000\000” and -1 is
encoded as “\377\377\377\377\377\377\377\377” (those are both little
endian, by the way).

My question is, what’s the best way to make a Ruby integer out of this
binary string? I’ve already tried constructing a hexadecimal
representation of the bytes and passing that to the Kernel::Integer
method before realizing that that method is doing direct conversion
rather than presuming 2s complement representation. I also tried
massaging the result of this process by doing a bit-wise complement and
adding one (trying to perform the 2s complement myself), but that
doesn’t work either.

Any ideas? Please note that I need a solution that works for big endian
and little endian, since the data can arrive in either order.

Thanks,
Jason Voegele

[1] The data is encoded like this:
lsb = __cdr_lsb(val) # least significant 4 bytes
msb = __cdr_msb(val) # most significant 4 bytes
lval = Kernel::big_endian? ? [msb, lsb] : [lsb, msb]
lval.pack(‘ll’)

I’ve added the big_endian? method to Kernel to determine whether the
underlying platform is big endian or not.

For a Ruby program I’m writing, I’m receiving binary data over a
network. For most of the data I need to read, String#unpack works like
a charm. However, some of the data I receive is 64 bit 2s complement
integers, and since String#unpack does not have a mode for 64 bit
numbers I’m having trouble figuring out how to get a Bignum from the
binary data.

As I’ve mentioned, the data is in 2s complement representation[1] such
that 1 is encoded as “\001\000\000\000\000\000\000\000” and -1 is
encoded as “\377\377\377\377\377\377\377\377” (those are both little
endian, by the way).

How about unpacking the string as: nn = str.unpack(“lL”),
(or whichever endian-format is appropriate for your situation),
then combining the halves as: n = (nn[0] << 32) | nn[1]

HTH,

Bill

···

From: “Jason Voegele” jason@jvoegele.com

Bill Kelly wrote:

From: “Jason Voegele” jason@jvoegele.com

As I’ve mentioned, the data is in 2s complement representation[1] such
that 1 is encoded as “\001\000\000\000\000\000\000\000” and -1 is
encoded as “\377\377\377\377\377\377\377\377” (those are both little
endian, by the way)

How about unpacking the string as: nn = str.unpack(“lL”),
(or whichever endian-format is appropriate for your situation),
then combining the halves as: n = (nn[0] << 32) | nn[1]

Beautiful! Worked like a charm. Thanks, Bill!

Jason Voegele