在 IL 中检测
double
值是否为有限值(既不是 NaN 也不是正/负无穷)而不引发异常的最快方法是什么?
我正在考虑以下方法(c# 表示法仅供读者方便,在我的项目中我为此使用 IL):
!double.IsNaN(x) && !double.IsInfinity(x)
- 最明显,也可能是最慢的,因为涉及 2 个方法调用。 (*(((long*) &x)) & 0x7fffffffffffffffL) < 0x7ff0000000000000L
或在伊利诺伊州:
ldloca x
conv.u
ldind.i8
ldc.i8 0x7fffffffffffffff
and
ldc.i8 0x7ff0000000000000
clt
我对第二种方法的疑问是:
根据我的研究,这应该精确确定任何给定的
x
是否是有限的。这是真的吗?这是在 IL 中解决任务的最佳方法(性能方面),还是有更好(更快)的解决方案?
附注我非常感谢运行我自己的基准测试并找出答案的建议,并且肯定会这样做。只是想也许有人已经遇到过类似的问题并且知道答案。 附言是的,我意识到我们在这里谈论的是纳秒,是的,它们对于我的特殊情况很重要
微软使用这个:
public unsafe static bool IsNaN(double d)
{
return (*(UInt64*)(&d) & 0x7FFFFFFFFFFFFFFFL) > 0x7FF0000000000000L;
}
还有这个:
public unsafe static bool IsInfinity(double d)
{
return (*(long*)(&d) & 0x7FFFFFFFFFFFFFFF) == 0x7FF0000000000000;
}
除非使用
!double.IsNaN(x) && !double.IsInfinity(x)
是你的程序的real瓶颈,我对此表示怀疑,否则我建议你使用这些函数,它们会更容易阅读和维护。
没有 unsafe 上下文和混合 NaN、+Inf、-Inf 值:
var isFinite = ((BitConverter.DoubleToInt64Bits(d) >> 52) & 0x7ff) != 0x7ff;
说明:
双精度数是 64 位值,存储为:
位号:63 62~~~~~~~52 51~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~0 位:0 00000000000 0000000000000000000000000000000000000000000000000000 符号 指数 尾数 如果符号 = 0 && 指数 == 11111111111 && 尾数 == 0 => +无穷大 如果符号 = 1 && 指数 == 11111111111 && 尾数 == 0 => -Infinity 如果指数 == 11111111111 && 尾数 != 0 => NaN 如果指数 != 11111111111 => 有限 换句话说: 如果指数 == 11111111111 => 不有限 如果指数 != 11111111111 => 有限 步骤 1:将 double 转换为 Int64 位 (DoubleToInt64Bits) 步骤 2:右移 52 位以删除尾数 (>> 52) 步骤 3:屏蔽指数位以删除符号 (& 0x7ff) 步骤 4:检查所有剩余位是否都设置为 1 注: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 不是有限的 PositiveInfinity 不是有限的 NaN 不是有限的 Epsilon 是有限的 MinValue 是有限的 MaxValue 是有限的
一个没有不安全的好选择是:
public static bool IsFinite(double value)
{
return (value > double.NegativeInfinity && value < double.PositiveInfinity);
}
我会遵循微软的实施方式,它似乎适合我:
public static bool IsFinite(double d)
{
ulong bits = BitConverter.DoubleToUInt64Bits(d);
return (~bits & PositiveInfinityBits) != 0;
}