Converting decimal/integer to binary - how and why it works the way it does?

杀马特。学长 韩版系。学妹 提交于 2019-11-28 14:00:58

The left shift in the code is meant to shift the bit you are interested in to the very left hand edge of the data type. By doing so, all bits to the left are shifted off the end and lost. Then when you shift right again, we shift all the way to the other end. The result is either 0 or 1.

However, your data type is still 32 bits, and so you are not shifting far enough. You are not getting all the bits to the left of the target bit to fall off the end. And so they return when you shift to the right.

To make your code work you need this:

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;

A version that might be easier to understand, in relation to the original, would be like this:

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;

Frankly however it is better to operate on a single byte. And I find this double shifting to be a little obscure. I'd use a single shift and a bit mask. Like this:

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;

And call it like this

str := IntToBinByte(Value and $ff);

assuming that Value is a 32 bit data type. Obviously if it is already a Byte then you don't need the bitwise and.

And the original 32 bit function would read better like this, in my humble opinion.


Earlier versions of this answer had the following incorrect attempt to solve the problem:

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;

The problem is that, even though Value is an 8 bit type, the bitwise operations are performed in 32 bit registers. So the bits that are left shifted to bit number >7 return when the right shift is performed. You can fix this easily enough by masking out those bits that are meant to fall off the end. Like this:

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;

This code is really convoluted I don't recommend that anyone ever uses it. The best version, in my opinion, is the third block of code in my answer.

Magoo

Personally, I'd do it this way:

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;

As David so clearly answered, your bitshifting was either to short, or the operand was implicitly expanded by the compiler.

If performance is important, here is a routine that is faster than the one David presented.

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;

By working with a pointer, you avoid protecting the string every time it is updated. The protection is not needed here, since no other part of your program can access the Result string.

The string protection scheme in Delphi is called Copy On Write (COW), and works by having a reference counter that keeps count on every instance that is referencing the string. When a string is written on and the reference count is greater than 1, a new string is allocated for writing.

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;
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;
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!