2009-07-29 84 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