2014-01-26 37 views
3

正如大衛在回答here時所說的,我對這個函數的工作方式非常感興趣,因爲如果將結果長度從32更改爲16或8,我似乎無法得到相同(正確)的值。將十進制/整數轉換爲二進制 - 它如何以及爲何如此工作?

我使用的功能

function IntToBin(Value: LongWord): string; 
var 
    i: Integer; 
begin 
    SetLength(Result, 32); 
    for i := 1 to 32 do begin 
    if ((Value shl (i-1)) shr 31) = 0 then begin 
     Result[i] := '0' 
    end else begin 
     Result[i] := '1'; 
    end; 
    end; 
end; 

莫名其妙地工作就好了。 (1返回爲000 .... 001,2返回爲000 .... 010,3返回爲000 ... 011等)。

然而,因爲我只需要8個字符長的字符串結果,我改變了數在函數中8得到這個:

function IntToBin(Value: LongWord): string; 
var 
    i: Integer; 
begin 
    SetLength(Result, 8); 
    for i := 1 to 8 do begin 
    if ((Value shl (i-1)) shr 7) = 0 then begin 
     Result[i] := '0' 
    end else begin 
     Result[i] := '1'; 
    end; 
    end; 
end; 

,但我得到的結果,因爲它們遵循:

1: 00000001 
2: 00000011 
3: 00000011 
4: 00000111 
5: 00000111 
6: 00000111 
7: 00000111 
8: 00001111 
9: 00001111 
10: 00001111 
11: 00001111 
12: 00001111 

有點相同,而不是8個。

試圖將LongWord更改爲Integer和Byte,但得到了相同的結果。

所以......嗯......我在這裏錯過了什麼,不明白? :/

PS:爲了學習的目的,在第一個函數結束時使用Copy(Result,25,8)解決了我的情況,因爲需要8個字符長的字符串通過,但我真的想知道發生了什麼... :)

感謝

+1

+1出於好奇 – MartynA

回答

2

代碼左shift是爲了轉移你感興趣的數據類型的非常左手邊緣位。通過這樣做,左邊的所有位都被移出並丟失。然後當你再次右轉時,我們一路轉移到另一端。結果爲0或1.

但是,您的數據類型仍然是32位,因此您的移位不夠遠。你沒有得到目標位左邊的所有位以結束。所以當你向右移動時他們會回來。

爲了讓你的代碼工作,你需要這樣的:

function IntToBinLowByte(Value: LongWord): string; 
var 
    i: Integer; 
begin 
    SetLength(Result, 8); 
    for i := 1 to 8 do begin 
    if ((Value shl (24+i-1)) shr 31) = 0 then begin 
     Result[i] := '0' 
    end else begin 
     Result[i] := '1'; 
    end; 
    end; 
end; 

,可能是更容易理解,相對於原來的一個版本是這樣的:

function IntToBinLowByte(Value: LongWord): string; 
var 
    i: Integer; 
begin 
    SetLength(Result, 8); 
    for i := 25 to 32 do begin 
    if ((Value shl (i-1)) shr 31) = 0 then begin 
     Result[i-24] := '0' 
    end else begin 
     Result[i-24] := '1'; 
    end; 
    end; 
end; 

坦白然而,最好在一個字節上操作。我發現這種雙重轉變有點模糊。我會用一個班次和一個掩碼。就像這樣:

function IntToBinByte(Value: Byte): string; 
var 
    i: Integer; 
begin 
    SetLength(Result, 8); 
    for i := 1 to 8 do begin 
    if (Value shr (8-i)) and 1 = 0 then begin 
     Result[i] := '0' 
    end else begin 
     Result[i] := '1'; 
    end; 
    end; 
end; 

,並調用它像這樣

str := IntToBinByte(Value and $ff); 

假設Value是一個32位的數據類型。很明顯,如果它已經是Byte那麼你不需要按位and

原來的32位函數讀起來會更好,就我個人的意見而言。


這個答案的早期版本有以下不正確的嘗試解決問題:

function IntToBinByte(Value: Byte): string; 
var 
    i: Integer; 
begin 
    SetLength(Result, 8); 
    for i := 1 to 8 do begin 
    if ((Value shl (i-1)) shr 7) = 0 then begin 
     Result[i] := '0' 
    end else begin 
     Result[i] := '1'; 
    end; 
    end; 
end; 

的問題是,即使Value是一個8位的,按位操作都在32進行位寄存器。因此,當執行右移時,左移位數> 7的位會返回。你可以通過掩蓋那些意味着落後的位來輕鬆地解決這個問題。就像這樣:

function IntToBinByte(Value: Byte): string; 
var 
    i: Integer; 
begin 
    SetLength(Result, 8); 
    for i := 1 to 8 do begin 
    if (Value shl (i-1) and $ff) shr 7 = 0 then begin 
     Result[i] := '0' 
    end else begin 
     Result[i] := '1'; 
    end; 
    end; 
end; 

此代碼確實令人費解,我不建議任何人任何時候使用它。在我看來,最好的版本是我答案中的第三塊代碼。

+0

喔,ok了。現在我想我更清楚地瞭解發生了什麼。 第二個代碼完美地工作。 :) 謝謝!但是,如果我完全理解這一點,如果我將字節作爲值的類型(您在其他線程的註釋中已經建議的內容),則這將是8位,而不是32位,因此應該使用移位0左邊,右邊7,還是不一樣? –

+0

查看我的最新更新 –

+0

剛纔看到了,是的。 但是,第三個例子對我來說不起作用,即使用「和$ ff」調用它...? –

1

就個人而言,我會做這種方式:

function inttobin (p_nb_int: uint64; p_nb_digits: byte=64): string; 
begin 
    SetLength(Result, p_nb_digits); 
    while p_nb_digits > 0 do 
    begin 
    if odd(p_nb_int) then 
     Result[p_nb_digits] := '1' 
    else 
     Result[p_nb_digits] := '0'; 
    p_nb_int := p_nb_int shr 1; 
    dec(p_nb_digits); 
    end; 
end; 
+1

這對我來說看起來要複雜得多。 (專注於「對我而言」......)。此外,我需要分別傳遞長度,所以每次需要特別注意和額外的數據,如果使用不同的類型... :) 感謝您的提示和另一種選擇和意見,但! :) –

+0

答案也沒有真正解決被問到的問題。當然,在一個函數中能夠處理任何長度的數據類型會更加靈活。我當然會讓'p_nb_digits'的類型爲'Integer',這是本地整數類型。在這裏使用字節沒有任何好處。 –

+0

此方法與我的答案中的第三個代碼塊使用相同的方法。它在這裏完成的方式的缺點是它修改了一個實際的輸入參數,如果可能的話最好避免。另一個缺點是編譯器似乎不會將'p_nb_int'優化到一個寄存器中。這段代碼導致大量的mov指令執行'p_nb_int:= p_nb_int shr 1;'優化器能夠使用問題中的方法以及我的答案中列出的各種方法刪除所有這些指令。表現可能不是關鍵,但我認爲這仍然是一個有趣的觀點。 –

1

正如David如此明確答覆,您bitshifting要麼做空,或操作數是隱式由編譯器擴展。

如果表現很重要,那麼這個例程比David提供的例程要快。

function IntToBinByte(Value: Byte): String; 
var 
    i: Integer; 
    pStr: PChar; 
begin 
    SetLength(Result,8); 
    pStr := PChar(Pointer(Result)); // Get a pointer to the string 
    for i := 7 downto 0 do begin 
    pStr[i] := Char(Ord('0') + ((Value shr (7 - i)) and 1)); 
    end; 
end; 

通過使用指針,避免每次更新時保護字符串。 此處不需要保護,因爲程序的其他部分不能訪問Result字符串。

德爾福中的字符串保護機制被稱爲Copy On Write(COW),它的工作方式是引用計數器保持每個引用字符串的實例的計數。 當寫入字符串並且引用計數大於1時,會分配一個新字符串用於寫入。

+0

因此,在你的例子中,字符串的副本(或副本?是否每次都複製整個或每次直到完成?)不會被創建,因此它會更快。我懂了。可能來更重的操作和項目得心應手.. :)謝謝。 –

+1

我的例程避免了內置引用計數檢查機制,它具有開銷,這就是更快的原因。 Davids函數的工作方式,仍然沒有複製發生,因爲只有一個持有對字符串的引用。 –

0
function IntToBin2(Value: Integer): string; 
var 
    i, pol: Integer; 
begin 
    Result:= ''; 
    for i := 1 to Value do 
    begin 
    pol:= Value div 2; 
    Result:= IntToStr(Value - pol * 2) + Result; 
    Value:= pol; 
    if pol = 0 then 
     Break; 
    end; 
end;
+2

你能給出你的代碼至少在做什麼的小解釋嗎? – SiKing

1
function TForm1.Dec2Bin(iDec: Integer): string; 
begin 
    Result:=''; 
while iDec>0 do 
    begin 
    Result:=IntToStr(iDec and 1)+Result; 
    iDec:=iDec shr 1; 
    end; 
end; 
+0

請添加說明。 – Robert

相關問題