Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I make signaling NaNs easy to work with?

Tags:

delphi

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?

like image 608
David Heffernan Avatar asked Apr 27 '13 08:04

David Heffernan


People also ask

What is NaN software?

NaN provides development and consulting services for software projects. It is currently a one man business run by Nir Nahum. NaN's core values: Integrity – Always be honest and do what you said you would do, when you said you would do it. Efficiency – Reuse code wherever applicable. Automate tasks as much as possible.

How is NaN represented?

A signaling NaN is represented by any bit pattern between X'7F80 0001' and X'7FBF FFFF' or between X'FF80 0001' and X'FFBF FFFF'. A quiet NaN is represented by any bit pattern between X'7FC0 0000' and X'7FFF FFFF' or between X'FFC0 0000' and X'FFFF FFFF'.

What would a SNaN value be used for?

SNaNs are typically used to trap or invoke an exception handler. They must be inserted by software; that is, the processor never generates an SNaN as a result of a floating-point operation. This can be seen from our example where both: float div_0_0 = 0.0f / 0.0f; float sqrt_negative = std::sqrt(-1.0f);

Is NaN infinity?

In floating-point calculations, NaN is not the same as infinity, although both are typically handled as special cases in floating-point representations of real numbers as well as in floating-point operations.


1 Answers

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}
like image 120
LU RD Avatar answered Oct 05 '22 00:10

LU RD