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

问题描述 投票:0回答:4

在 IL 中检测

double
值是否为有限值(既不是 NaN 也不是正/负无穷)而不引发异常的最快方法是什么?

我正在考虑以下方法(c# 表示法仅供读者方便,在我的项目中我为此使用 IL):

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

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

或在伊利诺伊州:

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

我对第二种方法的疑问是:

  1. 根据我的研究,这应该精确确定任何给定的

    x
    是否是有限的。这是真的吗?

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

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

c# .net il
4个回答
11
投票

微软使用这个

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瓶颈,我对此表示怀疑,否则我建议你使用这些函数,它们会更容易阅读和维护。


4
投票

没有 unsafe 上下文和混合 NaN+Inf-Inf 值:

var isFinite = ((BitConverter.DoubleToInt64Bits(d) >> 52) & 0x7ff) != 0x7ff;

说明:

双精度数是 64 位值,存储为:

  • 1 位用于 符号
  • 11 位用于指数
  • 52 位用于尾数
位号: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 是有限的

3
投票

一个没有不安全的好选择是:

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

0
投票

我会遵循微软的实施方式,它似乎适合我:


    public static bool IsFinite(double d)
    {
        ulong bits = BitConverter.DoubleToUInt64Bits(d);
        return (~bits & PositiveInfinityBits) != 0;
    }

https://source.dot.net/#System.Private.CoreLib/src/libraries/System.Private.CoreLib/src/System/Double.cs,169

© www.soinside.com 2019 - 2024. All rights reserved.