Search code examples
delphi-xe2

Pls correct these functions of float number (type: double) under binary form in delphi


I've written 2 functions to convert a float number into/from binary, but the result is incorrect. Please help me to find its bug. (I found some FAQ for this topic, but they was written for C/C++)

function MyFloatToBin(d: double): String;
var
  d_ptr: ^Int64;
  d_str: string;
  i: Integer;
  ch: char;
begin
  d_ptr:= @d;
  d_str:= '';
  for i:= 0 to 63 do begin
    if (d_ptr^ and (1 shl i)) > 0 then
      ch:= '1'
    else
      ch:= '0';
    d_str:= d_str + ch;
  end;
  Result:= 'F' + d_str;
end;

function MyBinToFloat: Double;
var
  d_str: String;
  i64: Int64;
  d_ptr: ^double;
  i, len: Integer;
begin
  d_str:= pop;
  len:= length(d_str);
  if (pos('F', d_str) <> 1)and(len <> 65) then begin
    push(d_str);
    exit;
  end;
  i64:= 0;
  for i:= 2 to len do
    if d_str[i] = '1' then
      i64:= i64 or (1 shl (i - 2));
  d_ptr:= @i64;
  Result:= d_ptr^;
end;

Using

temp: string;
f: double;

temp:= MyFloatToBin(pi);//pi = 3.14....
f:= MyBinToFloat(temp);//result at f is 0

I wonder the variable f should be 3.14... but..??? Please help me correct them. Thanks


Solution

  • Your problem is essentially the mask computation. You do 1 shl I which gives a 32 bit value. You must do UInt64(1) shl I to get a 64 bit value.

    Here is your code fixed:

    function MyFloatToBinString(d: double): String;
    var
        VP : ^UInt64;
        I  : Integer;
    begin
        VP  := @d;
        Result := 'F';
        for I := 63 downto 0 do begin
            if (VP^ and (UInt64(1) shl I)) <> 0 then
                Result := Result + '1'
            else
                Result := Result + '0';
        end;
    end;
    
    function MyBinStringToFlat(S : String) : Double;
    var
        V : UInt64;
        I : Integer;
        J : Integer;
    begin
        if (Length(S) <> 65) or ((S[1] <> 'F') and (S[1] <> 'f')) then
            raise Exception.Create('Invalid format');
    
        V := 0;
        for I := 65 downto 2 do begin
            case S[I] of
            '1': V := V or (UInt64(1) shl (65 - I));
            '0': { Nothing to do };
            else
                raise Exception.Create('Invalid format');
            end;
        end;
        Result := PDouble(@V)^;
    end;
    

    And if you want to test:

    procedure TForm1.Button1Click(Sender: TObject);
    var
        V1 : Double;
        V2 : Double;
        S  : String;
    begin
        V1 := 3.1416;
        Memo1.Lines.Add(IntToHex(PUInt64(@V1)^));
        S := MyFloatToBinString(V1);
        Memo1.Lines.Add(S);
        V2 := MyBinStringToFlat(S);
        Memo1.Lines.Add(V2.ToString);
    end;