如何使用Ruby的按位运算符计算一个补码?

时间:2009-07-29 17:01:04

标签: ruby operators bit-manipulation

我想要的是什么:

assert_equal 6, ones_complement(9)   # 1001 => 0110
assert_equal 0, ones_complement(15)  # 1111 => 0000
assert_equal 2, ones_complement(1)   # 01 => 10

输入的大小不固定为4位或8位。而是它的二进制流。

我所看到的:

v = "1001".to_i(2)                 => 9

有一点翻转运算符~

(~v).to_s(2)                       => "-1010"
sprintf("%b", ~v)                  => "..10110"
~v                                 => -10

我认为它与一个用于存储标志的东西有关...有人可以解释这个输出吗?如何在不采用字符串操作的情况下获得一个补码,例如从sprintf输出中删除最后的n个字符以获得“0110”或将0替换为1,反之亦然

6 个答案:

答案 0 :(得分:6)

Ruby只存储(签名)号码。此数字的内部表示不相关:它可能是FixNum,BigNum或其他内容。因此,数字中的位数也是未定义的:毕竟它只是一个数字。这与例如C相反,其中int可能是32位(固定)。

那么〜运算符会做什么呢? Wel,就像这样:

class Numeric
    def ~
        return -self - 1
    end
end

...因为在查看2的补数时,这就是'〜'所代表的。

因此输入语句中缺少的是要切换的位数:32位〜与泛型不同〜就像在Ruby中一样。

现在,如果您只想对n位进行位翻转,可以执行以下操作:

class Numeric
    def ones_complement(bits)
        self ^ ((1 << bits) - 1)
    end
end

...但你必须指定要翻转的位数。这不会影响符号标志,因为那个符号标志在XOR之外:)

答案 1 :(得分:4)

听起来你只想翻转四位(输入的长度) - 所以你可能想要用1111进行异或。

答案 2 :(得分:3)

请参阅this question了解原因。

您的方法存在的一个问题是,只有当您只翻转四个有效位时,您的预期答案才会成立:1001 -> 0110

但是数字以前导零存储,而〜运算符也会翻转所有前导位:00001001 -> 11110110。然后前导1被解释为负号。

在确定如何实现它之前,您确实需要指定函数应该对0b1010b11011等数字执行的操作。如果你只想翻转4位,你可以v^0b1111,如另一个答案所示。但是如果你想要翻转所有有效位,它会变得更加复杂。

修改

这是翻转所有有效位的一种方法:

def maskbits n
  b=1
  prev=n;
  mask=prev|(prev>>1)
  while (mask!=prev)
    prev=mask;
    mask|=(mask>>(b*=2))
  end
  mask
end

def ones_complement n
  n^maskbits(n)
end

这给出了

p ones_complement(9).to_s(2)  #>>"110" 
p ones_complement(15).to_s(2) #>>"0"
p ones_complement(1).to_s(2)  #>>"0"

这不会为ones_compliment(1)提供所需的输出,因为它将1视为“1”而不是“01”。我不知道函数如何在不将宽度作为参数的情况下推断出你想要多少个前导零。

答案 3 :(得分:0)

你正在做什么(使用~)运算符,确实是一个补充。由于Ruby对数字的解释方式,你得到了那些你不期望的值。

您实际需要做的事情取决于您使用此功能。也就是说,为什么你需要1的补码?

答案 4 :(得分:0)

如果你正在使用字符串,你可以这样做:

s = "0110"
s.gsub("\d") {|bit| bit=="1"?"0":"1"}

如果您正在使用数字,则必须定义有效位的数量,因为:
0110 = 6; 1001 = 9;
110 = 6; 001 = 1;

即使忽略了这个标志,你也可能不得不处理这个问题。

答案 5 :(得分:0)

请记住,如果你传入一个Fixnum,你现在正在得到一个补码:表示数字的位数是解释器中的固定数量,因此在二进制表示法前面有前导0数字9(二进制1001)。您可以通过检查任何Fixnum的大小来找到此位数。 (答案以字节为单位返回)

1.size            #=> 4
2147483647.size   #=> 4

〜也是在Bignum上定义的。在这种情况下,它的行为就好像所有在Bignum中指定的位都被反转,然后如果在该Bignum前面有一个无限的1的字符串。你可以想象将你的比特流推入Bignum并颠倒整个事物。但是,您需要在反转之前知道比特流的大小,以便在反转之后获得有用的结果。

为了回答这个问题,你可以在蝙蝠身上摆出姿势,你可以找到比你的输入少2的最大功率,加倍,减去1,然后用你的输入对结果进行异或,并总是得到一个补码只是输入数字中的重要位。

def sig_ones_complement(num)
  significant_bits = num.to_s(2).length
  next_smallest_pow_2 = 2**(significant_bits-1)
  xor_mask = (2*next_smallest_pow_2)-1
  return num ^ xor_mask
end