2015-06-18 12 views
6

Qual è il modo più veloce per rilevare se un valore double è un valore finito (né NaN né infinito positivo/negativo) in IL senza generare un'eccezione?Il modo più veloce per rilevare se un doppio è finito?

stavo considerando i seguenti approcci (C# notazione per comodità del lettore solo, nel mio progetto che sto utilizzando IL per questo):

  1. !double.IsNaN(x) && !double.IsInfinity(x) - il più evidente e, probabilmente, il più lento a causa 2 le chiamate al metodo sono coinvolte.

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

o in IL:

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

mie domande circa il secondo approccio sono:

  1. Secondo la mia ricerca, questo dovrebbe determinare con precisione se qualsiasi dato x è finito. È vero?

  2. È il modo migliore (in termini di prestazioni) per risolvere l'attività in IL oppure esiste una soluzione migliore (più veloce)?

P.S. Apprezzo le raccomandazioni per gestire i miei benchmark e scoprirlo, e lo farò sicuramente. Pensavo che qualcuno avesse già un problema simile e conoscesse la risposta. P.P.S. Sì, mi rendo conto che stiamo parlando nanosecondi abot qui, e sì, sono importanti per il mio caso particolare

risposta

10

Microsoft usa this:

public unsafe static bool IsNaN(double d) 
{ 
    return (*(UInt64*)(&d) & 0x7FFFFFFFFFFFFFFFL) > 0x7FF0000000000000L; 
} 

E this:

public unsafe static bool IsInfinity(double d) 
{ 
    return (*(long*)(&d) & 0x7FFFFFFFFFFFFFFF) == 0x7FF0000000000000; 
} 

A meno che l'uso di !double.IsNaN(x) && !double.IsInfinity(x) è il collo di bottiglia del reale del tuo programma, che dubito, ti consiglio di utilizzare queste funzioni, saranno più facili da leggere e gestire.

+0

put '' (* (((long *) & x)) E 0x7fffffffffffffffL) <0x7ff0000000000000L'' in una funzione '' inline''d e b e felice. –

+1

@BinkanSalaryman sì, potrebbe, ma se l'OP non è l'unico a mantenere il codice, deve scrivere un * molto, molto, molto * buon commento ... –

+0

Sì, davvero. A meno che non sia necessario attenersi semplicemente ai metodi del framework .NET forniti. –

1

Senza pericoloso contesto e miscelazione NaN, + Inf, -Inf valori:

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

Spiegazione:

Un doppio è un valore di 64 bit memorizzato come:

  • 1 bit per il segno
  • 11 bit per il esponente
  • 52 bit per il mantissa
 
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 

Infine, questo può essere semplificato per:

var isFinite = (BitConverter.DoubleToInt64Bits(d) & 0x7ff0000000000000) != 0x7ff0000000000000; 

in metodo di estensione e pericoloso contesto:

internal static class ExtensionMethods 
{ 
    public static unsafe bool IsFinite(this double d) => (*(long*)&d & 0x7ff0000000000000) != 0x7ff0000000000000; 
} 

Test:

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")); 

Risultato:

 
NegativeInfinity is not finite 
PositiveInfinity is not finite 
NaN is not finite 
Epsilon is finite 
MinValue is finite 
MaxValue is finite