如何使信号NaN容易使用? [英] How can I make signaling NaNs easy to work with?

查看:249
本文介绍了如何使信号NaN容易使用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

IEEE754标准定义了两类NaN,安静的NaN,QNaN和信号NaN,SNaN。当SNaN加载到浮点寄存器中时,浮点单元会产生异常。



QNaN通过名为<$ c $的常量可用于Delphi代码在 Math 中声明的c> NaN 。该常量的定义是:

  const 
NaN = 0.0 / 0.0;

我想要使用类似的东西来声明一个常数,这是一个信号NaN,但是还没有找到一种办法。



你可以写这段代码:

 函数SNaN:Double; 
begin
PInt64(@Result)^:= $ 7FF7FFFFFFFFFFFF; //这个位模式指定一个SNaN
end;

但ABI的浮点返回值意味着SNaN加载到浮点寄存器,以便它可以返回。当然,这导致了一个例外,而不是打破目的。



所以,你可以这样编写代码:

 程序SetToSNaN(out D:Double); 
begin
PInt64(@D)^:= $ 7FF7FFFFFFFFFFFF;
结束

现在,这是有效的,但这是非常不方便的。假设您需要将SNaN传递给另一个功能。理想情况下,您想写:

  Foo(SNaN)

,而您必须这样做:

  var 
SNaN:双
....
SetToSNaN(SNaN);
Foo(SNaN);

所以,建立之后,这里是问题。



有没有办法写入 x:= SNaN 并将浮点变量 x 一个值是一个信号NaN?

解决方案

此声明在编译时解决:

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

编译器仍将 SNaN 视为



尝试将值分配给 SNaN 会给出编译时错误: E2064左侧无法分配给

 程序DoSomething(var d:Double); 
begin
d:= 2.0;
结束

SNaN:= 2.0; // < - E2064左侧不能分配给
DoSomething(SNaN); //< - E2197常量对象不能作为var参数
WriteLn(Math.IsNaN(SNaN))传递; //< - 写true

如果您有编译器指令 $ WRITEABLECONSTS ON (或 $ J + ),这可以暂时关闭,以确保不会更改 SNaN

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

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

{$ IFDEF UNDEFWRITEABLECONSTANTS}
{$ J +}
{$ ENDIF}


The IEEE754 standard defines two classes of NaN, the quiet NaN, QNaN, and the signaling NaN, SNaN. When an SNaN is loaded into a floating point register, an exception is raised by the floating point unit.

QNaN is available to Delphi code through the constant named NaN that is declared in Math. The definition of that constant is:

const
  NaN = 0.0 / 0.0;

I would like to be able to use something similar to declare a constant that is a signaling NaN, but have not yet found a way to do that.

Naively you might write this code:

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

But the ABI for floating point return values means that the SNaN is loaded into a floating point register so that it can be returned. Naturally that leads to an exception which rather defeats the purpose.

So you are then led to writing code like this:

procedure SetToSNaN(out D: Double);
begin
  PInt64(@D)^ := $7FF7FFFFFFFFFFFF;
end;

Now, this works, but it's very inconvenient. Suppose you need to pass an SNaN to another function. Ideally you would like to write:

Foo(SNaN)

but instead you have to do this:

var
  SNaN: Double;
....
SetToSNaN(SNaN);
Foo(SNaN);

So, after the build-up, here's the question.

Is there any way to write x := SNaN and have the floating point variable x assigned a value that is a signaling NaN?

解决方案

This declaration solves it at compile time:

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

The compiler still treats the SNaN as a constant.

Trying to assign a value to SNaN will give a compile time error: 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"

Should you have the compiler directive $WRITEABLECONSTS ON (or $J+), this could be turned off temporarily to ensure not altering SNaN.

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

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

{$IFDEF UNDEFWRITEABLECONSTANTS}
   {$J+}
{$ENDIF}

这篇关于如何使信号NaN容易使用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆