Die Standard-C-Math-Bibliothek bietet keine Funktion zur Berechnung der CDF der Standardnormalverteilung, normcdf()
. Es bietet jedoch eng verwandte Funktionen: die Fehlerfunktion erf()
und die komplementäre Fehlerfunktion erfc()
. Der schnellste Weg, um die CDF zu berechnen ist häufig über die Fehlerfunktion, die unter Verwendung von vordefinierten Konstante M_SQRT1_2 darzustellen √½:Präzise Berechnung von CDF der Standardnormalverteilung unter Verwendung der C-Standardmath-Bibliothek
double normcdf (double a)
{
return 0.5 + 0.5 * erf (M_SQRT1_2 * a);
}
Offensichtlich Diese leiden unter massiver subtraktiven Löschung in der negativen Halbebene und sind nicht geeignet für die Mehrheit der Anwendungen. Da die Stornierung Problem leicht durch die Verwendung von erfc()
, die jedoch in der Regel von etwas Leistung vermieden wird niedriger als erf()
, die am häufigsten empfohlene Berechnung ist:
double normcdf (double a)
{
return 0.5 * erfc (-M_SQRT1_2 * a);
}
Einige Tests zeigen, dass die maximale ULP Fehler in der negativen Halb entstanden Flugzeug ist immer noch ziemlich groß. Unter Verwendung einer Doppelpräzisionsimplementierung von erfc()
, die auf 0,51 ul genau ist, kann man Fehler von bis zu 1705,44 uls in normcdf()
beobachten. Das Problem hierbei ist, dass der Berechnungsfehler in der Eingabe zu erfc()
durch die exponentielle Skalierung, die erfc()
inhärent ist, vergrößert wird (Siehe diese answer für eine Erklärung der Fehlervergrößerung durch Potenzierung).
Der folgende Beitrag zeigt, wie eine (fast) richtig gerundet, Produkte erreichen können, wenn sie mit beliebiger Genauigkeit Konstanten wie √½ Gleitkommaoperanden Multiplikation:
Nicolas Brisebarre und Jean-Michel Muller, „Korrekt gerundet Multiplikation mit beliebigen Präzisionskonstanten ", IEEE Transactions on Computers, Vol. Die Methode, auf die sich das Papier stützt, beruht auf der fusionierten Multiply-Add-Operation, die bei neueren Implementierungen aller gängigen Prozessorarchitekturen verfügbar ist und in C exponiert ist über die Standard-Mathematikfunktion fma()
. Dies führt zu der folgenden Version:
double normcdf (double a)
{
double SQRT_HALF_HI = 0x1.6a09e667f3bcd0p-01; // 7.0710678118654757e-01
double SQRT_HALF_LO = -0x1.bdd3413b264560p-55; // -4.8336466567264567e-17
return 0.5 * erfc (fma (-SQRT_HALF_HI, a, -SQRT_HALF_LO * a));
}
Tests zeigen, dass dies den maximalen Fehler im Vergleich zur vorherigen Version ungefähr halbiert. Unter Verwendung der gleichen hochpräzisen erfc()
Implementierung wie zuvor ist der maximale beobachtete Fehler 842,71 uls. Dies ist immer noch weit entfernt von dem üblichen Ziel, grundlegende mathematische Funktionen mit einem Fehler von höchstens einigen ul zu versehen.
Gibt es eine effiziente Methode, die die genaue Berechnung von normcdf()
ermöglicht, und die nur Funktionen verwendet, die in der C-Standardbibliothek verfügbar sind?
Sind Sie auf einem System, wo 'long double' mehr Präzision als' double' bietet? Wenn ja, hilft 'erfl()' und 'erfcl()'? –
@JonathanLeffler Meine aktuell verwendeten Plattformen unterstützen entweder 'long double' oder' 'long double' '' double'' nicht. Andernfalls, mit 'long double' auf 80-bit extended precision, double-double oder quadruple precision abgebildet, würde ich erwarten, dass die einfache Formel, die auf' erfcl' basiert, Ergebnisse mit doppelter Genauigkeit liefern würde, die ich aber nicht habe eine Möglichkeit, das jetzt zu demonstrieren. Selbst wenn angenommen wird, dass die Berechnung über "erfl()" auf die volle IEEE-754-Vierfachgenauigkeit abbildet, führt andererseits eine massive Aufhebung zu ungenauen Ergebnissen in der negativen Halbebene. – njuffa