Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delphi: define const double from binary representation

Background

I want to create a unit test to test some rtl aspects of Delphi, and in order to do this I want to use very specific IEEE floating point values that I want to create directly from their binary representation.

I have come up so far with this routine, and it actually does the job.

function TestIEEEDouble.BuildDoubleFromRawInt64(const aBinData:UInt64):double;
begin
  CheckEquals(sizeof(aBinData),sizeof(Result),'sizeof(aBinData),Sizeof(Result)'); // ensures we dont mess up with wrong types
  Move(aBinData,Result,sizeof(Result));
end;

It is used as follows (bindata may still be wrong, but thats not the question):

procedure TestIEEEDouble.Setup;
begin
  inherited;
  FSmallestPositiveDouble:=BuildDoubleFromRawInt64($0000000000000001);
  FSmallestNegativeDouble:=BuildDoubleFromRawInt64($8000000000000001);
  FLargestPositiveDouble :=BuildDoubleFromRawInt64($7FEFFFFFFFFFFFFF);
  FLargestNegativeDouble :=BuildDoubleFromRawInt64($8FEFFFFFFFFFFFFF);
end;

Question

Is there syntax that would allow creating a const double directly from a binary (hex) representation looking something like this:

const
  cSmallestPositiveDouble:double=double($0000000000000001);

Where the result obviously should NOT be 1.0 (it is with this syntax) but (close to) 4.94065645841247e-324

like image 260
H.Hasenack Avatar asked Jan 16 '21 21:01

H.Hasenack


2 Answers

This can be done directly thru unions:

type  // In a union all variables share the same address
  TAll= packed record
    case Integer of
    1:( d: Double );
    2:( i: Int64 );
    3:( f: TFileTime );
  end;

const  // Define the constant just like you would define an array
  ALL: TAll= ( i: 43 );

The rest is obvious: either you now access ALL.d to see how 43 as Int64 is interpreted as Double. Or do it the other way around. Likewise you can even check how the TFileTime interpretation looks by accessing ALL.f.

When dealing with or checking binary keep in mind that the byte order is also important (LE versus BE) - especially when reading from different storage types (FS versus RAM).

like image 188
AmigoJack Avatar answered Sep 18 '22 07:09

AmigoJack


To answer with my own initial solution, the syntax is a bit more extensive than I like, but this seems to do the trick using a variant record and implicit type casting.

Nice feature/side effect is that you can quickly see binary representation and double representation together in the inspector/watchwindow.

type
  RDoubleHelperRec=record
    class operator Implicit(aRec:RDoubleHelperRec):double;
    case Bin:boolean of
      True:
        (BinData:UINT64);
      False:
        (DoubleData:double);
  end;
...
class operator RDoubleHelperRec.Implicit(aRec: RDoubleHelperRec): double;
begin
  Result:=aRec.DoubleData;
end;

then when using it, declare as const:

procedure TestIEEEDouble.TestCompareValueBoundary;
const
  cSmallestPositiveDouble:RDoubleHelperRec=(BinData:$0000000000000001);
begin
  CheckEquals(0,CompareValue(FSmallestPositiveDouble,cSmallestPositiveDouble),'CompareValue(FSmallestPositiveDouble,cSmallestPositiveDouble)');
end;
like image 40
H.Hasenack Avatar answered Sep 20 '22 07:09

H.Hasenack