2009-07-29 72 views
3

我想什么:如何使用Ruby的按位运算符来计算补码?

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”或用1代替0,反之亦然

回答

4

听起来好像您只想翻转四位(您输入的长度) - 因此您可能希望与1111异或。

+0

P.S.我不使用Ruby,但是在Python中,9^15返回6(即1001 xor 1111是0110)。 – Cascabel 2009-07-29 17:14:45

+0

Ruby中的9^15 => 6也是如此。谢谢..我的脑细胞似乎已经称它为一天。需要睡觉..我想。 – Gishu 2009-07-29 18:07:20

+0

1^0b1111 == 1,而不是2.您如何决定使用多少个前导零?实际上, – AShelly 2009-07-29 19:01:53

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”。我不知道该函数如何能够推断出你想要的多少前导零,而不用将宽度作为参数。

+0

我的确读过这个问题,但我仍然需要这个功能。用更多规格更新了问题。 – Gishu 2009-07-29 17:30:44

+0

找到重要位的很好的算法。 – jacobsimeon 2013-01-10 00:17:09

0

你在做什么(使用~)算子,确实是一个补充。由于Ruby解释数字的方式,您正在获得您不期待的那些值。

你实际需要做什么取决于你使用的是什么。那就是说,为什么你需要1的补码?

0

如果你对字符串进行操作,你可以这样做:

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

如果你与数字打交道,你必须定义显著位的数量,因为:
0110 = 6; 1001 = 9;
110 = 6; 001 = 1;

即使忽略符号,您可能也得处理这个问题。

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范围之内:)

0

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

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

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

为了回答这个问题,你可以找到最大的2的权力,比你的输入少2倍,减1,然后XOR结果与你的输入,总是得到一个只是输入数字中有效位的补充。

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