2015-06-18 2 views
6

Каков самый быстрый способ определить, является ли значение double конечным значением (ни NaN, ни положительной/отрицательной бесконечности) в IL, не выбрасывая исключение?Самый быстрый способ определить, является ли двойной конечным?

я рассматривал следующие подходы (C# обозначения для удобства читателя только в моем проекте я использую IL для этого):

  1. !double.IsNaN(x) && !double.IsInfinity(x) - самый очевидный и, наверное, самый медленный, потому что 2 вызовы методов.

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

или IL:

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

Мои вопросы о втором подходе являются:

  1. Согласно моим исследованиям, это должно точно определить, какие данный x конечен. Это правда?

  2. Это лучший способ (по эффективности) решить задачу в IL, или есть лучшее (более быстрое) решение?

P.S. Я действительно ценю рекомендации для запуска своих собственных тестов и выяснения, и, безусловно, это сделаю. Просто подумал, может быть, у кого-то уже была аналогичная проблема и он знает ответ. P.P.S. Да, я понимаю, что мы говорим Аботы наносекунд здесь, и да, они очень важны для моего конкретного случая

ответ

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) является реальным узким местом вашей программы, что я сомневаюсь, я рекомендую вам использовать эти функции, их будет легче читать и обслуживать.

+0

Поместите '' (* ((long *) & x)) & 0x7fffffffffffffffL) <0x7ff0000000000000L'' в функцию '' inline''' и будьте счастливы. –

+1

@BinkanSalaryman да, он мог, но если OP не единственный, кто поддерживает код, он должен написать * очень, очень, очень хороший комментарий ... –

+0

Да, действительно. Если это не требуется, просто придерживайтесь данных .NET-методов. –

1

Без небезопасного контекста и смешивания NaN, + Inf, -Inf значения:

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

Пояснение:

Двойной представляет собой значение 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