2015-06-18 17 views
6

Was ist der schnellste Weg zu erkennen, ob ein double Wert ein endlicher Wert (weder NaN noch positive/negative Unendlichkeit) in IL ist, ohne eine Ausnahme zu werfen?Der schnellste Weg zu erkennen, ob ein Double endlich ist?

ich erwäge die folgenden Ansätze (C# Notation für die Bequemlichkeit des Lesers nur, in meinem Projekt verwende ich IL für diese):

  1. !double.IsNaN(x) && !double.IsInfinity(x) - das offensichtlichste und wahrscheinlich die langsamste weil 2 Methodenaufrufe sind beteiligt.

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

oder in IL:

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

Meine Fragen über den zweiten Ansatz sind:

  1. Nach meinen Recherchen, sollte diese genau bestimmen, ob eine gegeben x ist endlich. Ist das wahr?

  2. Ist es der bestmögliche Weg (Performance-weise), um die Aufgabe in IL zu lösen, oder gibt es eine bessere (schnellere) Lösung?

P.S. Ich schätze Empfehlungen, meine eigenen Benchmarks zu betreiben und herauszufinden, und werde dies sicherlich tun. Dachte nur, vielleicht hat jemand schon ähnliches Problem und kennt die Antwort. P.P.S. Ja, ich merke, dass wir hier abot ns sprechen, und ja, sie sind wichtig für meinen speziellen Fall

Antwort

10

Microsoft this verwenden:

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

Und this:

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

Es sei denn, die Verwendung von !double.IsNaN(x) && !double.IsInfinity(x) ist der echte Flaschenhals Ihres Programms, die ich bezweifle, empfehle ich Ihnen, diese Funktionen zu verwenden, werden sie leichter zu lesen und zu pflegen.

+0

Put '' (* (((long *) & x)) & 0x7fffffffffffffffL) <0x7ff0000000000000L'' in einer '' inline''d Funktion und b Ich bin glücklich. –

+1

@BinkanSalaryman ja, er könnte, aber wenn der OP nicht der einzige ist, der den Code verwaltet, muss er einen * sehr, sehr, sehr * guten Kommentar schreiben ... –

+0

Ja, tatsächlich. Wenn es nicht erforderlich ist, halten Sie sich einfach an die angegebenen .NET-Framework-Methoden. –

1

Ohne unsicheren Kontext und Mischen NaN, + Inf, -Inf Werte:

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

Erläuterung:

A double ist ein 64 Bit-Wert gespeichert als:

  • 1 Bit für das Zeichen
  • 11 Bits für die Exponent
  • 52 Bits für die Mantisse
 
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 

Schließlich kann diese zu vereinfacht werden:

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

In Erweiterungsmethode und unsicher Kontext:

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

Tests:

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

Ergebnis:

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