Unterscheiden sich numerische Programmiersprachen zwischen einer "größten endlichen Zahl" und "Unendlichkeit"?

8

Frage Motivation:

In normalen numerischen Sprachen, die mir bekannt sind (z. B. Matlab, Python numpy, usw.), wenn Sie zum Beispiel das Exponential einer bescheidenen großen Zahl verwenden, ist die Ausgabe unendlich als Ergebnis des numerischen Überlaufs. Wenn dies mit 0 multipliziert wird, erhalten Sie NaN. Separat sind diese Schritte zwar vernünftig, aber sie zeigen einen logischen Fehler bei der Implementierung der Mathematik. Die erste Zahl, die sich aus dem Überlauf ergibt, ist als endlich bekannt, und wir wollen eindeutig, dass das Ergebnis einer Multiplikation mit 0 mit dieser großen endlichen Zahl 0 ist.

Explizit:

%Vor%

Ich stelle mir vor, wir könnten hier einen Begriff des "größten endlichen Wertes" (LFV) einführen, der die folgenden Eigenschaften haben würde:

  • LFV wäre der Standard für numerische Überläufe, die andernfalls auftreten würden bis zur Unendlichkeit
  • LFV & lt; Unendlichkeit
  • jede explizite Nummer & lt; LFV (wenn LEV beispielsweise für "größten expliziten Wert" steht, dann LEV
  • (MATLAB-Detail: realmax & lt; LFV)
  • LFV * 0 = 0

Auf der anderen Seite sollte Unendlichkeit nicht einfach in der für LFV beschriebenen Weise neu definiert werden. Für 0 * infinity = 0 ... ergibt das keinen Sinn, die aktuelle Standardimplementation von unendlich ergibt in dieser Einstellung NaN. Manchmal besteht auch die Notwendigkeit, Zahlen auf unendlich zu initialisieren, und Sie möchten das Ergebnis einer numerischen Operation, selbst wenn LFV streng kleiner als der initialisierte Wert ist (dies ist für einige logische Aussagen sinnvoll). Ich bin mir sicher, dass andere Situationen existieren, wo eine richtige Unendlichkeit notwendig ist - mein Punkt ist einfach, dass Unendlichkeit nicht einfach neu definiert werden sollte, um einige der LFV-Eigenschaften oben zu haben.

Die Frage:

Ich möchte wissen, ob es eine Sprache gibt, die ein solches Schema verwendet und ob es Probleme mit einem solchen Schema gibt. Dieses Problem tritt nicht in der richtigen Mathematik auf, da es diese numerischen Grenzen für die Größe von Zahlen nicht gibt, aber ich denke, dass es ein echtes Problem ist, wenn eine konsistente Mathematik in Programmiersprachen implementiert wird. Im Wesentlichen möchte ich mit LFV eine Abkürzung für das offene Intervall zwischen dem größten expliziten Wert und unendlich LFV = (LEV, Unendlichkeit), aber vielleicht ist diese Intuition falsch.

Update: In den Kommentaren scheinen die Leute ein wenig gegen den Nutzen des Problems, das ich anbringe, zu protestieren. Meine Frage entsteht nicht, weil viele verwandte Probleme auftreten, sondern weil das gleiche Problem häufig in vielen verschiedenen Einstellungen auftritt. Im Gespräch mit Leuten, die Daten analysieren, trägt dies oft zu Laufzeitfehlern beim Training / Anpassen von Modellen bei. Die Frage ist im Grunde, warum dies nicht von numerischen Sprachen gehandhabt wird. Aus den Kommentaren entnehme ich im Wesentlichen, dass die Leute, die die Sprachen schreiben, nicht den Nutzen sehen, die Dinge so zu behandeln. Wenn bestimmte spezifische Probleme häufig genug für Benutzer auftreten, die eine Sprache verwenden, kann es meiner Meinung nach sinnvoll sein, diese Ausnahmen auf prinzipielle Weise zu behandeln, so dass jeder Benutzer dies nicht tun muss.

    
Josh 25.04.2015, 00:11
quelle

1 Antwort

7

Also ... ich wurde neugierig und grub ein bisschen herum.

Wie ich bereits in den Kommentaren erwähnt habe, gibt es in IEEE 754 eine Art "größten endlichen Wert", wenn Sie die betrachten Ausnahmestatusflags Ein Wert von unendlich mit gesetztem Überlaufmerker entspricht Ihrem vorgeschlagenen LFV, mit dem Unterschied, dass das Flag nur nach dem Vorgang ausgelesen werden kann, anstatt als Teil des Wertes selbst gespeichert zu werden. Das bedeutet, dass Sie das Flag manuell überprüfen und bei einem Überlauf handeln müssen, anstatt nur LFV * 0 = 0 eingebaut zu haben.

Es gibt ein ziemlich interessantes Papier zur Ausnahmebehandlung und dessen Unterstützung in Programmiersprachen. Quote:

  

Das IEEE 754-Modell zum Setzen eines Flags und zum Zurückgeben eines unendlichen oder stillen NaN setzt voraus, dass der Benutzer den Status häufig (oder zumindest angemessen) testet. Um das ursprüngliche Problem zu diagnostizieren, muss der Benutzer alle Ergebnisse auf außergewöhnliche Werte überprüfen , die wiederum annimmt, dass sie durch alle Operationen gefiltert werden, so dass fehlerhafte Daten markiert werden können. Unter diesen Annahmen sollte alles funktionieren, aber leider sind sie nicht sehr realistisch.

Das Papier beklagt auch die schlechte Unterstützung für die Verarbeitung von Fließkommaausnahmen, besonders in C99 und Java (ich bin sicher, dass die meisten anderen Sprachen nicht besser sind). Angesichts der Tatsache, dass es keine größeren Anstrengungen gibt, dies zu beheben oder einen besseren Standard zu schaffen, scheint mir zu zeigen, dass IEEE 754 und seine Unterstützung in gewissem Sinne "gut genug" sind (mehr dazu später) / p>

Lassen Sie mich Ihr Beispielproblem lösen, um etwas zu demonstrieren. Ich verwende numpys seterr , damit beim Überlauf eine Ausnahme ausgelöst wird :

%Vor%
  • exp_then_mult_naive macht, was du getan hast: Ausdruck, der überläuft, multipliziert mit 0 und du erhältst nan .
  • exp_then_mult_check_zero fängt den Überlauf ab und gibt 0 zurück, wenn das zweite Argument 0 ist, ansonsten wie die naive Version (beachten Sie, dass inf * 0 == nan während inf * positive_value == inf ). Dies ist das Beste, was Sie tun könnten, wenn eine LFV-Konstante vorhanden wäre.
  • exp_then_mult_scaling verwendet Informationen über das Problem, um Ergebnisse für Eingaben zu erhalten, mit denen die anderen zwei nicht umgehen können: Wenn b klein ist, können wir es mit e multiplizieren, während wir a dekrementieren, ohne das Ergebnis zu ändern. Also, wenn np.exp(a) < np.inf vor b >= 1 , passt das Ergebnis. (Ich weiß, ich könnte prüfen, ob es in einem Schritt passt, anstatt die Schleife zu verwenden, aber das war jetzt einfacher zu schreiben.)

Nun haben Sie also eine Situation, in der eine Lösung, die keine LFV erfordert, für mehr Eingabe-Paare korrekte Ergebnisse liefert als eine, die dies tut. Der einzige Vorteil, den ein LFV hier hat, ist die Verwendung von weniger Codezeilen, während in diesem einen Fall immer noch ein korrektes Ergebnis erzielt wird.

Übrigens bin ich mir nicht sicher über die Thread-Sicherheit mit seterr . Also, wenn Sie es in mehreren Threads mit verschiedenen Einstellungen in jedem Thread verwenden, testen Sie es vorher, um Kopfschmerzen später zu vermeiden.

Bonus factoid: Der Original-Standard hat tatsächlich festgelegt, dass Sie in der Lage sein sollten Registrieren Sie einen Trap-Handler, der beim Überlauf das Ergebnis der Operation geteilt durch eine große Zahl erhalten würde (siehe Abschnitt 7.3). Das würde Ihnen erlauben, die Berechnung fortzusetzen, solange Sie sich bewusst sein, dass der Wert tatsächlich viel größer ist. Obwohl ich denke, es könnte ein Minenfeld von WTF in einer Multithread-Umgebung werden, egal, dass ich nicht wirklich Unterstützung dafür gefunden habe.

Um von oben auf den Punkt "gut genug" zurückzukommen: Nach meinem Verständnis wurde IEEE 754 als universelles Format entworfen, das für praktisch jede Anwendung geeignet ist. Wenn Sie sagen, "das gleiche Problem tritt häufig in vielen verschiedenen Einstellungen auf", ist es (oder war es zumindest) nicht oft genug, um das Aufblasen des Standards zu rechtfertigen.

Lassen Sie mich aus dem Wikipedia-Artikel zitieren:

  

[...] die eher esoterischen Eigenschaften des hier diskutierten IEEE 754-Standards, wie zB erweiterte Formate, NaN, Unendlichkeiten, Subnormale usw. [...] sollen sichere, robuste Standardwerte für numerisch einfache Programmierer schaffen Zusätzlich zur Unterstützung anspruchsvoller numerischer Bibliotheken durch Experten.

Abgesehen davon, dass meiner Meinung nach sogar NaN als besonderen Wert eine etwas zweifelhafte Entscheidung ist, wird die Hinzufügung eines LFV es nicht wirklich einfacher oder sicherer für die "numerisch einfachen" machen, und das tut es auch nicht. t Experten erlauben, alles zu tun, was sie nicht schon konnten.

Ich nehme an, dass die Darstellung von rationalen Zahlen schwierig ist. IEEE 754 macht es ziemlich gut, es für viele Anwendungen einfach zu machen.Wenn deins nicht einer von ihnen ist, müssen Sie am Ende nur mit den harten Sachen umgehen, entweder durch

  • Verwenden Sie eine höhere Präzision Float, wenn verfügbar (ok, das ist ziemlich einfach),
  • Wählen Sie sorgfältig die Reihenfolge der Ausführung, so dass Sie keine Überläufe an erster Stelle bekommen,
  • fügt allen Ihren Werten einen Offset hinzu, wenn Sie wissen, dass sie alle sehr groß sein werden,
  • Verwenden Sie eine Darstellung mit beliebiger Genauigkeit, die nicht überlaufen kann (es sei denn, Sie haben nicht genügend Arbeitsspeicher) oder
  • etwas anderes, an das ich gerade nicht denken kann.
dddsnn 25.04.2015, 18:42
quelle