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):
!double.IsNaN(x) && !double.IsInfinity(x)
- the most obvious and, probably, the slowest because 2 method calls are involved.
(*(((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:
According to my research, this should precisely determine whether any given x
is finite. Is this true?
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
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.
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:
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With