Ruby を使用して、たとえば 16 進数'C4D81633'
を符号付き 2 の補数に変換するにはどうすればよいですか? '-992471501'
私は試した
['C4D81633'.scan(/[0-9a-f]{2}/i).reverse.join].pack('H*').unpack('l')
しかし、8文字未満の長さの入力を与えると、上記のコードはうまく機能しません。
Ruby を使用して、たとえば 16 進数'C4D81633'
を符号付き 2 の補数に変換するにはどうすればよいですか? '-992471501'
私は試した
['C4D81633'.scan(/[0-9a-f]{2}/i).reverse.join].pack('H*').unpack('l')
しかし、8文字未満の長さの入力を与えると、上記のコードはうまく機能しません。
否定演算子 ( -
) は既に 2 の補数を行っています。
p (-0x1234).to_s(16) # => "-1234"
問題は、Ruby の to_s にあります。これは、それが否定されていることに気づき、ほとんどの場合必要な方法 (マイナス記号を使用) で出力します。
ただし、否定された値をマスクすると、Ruby に希望どおりの動作をさせることができます。
p (-0x1234 & 0xffff).to_s(16) # => "edcc"
それでは、すべてをまとめてみましょう。
def negate(n, num_bits)
mask = (1 << num_bits) - 1
-n & mask
end
def negate_string(s, num_bits)
negate(s.hex, num_bits).to_s(16)
end
p negate_string("C4D81633", 32) # "3b27e9cd"
可能であれば、実際の数値で作業することをお勧めします。したがって、「文字列化された16進数っぽい数値」を取得して、実際の整数に変換します。(だから、他の人が示唆したように; 'C4D81633'.hex
)。メソッドを数回繰り返しました。最初のものは @shivashankar のもので、他のものは単なる速度の改善です。ブラックボックス化してパフォーマンスを得たい場合は、最後のものを使用してください。
def signed_twos_complement(integer_value, num_bits)
length = num_bits
mid = 2**(length-1)
max_unsigned = 2**length
(integer_value >= mid) ? integer_value - max_unsigned : integer_value
end
def signed_twos_complement2(i)
-(-i & (2**i.to_s(2).length)-1)
end
def signed_twos_complement3(i)
-(-i & (2**i.bit_length)-1)
end
def signed_twos_complement4(i, bits=i.bit_length)
-(-i & (2**bits)-1)
end
# Run chunk below in irb or pry to try benchmarking for yourself
require 'benchmark'
n = 9000000
a = Array.new(n) { rand(1...n) };
Benchmark.bm do |x|
x.report { a.each { |z| signed_twos_complement(z, z.to_s(2).length) } }
x.report { a.each { |z| signed_twos_complement(z, z.bit_length) } }
x.report { a.each { |z| signed_twos_complement2(z) } }
x.report { a.each { |z| signed_twos_complement3(z) } }
x.report { a.each { |z| signed_twos_complement4(z) } }
end
これが私のベンチマーク実行の結果です
user system total real
6.250000 0.040000 6.290000 ( 6.355923)
2.290000 0.010000 2.300000 ( 2.347434)
5.840000 0.040000 5.880000 ( 5.943748)
2.160000 0.010000 2.170000 ( 2.223785)
2.160000 0.020000 2.180000 ( 2.218012)
編集:signed_twos_complement4
もう少し拡張できるように追加しました。高度な補完のために意図したビットをオーバーライドできます。
String#rjust
まず、入力に使用してみませんか?
['C4D81633'.rjust(8,'0').scan(/[0-9a-f]{2}/i).reverse.join].pack('H*').unpack('l')
このようにして、常に 8 桁の 16 進数を保持します (入力が 8 桁を超える場合を除く)。
グーグルで答えを見つけました…次のような方法で追加します
def convert_to_signed_twos_complement(integer_value, num_of_bits)
length = num_of_bits
mid = 2**(length-1)
max_unsigned = 2**length
(integer_value >= mid) ? integer_value - max_unsigned : integer_value
end
hex_to_convert = 'C4D81633'
convert_to_signed_twos_complement(hex_to_convert.hex, 16)
Note: the num_of_bits in method denote the max bit size of the binary you want