delphi 将十进制/整数转换为二进制-如何以及为什么它的工作方式?

41ik7eoe  于 2022-11-23  发布在  其他
关注(0)|答案(6)|浏览(310)

正如大卫在这里回答的评论中所问的,我对这个函数的工作原理非常感兴趣,因为如果将结果长度从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

16岁的人也是这样,而不是8岁。
尝试将长字更改为整数和字节,但得到相同的结果。
所以......嗯......我在这里错过了什么,不明白吗?:/
PS:出于学习的目的,在第一个函数的末尾用Copy(Result,25,8)解决了我的情况,因为需要传递8个字符长的字符串,但我真的很想弄明白是怎么回事...:)
谢谢

pqwbnv8z

pqwbnv8z1#

作为David so clearly answered,您的位移位太短,或者编译器隐式扩展了操作数。
如果性能很重要,那么下面的例程比大卫介绍的例程更快:

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时,一个新的字符串被分配用于写入。

mkshixfv

mkshixfv2#

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

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

0h4hbjxa

0h4hbjxa3#

就我个人而言,我会这么做:

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;
a8jjtwal

a8jjtwal4#

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;
gmol1639

gmol16395#

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;
vuv7lop3

vuv7lop36#

我不得不为我的南非IT 12级期末考试2020考试写这个。它是在一个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.

相关问题