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的值?
该声明在编译时解决了这个问题:
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)