将十进制/整数转换为二进制 - 它的工作方式和原理如何?

时间:2014-01-26 09:05:29

标签: delphi delphi-xe3

正如David在回答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个字符长字符串传递,但我真的想了解发生了什么... :)

由于

6 个答案:

答案 0 :(得分:2)

代码中的左移意味着将您感兴趣的位移到数据类型的最左边。通过这样做,左边的所有位都移出结束并丢失。然后当你再次向右移动时,我们一直转移到另一端。结果是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;

这段代码真的很复杂我不建议任何人使用它。在我看来,最好的版本是我的答案中的第三个代码块。

答案 1 :(得分: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;

答案 2 :(得分:1)

大卫如此清楚地回答,你的位移要么是短暂的,要么是操作数被编译器隐式扩展。

如果表现很重要,这里的例行程序比大卫提出的程序要快。

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字符串。

Delphi中的字符串保护方案称为Copy On Write(COW),它的工作原理是使用一个引用计数器来计算引用该字符串的每个实例。 当写入字符串并且引用计数大于1时,将为写入分配新字符串。

答案 3 :(得分: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;

答案 4 :(得分: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;

答案 5 :(得分:0)

必须为我的2020年南非IT 12年级期末考试编写这篇文章。 它在GUI应用程序中,在这个示例中,我使用了控制台应用程序。

program IntToBinary;
    
    {$APPTYPE CONSOLE}
    
    {$R *.res}
    
    uses
      System.SysUtils,StrUtils;
    
    var
    i,iNumber,iReminder:integer;
    sTemp:string;
    sBinary:string;
    begin
      try
        { TODO -oUser -cConsole Main : Insert code here }
          Writeln('Enter integer number:');
          Readln(Input,iNumber);
          sTemp:= '';
          repeat
          iNumber := iNumber DIV 2;
          iReminder:= iNumber MOD 2;
          sTemp:= sTemp+' '+inttostr(iReminder);
          until (iNumber = 0);
    
          sBinary:= AnsiReverseString(sTemp);
          Writeln(sBinary);
          readln;
    
      except
        on E: Exception do
          Writeln(E.ClassName, ': ', E.Message);
      end;
    end.