检测双精度是否有限的最快方法是什么?

时间:2015-06-18 08:07:11

标签: c# .net il

在没有抛出异常的情况下,检测IL中double值是否为有限值(NaN和正/负无穷大)的最快方法是什么?

我正在考虑以下方法(c#符号仅供读者使用,在我的项目中我使用IL进行此操作):

  1. !double.IsNaN(x) && !double.IsInfinity(x) - 最明显的,也可能是最慢的,因为涉及2个方法调用。

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

  3. 或在IL中:

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

    关于第二种方法的问题是:

    1. 根据我的研究,这应该精确地确定任何给定的x是否是有限的。这是真的吗?

    2. 在IL中解决任务是最好的方法(性能方面),还是有更好(更快)的解决方案?

    3. P.S。我非常感谢建议运行我自己的基准并找出答案,并且肯定会这样做。只是想到也许有人已经有类似的问题,并知道答案。 P.P.S.是的,我意识到我们在这里谈论的是纳秒,是的,它们对我的特殊情况非常重要

3 个答案:

答案 0 :(得分:10)

Microsoft使用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)是您的程序的真正的瓶颈,我怀疑,我建议您使用这些功能,它们将更易于阅读和维护。

答案 1 :(得分:1)

没有不安全上下文并混合 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

答案 2 :(得分:0)

没有不安全的好选择是:

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