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

Tha*_*arc 5 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
      Result[i] := '0'
    end else begin
      Result[i] := '1';
    end;
  end;
end;
Run Code Online (Sandbox Code Playgroud)

这在某种程度上工作得很好。(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;
Run Code Online (Sandbox Code Playgroud)

但我得到的结果如下:

 1: 00000001
 2: 00000011
 3: 00000011
 4: 00000111
 5: 00000111
 6: 00000111
 7: 00000111
 8: 00001111
 9: 00001111
10: 00001111
11: 00001111
12: 00001111
Run Code Online (Sandbox Code Playgroud)

16 个而不是 8 个有点相同。

也尝试将 LongWord 更改为 Integer 和 Byte,但得到了相同的结果。

那么...嗯...我在这里想念什么,不明白吗?:/

PS:出于学习目的询问,在第一个函数末尾用 Copy(Result, 25, 8) 解决了我的情况,因为需要传递 8 个字符长的字符串,但我真的想了解发生了什么......:)

谢谢

Dav*_*nan 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;
Run Code Online (Sandbox Code Playgroud)

相对于原始版本,可能更容易理解的版本如下:

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;
Run Code Online (Sandbox Code Playgroud)

但坦率地说,最好对单个字节进行操作。我发现这种双重转变有点晦涩难懂。我会使用一个移位和一个位掩码。像这样:

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;
Run Code Online (Sandbox Code Playgroud)

并这样称呼它

str := IntToBinByte(Value and $ff);
Run Code Online (Sandbox Code Playgroud)

假设Value是 32 位数据类型。显然,如果它已经是 aByte那么你不需要按位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;
Run Code Online (Sandbox Code Playgroud)

问题是,尽管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;
Run Code Online (Sandbox Code Playgroud)

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