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
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).
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;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With