Auf NaN überprüfen

Kennt jemand eine gute Möglichkeit um nach Aufkommen von NaN dynamisch zur Laufzeit zu suchen?

Entwickle an einer Physics Engine und bei einem reproduzierbarem Verhalten entsteht scheinbar irgendwo im Code als Ergebnis “NaN” was sich dann wie ein Tumor innerhalb von Nanosekunden ausbreitet und ganze Programmteile vollständig kompromittiert und faktisch lahmlegt.

Ich weiß aber nicht von wo aus das ganze das erste mal auftritt.

Jede Menge Debuganweisungen einstreuen, würde ich sagen. Außerdem gibt es nur wenige geläufige Operationen, die überhaupt NaN liefern können: Division durch Null, Wurzel ziehen…

Float.isNaN?

edit: Ahso, jetzt versteh ich die Frage :wink:

Also zuerstmal mit isNaN an einer Stelle wo du weisst, dass es irgendwann true liefert, einen Breakpoint setzen - dann kann den Callstack im Debugger schrittweise zurueckverfolgen und siehst auch die aktuellen Werte in anderen Variablen. Vermutlich tritt es bei einer Division durch 0 auf, wie schon erwaehnt. Z.B. bei nem Cast zu einem ganzzahligen Typ.

[ot]
Den Effekt kenne ich. Im Rahmen meiner früheren Arbeit habe ich mlal einem (physikalisch basierten) Kleidungssimulationssystem gearbeitet. Wenn dort an einem Punkt des Dreiecksnetzes (z.B. durch eine kaputte Kollisionserkennung) ein „NaN“ auftrat, hat sich das „viral“ durch die ganze Kleidung ausgebreitet, da sich ein NaN jeweils in einem Simulationsschritt auf die Nachbarpunkte übertragen hat. Zwischen einem normalen Punkt und einem kaputten Punkt hatte das gerenderte Kleidungsstück dann einen Übergang von der normalen Textur, über „Schwarz“ zu „nicht mehr gerendert“. Wenn so ein Fall auftrat, äußerte sich das also darin, dass das Kleidungsstück in einer hübschen Animation von einem Punkt ausgehend „verbrannte“, und das Modell am Ende nackt dastand :smiley:
[/ot]
Ja, „„Abhilfe““: Per compile-time-flag zuschaltbare
if (DEBUG && containsNaN(someVector)) throw new SomethingWrongException("Kreisch!");
checks :rolleyes:

eine allgemein gute Sache, hier auch vollständige Lösung, wäre, nirgendwo im Code double/double und ähnlich ‘primitives’ zu rechnen,
alle Rechnungen durch Methoden a la divide(double,double) zu ersetzen, dann nur wenige Stellen die auf NaN zu testen,

nächster/ weiterer denkbarer Schritt wäre Verzicht auf double-Datentyp an sich, auch anderen Standard wie BigDecimal,
und eigenen Datentyp zu verwenden mit Rechenmethoden, so dass gar nicht richtig möglich, irgendwo mal unkontrolliert zu rechnen,

dabei zu entscheiden ob wie BigDecimal immutable oder nicht…
vektor.wert1 = vektor.wert1.mult(faktor.wert1); // Setzen an richtige Stelle zurück fehleranfällig
oder nur vektor.wert1.mult(faktor.wert1); // ändert vektor.wert1

Vorteil: Rechnung nur in wenigen Methoden, umfangreiche Prüf- und Log-Möglichkeiten, evtl. Verlauf eines Wertes mit der Zeit merken,
Notizen eintragen zur Erstellung, Verrechnung wann mit welchen fachlichen Werten warum usw. (ein- und ausschaltbar)

Nachteil: Aufwand kann groß werden…, Inkompatibilät mit anderen Programmen/-teilen…,
wenn schon Log-Fan, dann vielleicht auch an anderen Stellen wie einem Vektor gewünscht, Überschneidung,
langfristrig lieber doch nicht wünschenswert wegen Performance, ob dann wieder einfach zurück oder Fehlergefahr?

Das stimmt, aber in Physics Engines gibt es davon wiederum eine Menge. :smiley:

Möglich ja. Hatte natürlich auf irgendeinen „Hack“ gehofft, ist NaN doch sowas wie ein null-Pointer für Zahlen.

[quote=Marco13;138360]Ja, „„Abhilfe““: Per compile-time-flag zuschaltbare
if (DEBUG && containsNaN(someVector)) throw new SomethingWrongException(„Kreisch!“);
checks[/quote]
davon hab ich mittlerweile genug :smiley:
if (BuildConfig.DEBUG) → Found 274 usages

[quote=SlaterB;138382]eine allgemein gute Sache, hier auch vollständige Lösung, wäre, nirgendwo im Code double/double und ähnlich ‚primitives‘ zu rechnen,
alle Rechnungen durch Methoden a la divide(double,double) zu ersetzen, dann nur wenige Stellen die auf NaN zu testen,[/quote]

[quote=SlaterB;138382]Nachteil: Aufwand kann groß werden…, Inkompatibilät mit anderen Programmen/-teilen…,
wenn schon Log-Fan, dann vielleicht auch an anderen Stellen wie einem Vektor gewünscht, Überschneidung,
langfristrig lieber doch nicht wünschenswert wegen Performance, ob dann wieder einfach zurück oder Fehlergefahr?[/quote]
Das Problem ist hier glaube ich wirklich der Aufwand. Erstmal müssen alle Divisionen ersetzt werden, das mach mal einfach so denn „/“ taucht in anderem Kontext weiter öfters auf.
Eine andere Math-class zu injecten ist aber vielleicht keine schlechte Möglichkeit.

Den Hinweis auf BigDecimal sehe ich eher skeptisch (zurückhaltend formuliert :rolleyes: ). Sowas wie

HighPerformanceComputationsVector3D {
    double x,y,z;
}

durch

HighPerformanceComputationsVector3D {
    BigDecimal x,y,z;
}

zu ersetzen ist dann in mehrerer Hinisicht nicht das Beste…