Array#packとString#unpackを使うときの注意点

数値がフォーマットの精度いっぱいまで桁数を使っているとunpackのときにマイナスの値だと解釈される可能性がある。(逆も。マイナスの値がプラスと解釈される場合もありえる)

[0xffffffff,0xffffffff].pack('L*').unpack('L*') #unsigned - unsigned
#=> [4294967295, 4294967295]
[0xffffffff,0xffffffff].pack('l*').unpack('l*') #signed - signed
#=> [-1, -1]
[0xffffffff,0xffffffff].pack('l*').unpack('L*') #signed - unsigned
#=> [4294967295, 4294967295]
[0xffffffff,0xffffffff].pack('L*').unpack('l*') #unsigned - signed
#=> [-1, -1]

#別の数値の例
[0xfffffffe,0xfffffffe].pack('L*').unpack('L*') #unsigined - unsigned
#=> [4294967294, 4294967294]
[0xfffffffe,0xfffffffe].pack('l*').unpack('l*') #signed - signed
#=> [-2, -2]

フォーマットの精度を越える値をpack/unpackすると数値が切り(分け|捨て)られる

[4294967295].pack('s*').unpack('S*') #packで切り捨てられる
#=> [65535]
[4294967295].pack('l*').unpack('S*') #unpackで二つの値に切り分けられる
#=> [65535, 65535]
[4294967295].pack('s*').unpack('l*') #packされた文字列をlongとして解釈できない?
#=> []

数値がフォーマットに対して大きすぎると例外が起こる

[0xfffffffff,0xffffffff].pack('s*')
#RangeError: bignum too big to convert into `unsigned long'
#        from (irb):123:in `pack'
#        from (irb):123
#        from ^C:0
[0xfffffffff,0xffffffff].pack('l*')
#RangeError: bignum too big to convert into `unsigned long'
#        from (irb):124:in `pack'
#        from (irb):124
#        from ^C:0
[0xfffffffff,0xffffffff].pack('q*') #大きな数値を扱うフォーマットなら大丈夫
#=> "\377\377\377\377\017\000\000\000\377\377\377\377\000\000\000\000"


普段Cとかでプログラミングしたり、バイナリファイルをじかに扱っている人から見たら当たり前と言えば当たり前と思うんですが、どうなるのかなあと思って。
プログラミング言語 Ruby リファレンスマニュアル
プログラミング言語 Ruby リファレンスマニュアル
プログラミング言語 Ruby リファレンスマニュアル