Reputation: 1307
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
Upvotes: 8
Views: 3986
Reputation: 164
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;
}
Upvotes: -1
Reputation: 1025
A good alternative without unsafe would be:
public static bool IsFinite(double value)
{
return (value > double.NegativeInfinity && value < double.PositiveInfinity);
}
Upvotes: 3
Reputation: 5721
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
Upvotes: 4
Reputation: 29431
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.
Upvotes: 11