The fastest way to detect if a double is finite?

3.6k views Asked by At

What is the fastest way to detect whether a double value is a finite value (neither NaN nor positive/negative infinity) in IL without throwing an exception?

I was considering the following approaches (c# notation for reader's convenience only, in my project I'm using IL for this):

  1. !double.IsNaN(x) && !double.IsInfinity(x) - the most obvious and, probably, the slowest because 2 method calls are involved.

  2. (*(((long*) &x)) & 0x7fffffffffffffffL) < 0x7ff0000000000000L

or in IL:

  ldloca x
  conv.u 
  ldind.i8 
  ldc.i8 0x7fffffffffffffff
  and 
  ldc.i8 0x7ff0000000000000
  clt 

My questions about the second approach are:

  1. According to my research, this should precisely determine whether any given x is finite. Is this true?

  2. Is it the best possible way (performance-wise) to solve the task in IL, or is there a better (faster) solution?

P.S. I do appreciate recommendations to run my own benchmarks and find out, and will most certainly do this. Just thought maybe someone already had similar problem and knows the answer. P.P.S. Yes, I realize that we are talking abot nanoseconds here, and yes, they are important for my particular case

4

There are 4 answers

7
Thomas Ayoub On BEST ANSWER

Microsoft use this:

public unsafe static bool IsNaN(double d)
{
    return (*(UInt64*)(&d) & 0x7FFFFFFFFFFFFFFFL) > 0x7FF0000000000000L;
}

And this:

public unsafe static bool IsInfinity(double d) 
{
    return (*(long*)(&d) & 0x7FFFFFFFFFFFFFFF) == 0x7FF0000000000000;
}

Unless the use of !double.IsNaN(x) && !double.IsInfinity(x) is the real bottleneck of your program, which I doubt, I recommend you to use theses functions, they will be easier to read and maintain.

1
lilo0 On

A good alternative without unsafe would be:

public static bool IsFinite(double value)
{
    return (value > double.NegativeInfinity && value < double.PositiveInfinity);
}
0
Moustafa Khalil On

I would follow how Microsoft implemented it seems suitable for me:


    public static bool IsFinite(double d)
    {
        ulong bits = BitConverter.DoubleToUInt64Bits(d);
        return (~bits & PositiveInfinityBits) != 0;
    }

https://source.dot.net/#System.Private.CoreLib/src/libraries/System.Private.CoreLib/src/System/Double.cs,169

0
alex On

Without unsafe context and mixing NaN, +Inf, -Inf values:

var isFinite = ((BitConverter.DoubleToInt64Bits(d) >> 52) & 0x7ff) != 0x7ff;

Explanation:

A double is a 64 bits value stored as:

  • 1 bit for the sign
  • 11 bits for the exponent
  • 52 bits for the mantissa
Bit No:  63  62~~~~~~~52  51~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~0
Bit:      0  00000000000  0000000000000000000000000000000000000000000000000000
       sign   exponent                         mantissa

If sign = 0 && exponent == 11111111111 && mantissa == 0   =>   +Infinity
If sign = 1 && exponent == 11111111111 && mantissa == 0   =>   -Infinity
If             exponent == 11111111111 && mantissa != 0   =>   NaN
If             exponent != 11111111111                    =>   Finite

In other terms:
If exponent == 11111111111   =>   Not finite
If exponent != 11111111111   =>   Finite

Step 1: Convert double as Int64 bits (DoubleToInt64Bits)
Step 2: Shift right 52 bits to remove mantissa (>> 52)
Step 3: Mask exponent bits to remove sign (& 0x7ff)
Step 4: Check if all remaining bits are set to 1

Note: 0b11111111111 = 0x7ff = 2047

Finally, this can be simplified to:

var isFinite = (BitConverter.DoubleToInt64Bits(d) & 0x7ff0000000000000) != 0x7ff0000000000000;

In extension method and unsafe context:

internal static class ExtensionMethods
{
    public static unsafe bool IsFinite(this double d) => (*(long*)&d & 0x7ff0000000000000) != 0x7ff0000000000000;
}

Tests:

Console.WriteLine("NegativeInfinity is " + (double.NegativeInfinity.IsFinite() ? "finite" : "not finite"));
Console.WriteLine("PositiveInfinity is " + (double.PositiveInfinity.IsFinite() ? "finite" : "not finite"));
Console.WriteLine("NaN is " + (double.NaN.IsFinite() ? "finite" : "not finite"));
Console.WriteLine("Epsilon is " + (double.Epsilon.IsFinite() ? "finite" : "not finite"));
Console.WriteLine("MinValue is " + (double.MinValue.IsFinite() ? "finite" : "not finite"));
Console.WriteLine("MaxValue is " + (double.MaxValue.IsFinite() ? "finite" : "not finite"));

Result:

NegativeInfinity is not finite
PositiveInfinity is not finite
NaN is not finite
Epsilon is finite
MinValue is finite
MaxValue is finite