有符号2的补码表示十六进制数

时间:2012-11-18 09:37:50

标签: ruby

如何使用Ruby将实例'C4D81633'的十六进制数转换为带符号的2的补码,即'-992471501'?我试过了

['C4D81633'.scan(/[0-9a-f]{2}/i).reverse.join].pack('H*').unpack('l')

但是当我提供长度小于8个字符的输入时,上面的代码效果不佳。

4 个答案:

答案 0 :(得分:6)

否定运算符(-)已经做了两个赞美:

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"

答案 1 :(得分:2)

我建议你尽可能使用实际数字。因此,请使用“stringified hex-ish number”,并将其转换为实际整数。 (所以,正如其他人所建议的那样; '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以使其更具可扩展性。您可以覆盖预期位以进行高级补充。

答案 2 :(得分:1)

首先在输入中使用String#rjust怎么样?

['C4D81633'.rjust(8,'0').scan(/[0-9a-f]{2}/i).reverse.join].pack('H*').unpack('l')

这样,您总是有8个十六进制数字(输入长度​​超过8位时除外)

答案 3 :(得分:0)

我在谷歌搜索后找到答案...我将其添加为如下方法

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