【问题标题】:Delphi: define const double from binary representationDelphi:从二进制表示中定义 const double
【发布时间】:2021-01-16 21:53:06
【问题描述】:

背景

我想创建一个单元测试来测试 Delphi 的某些 rtl 方面,为了做到这一点,我想使用非常具体的 IEEE 浮点值,我想直接从它们的二进制表示中创建。

到目前为止,我已经想出了这个例程,它确实可以完成这项工作。

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;

使用如下(bindata可能还是错,但这不是问题):

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

问题

是否存在允许直接从看起来像这样的二进制(十六进制)表示创建const double 的语法:

const
  cSmallestPositiveDouble:double=double($0000000000000001);

结果显然不应该是1.0(使用这种语法)而是(接近)4.94065645841247e-324

【问题讨论】:

  • 您可以在内联常量声明中使用仅限运行时的函数:begin const cSmallestPositiveDouble: Double = BuildDoubleFromRawInt64($0000000000000001);

标签: delphi type-conversion ieee-754 rad-studio


【解决方案1】:

这可以通过联合直接完成:

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 );

其余的很明显:要么您现在访问ALL.d,以查看43 作为Int64 被解释为Double。或者反过来做。同样,您甚至可以通过访问ALL.f 来检查TFileTime 解释的外观。

在处理或检查二进制文件时,请记住字节顺序也很重要 (LE versus BE) - 特别是在从不同存储类型(FS 与 RAM)读取时。

【讨论】:

【解决方案2】:

用我自己的初始解决方案来回答,语法比我喜欢的要广泛一些,但这似乎可以使用变体记录和隐式类型转换来解决问题。

不错的功能/副作用是您可以在检查器/监视窗口中快速查看二进制表示和双重表示。

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;

那么在使用的时候,声明为const:

procedure TestIEEEDouble.TestCompareValueBoundary;
const
  cSmallestPositiveDouble:RDoubleHelperRec=(BinData:$0000000000000001);
begin
  CheckEquals(0,CompareValue(FSmallestPositiveDouble,cSmallestPositiveDouble),'CompareValue(FSmallestPositiveDouble,cSmallestPositiveDouble)');
end;

【讨论】:

  • 您可以删除(无用的)额外的Bin 字段并使用case Boolean of
  • @OndrejKelle:我做到了,将其更改为整数并添加了ByteData:array[0..sizeof(Double)-1],因此小/大端也变得可见。我现在为SingleDoubleExtended 创建了测试用例
猜你喜欢
  • 2011-09-15
  • 1970-01-01
  • 2022-09-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-06
相关资源
最近更新 更多