检测 double 是否有限的最快方法?
The fastest way to detect if a double is finite?
在不抛出异常的情况下,在 IL 中检测 double
值是否为有限值(既不是 NaN 也不是 positive/negative 无穷大)的最快方法是什么?
我正在考虑以下方法(c# 符号只是为了 reader 的方便,在我的项目中我为此使用 IL):
!double.IsNaN(x) && !double.IsInfinity(x)
- 最明显,也可能是最慢的,因为涉及 2 个方法调用。
(*(((long*) &x)) & 0x7fffffffffffffffL) < 0x7ff0000000000000L
或在 IL 中:
ldloca x
conv.u
ldind.i8
ldc.i8 0x7fffffffffffffff
and
ldc.i8 0x7ff0000000000000
clt
我对第二种方法的问题是:
根据我的研究,这应该精确地确定任何给定的 x
是否是有限的。这是真的吗?
这是在 IL 中解决任务的最佳方法(性能方面),还是有更好(更快)的解决方案?
P.S。我非常感谢对 运行 我自己的基准测试的建议并找出答案,并且肯定会这样做。只是想也许有人已经遇到过类似的问题并且知道答案。
P.P.S。是的,我知道我们在这里谈论的是纳秒,是的,它们对我的特殊情况很重要
微软使用 this:
public unsafe static bool IsNaN(double d)
{
return (*(UInt64*)(&d) & 0x7FFFFFFFFFFFFFFFL) > 0x7FF0000000000000L;
}
和this:
public unsafe static bool IsInfinity(double d)
{
return (*(long*)(&d) & 0x7FFFFFFFFFFFFFFF) == 0x7FF0000000000000;
}
除非!double.IsNaN(x) && !double.IsInfinity(x)
的使用是你程序的真正的瓶颈,我对此表示怀疑,我建议你使用这些函数,它们会更容易阅读并保持。
没有 unsafe 上下文和混合 NaN, +Inf, -Inf 值:
var isFinite = ((BitConverter.DoubleToInt64Bits(d) >> 52) & 0x7ff) != 0x7ff;
解释:
double 是一个 64 位值,存储为:
- 1 位用于 符号
- 11 位用于 指数
- 52 位用于 尾数
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
最后,这可以简化为:
var isFinite = (BitConverter.DoubleToInt64Bits(d) & 0x7ff0000000000000) != 0x7ff0000000000000;
在扩展方法和不安全上下文中:
internal static class ExtensionMethods
{
public static unsafe bool IsFinite(this double d) => (*(long*)&d & 0x7ff0000000000000) != 0x7ff0000000000000;
}
测试:
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"));
结果:
NegativeInfinity is not finite
PositiveInfinity is not finite
NaN is not finite
Epsilon is finite
MinValue is finite
MaxValue is finite
一个没有不安全的好选择是:
public static bool IsFinite(double value)
{
return (value > double.NegativeInfinity && value < double.PositiveInfinity);
}
在不抛出异常的情况下,在 IL 中检测 double
值是否为有限值(既不是 NaN 也不是 positive/negative 无穷大)的最快方法是什么?
我正在考虑以下方法(c# 符号只是为了 reader 的方便,在我的项目中我为此使用 IL):
!double.IsNaN(x) && !double.IsInfinity(x)
- 最明显,也可能是最慢的,因为涉及 2 个方法调用。(*(((long*) &x)) & 0x7fffffffffffffffL) < 0x7ff0000000000000L
或在 IL 中:
ldloca x
conv.u
ldind.i8
ldc.i8 0x7fffffffffffffff
and
ldc.i8 0x7ff0000000000000
clt
我对第二种方法的问题是:
根据我的研究,这应该精确地确定任何给定的
x
是否是有限的。这是真的吗?这是在 IL 中解决任务的最佳方法(性能方面),还是有更好(更快)的解决方案?
P.S。我非常感谢对 运行 我自己的基准测试的建议并找出答案,并且肯定会这样做。只是想也许有人已经遇到过类似的问题并且知道答案。 P.P.S。是的,我知道我们在这里谈论的是纳秒,是的,它们对我的特殊情况很重要
微软使用 this:
public unsafe static bool IsNaN(double d)
{
return (*(UInt64*)(&d) & 0x7FFFFFFFFFFFFFFFL) > 0x7FF0000000000000L;
}
和this:
public unsafe static bool IsInfinity(double d)
{
return (*(long*)(&d) & 0x7FFFFFFFFFFFFFFF) == 0x7FF0000000000000;
}
除非!double.IsNaN(x) && !double.IsInfinity(x)
的使用是你程序的真正的瓶颈,我对此表示怀疑,我建议你使用这些函数,它们会更容易阅读并保持。
没有 unsafe 上下文和混合 NaN, +Inf, -Inf 值:
var isFinite = ((BitConverter.DoubleToInt64Bits(d) >> 52) & 0x7ff) != 0x7ff;
解释:
double 是一个 64 位值,存储为:
- 1 位用于 符号
- 11 位用于 指数
- 52 位用于 尾数
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
最后,这可以简化为:
var isFinite = (BitConverter.DoubleToInt64Bits(d) & 0x7ff0000000000000) != 0x7ff0000000000000;
在扩展方法和不安全上下文中:
internal static class ExtensionMethods
{
public static unsafe bool IsFinite(this double d) => (*(long*)&d & 0x7ff0000000000000) != 0x7ff0000000000000;
}
测试:
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"));
结果:
NegativeInfinity is not finite PositiveInfinity is not finite NaN is not finite Epsilon is finite MinValue is finite MaxValue is finite
一个没有不安全的好选择是:
public static bool IsFinite(double value)
{
return (value > double.NegativeInfinity && value < double.PositiveInfinity);
}