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

Delphi 将十进制/整数转换为二进制-它如何以及为什么以这种方式工作?,delphi,delphi-xe3,Delphi,Delphi Xe3,正如David在对答案的评论中所说的,我对这个函数的工作原理非常感兴趣,因为如果将结果长度从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

正如David在对答案的评论中所说的,我对这个函数的工作原理非常感兴趣,因为如果将结果长度从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:learning purposes中的提问,在第一个函数末尾用Copy(Result,25,8)解决了我的问题,因为需要传递8个字符的长字符串,但我真的想了解发生了什么…:)


谢谢

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

str := IntToBinByte(Value and $ff);
假设
是32位数据类型。显然,如果它已经是一个
字节
,则不需要按位

我的拙见是,原来的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;
问题是,尽管
是8位类型,但按位操作在32位寄存器中执行。因此,当执行右移位时,左移位到位号>7的位返回。你可以很容易地解决这个问题,方法是屏蔽掉掉掉下来的部分。像这样:

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

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

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

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;

正如David清楚地回答的那样,您的位移位要么是短的,要么是编译器隐式地扩展了操作数

如果性能很重要,这里有一个比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;
通过使用指针,可以避免每次更新字符串时都对其进行保护。 此处不需要保护,因为程序的其他部分都无法访问
结果
字符串

Delphi中的字符串保护方案称为
写时复制
(COW),其工作原理是使用一个引用计数器,对引用该字符串的每个实例进行计数。
当写入字符串且引用计数大于1时,将为写入分配一个新字符串

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

哦,好的。现在我想我更清楚地知道发生了什么。第二个代码可以完美地工作。:)非常感谢。不过,如果我完全理解这一点,如果我将字节作为值的类型(你在另一个线程的注释中已经建议过),那么这将是8位,而不是32位,因此应该向左移动0,向右移动7,否则就不一样了?我看到了,是的。然而,第三个例子对我不起作用,即使用“and$ff”来称呼它?第四个可以使用,无论是否使用
和$ff
调用。只有第三个一直用我第一次尝试时得到的食物喂我(如有疑问的帖子所示)。Bdw,谢谢大卫,谢谢你的努力。:)您可能对这个问题感兴趣:如果性能很关键,可以使用这个例程,
函数IntToBinByte(值:Byte):String;varⅠ:整数;pStr:PChar;开始设置长度(结果,8);pStr:=PChar(指针(结果));对于i:=7到0,则开始pStr[i]:=Char(Ord('0')+((Value shr(7-i))和1));结束;结束我觉得这要复杂得多。(专注于“对我来说”…)。另外,我需要单独传递长度,因此每次都需要特别注意和额外的数据,如果用于不同的类型…:)感谢您的提示和另一个选项&意见,尽管如此!:)答案也没有真正解决被问到的问题。当然,在一个函数中处理任意长度的数据类型更灵活。我当然会将
p\u nb\u digits
设置为
Integer
类型,即本机整数类型。在这里使用字节没有任何好处。这种方法与我的答案中的第三段代码使用相同的方法。这种方法的缺点是它修改了一个实际的输入参数,如果可能的话最好避免。另一个缺点是编译器似乎没有优化寄存器中的
p_nb_int
。这段代码产生大量mov指令,只是为了执行
p_nb_int:=p_nb_int shr 1优化者能够使用问题中的方法以及我在回答中概述的各种方法删除所有这些问题。在这里,性能可能不是关键,但我认为这仍然是一个有趣的问题。因此,在您的示例中,字符串的副本(或副本?它是否只是在每次创建整个字符串时复制,还是在每次完成之前复制?)