如何使信令NaN易于使用?

Dav*_*nan 10 delphi

IEEE754标准定义了两类NaN,即安静的NaN,QNaN和信令NaN,SNaN.当SNaN加载到浮点寄存器中时,浮点单元会引发异常.

通过NaN声明的常量命名,QNaN可用于Delphi代码Math.该常量的定义是:

const
  NaN = 0.0 / 0.0;
Run Code Online (Sandbox Code Playgroud)

我希望能够使用类似的东西来声明一个信号NaN的常量,但还没有找到一种方法来做到这一点.

天真地你可以写这段代码:

function SNaN: Double;
begin
  PInt64(@Result)^ := $7FF7FFFFFFFFFFFF;//this bit pattern specifies an SNaN
end;
Run Code Online (Sandbox Code Playgroud)

但浮点返回值的ABI意味着SNaN被加载到浮点寄存器中,以便可以返回它.当然,这导致了一个例外,而这个例外却违背了目的.

所以你被引导编写这样的代码:

procedure SetToSNaN(out D: Double);
begin
  PInt64(@D)^ := $7FF7FFFFFFFFFFFF;
end;
Run Code Online (Sandbox Code Playgroud)

现在,这可行,但它非常不方便.假设您需要将SNaN传递给另一个函数.理想情况下你想写:

Foo(SNaN)
Run Code Online (Sandbox Code Playgroud)

但相反,你必须这样做:

var
  SNaN: Double;
....
SetToSNaN(SNaN);
Foo(SNaN);
Run Code Online (Sandbox Code Playgroud)

所以,在积累之后,这就是问题所在.

有没有办法编写x := SNaN并为浮点变量x分配一个信号NaN的值?

LU *_* RD 8

该声明在编译时解决了这个问题:

const
  iNaN : UInt64 = $7FF7FFFFFFFFFFFF;
var
  SNaN : Double absolute iNaN;
Run Code Online (Sandbox Code Playgroud)

编译器仍然将其SNaN视为常量.

尝试分配值SNaN会产生编译时错误:E2064 Left side cannot be assigned to.

procedure DoSomething( var d : Double);
begin
  d := 2.0;
end;

SNaN := 2.0; // <-- E2064 Left side cannot be assigned to
DoSomething( SNaN); // <--E2197 Constant object cannot be passed as var parameter
WriteLn(Math.IsNaN(SNaN)); // <-- Writes "true"
Run Code Online (Sandbox Code Playgroud)

如果你有编译器指令$WRITEABLECONSTS ON(或$J+),可以暂时关闭它以确保不改变SNaN.

{$IFOPT J+}
   {$DEFINE UNDEFWRITEABLECONSTANTS}
   {$J-}
{$ENDIF}

const
  iNaN : UInt64 = $7FF7FFFFFFFFFFFF;
var
  SNaN : Double ABSOLUTE iNaN;

{$IFDEF UNDEFWRITEABLECONSTANTS}
   {$J+}
{$ENDIF}
Run Code Online (Sandbox Code Playgroud)