C - sicherer cast von double nach int?

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Antworten
Benutzeravatar
starcow
Establishment
Beiträge: 522
Registriert: 23.04.2003, 17:42
Echter Name: Mischa Schaub
Kontaktdaten:

C - sicherer cast von double nach int?

Beitrag von starcow »

Wie lässt sich eigentlich wirklich sicher von double nach int casten?

Ich bin grad etwas verunsichert: Zwar steht in dem C-Buch, das ich vor mir liegen habe, dass ein cast von double nach int ohnehin auch implizit vorgenommen werden würde - doch einige Seiten später wird erwähnt, dass bei einem expliziten cast mit einem Wert der grösser als das Fassungsvermögen des neuen Datentyps ist, ein undefiniertes Verhalten auftritt.
Was meint der Autor damit? Dass bei einer Grenzwertüberschreitung des Fassungsvermögens nicht mehr sicher gesagt werden kann, was für ein neuer Wert vorliegt (sonst aber alles glatt läuft)? Oder ist mit UB gemeint, dass nun das ganze Programmverhalten UB ist? Das Programm also auch "crashen" könnte?

Wie darf ich dann explizit von double nach int casten, so dass wirklich alles im grünen Bereich bleibt?

Gruss, starcow
Freelancer 3D- und 2D-Grafik
mischaschaub.com
Matthias Gubisch
Establishment
Beiträge: 470
Registriert: 01.03.2009, 19:09

Re: C - sicherer cast von double nach int?

Beitrag von Matthias Gubisch »

Wenn du es sicher haben willst wirst du selber checken müssen ob der Wert des double in den integer passt.

UB wird sich in diesem Fall vermutlich auf den Inhalt der Variablen auswirken. Das ist dann im Zweifel nicht mal rein Compiler abhängig sondern auch vom overflow Verhalten der CPU.

Heisst Compiler und HW machen was sie grad für passend halten, das kann von Compiler zu Compiler und von CPU zu CPU unterschiedlich sein

Genau deshalb gibt ein impliziter cast von double zu int zumindest auf modernen cpp Compilern auch eine Warnung.
Bevor man den Kopf schüttelt, sollte man sich vergewissern einen zu haben
Benutzeravatar
Krishty
Establishment
Beiträge: 8227
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: C - sicherer cast von double nach int?

Beitrag von Krishty »

Hat hier eigentlich noch nie jemand ein Programm mit Floating-Point Exceptions gewartet? Es gibt eine Overflow-Exception, und falls man das nicht gerade via Signal Handler (Linux) oder __except() (Windows) abfängt, ist der Prozess weg. Mit UBSan ist er das sowieso, und mit Valgrind müsste ich erst ausprobieren.

Undefined Behavior bedeutet durchaus mehr als undefiniert, was in der Variable landet.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Lord Delvin
Establishment
Beiträge: 574
Registriert: 05.07.2003, 11:17

Re: C - sicherer cast von double nach int?

Beitrag von Lord Delvin »

Lol, wusste nicht, dass das erlaubt ist; dachte, dass es in allen modernen Sprachen nur noch geht, wenn die Werte alle repräsentierbar sind. Aber zumindest die gcc-Fehlermeldung ist ganz brauchbar. Ich sehe aber, warum man sich schwer tut, das in einem Sprachupdate einzudämmen. Zumal auch int nach float oder long nach double verboten sein müssten.
XML/JSON/EMF in schnell: OGSS
Keine Lust mehr auf C++? Versuche Tyr: Get & Get started
Alexander Kornrumpf
Moderator
Beiträge: 2106
Registriert: 25.02.2009, 13:37

Re: C - sicherer cast von double nach int?

Beitrag von Alexander Kornrumpf »

Krishty hat geschrieben: 01.09.2022, 21:37 Hat hier eigentlich noch nie jemand ein Programm mit Floating-Point Exceptions gewartet? Es gibt eine Overflow-Exception, und falls man das nicht gerade via Signal Handler (Linux) oder __except() (Windows) abfängt, ist der Prozess weg. Mit UBSan ist er das sowieso, und mit Valgrind müsste ich erst ausprobieren.
Ich wusste das nicht, wie ich zugeben muss. Da du mich mit "noch nie jemand" sicherlich mitgemeint hast, habe ich meinen offenbar falschen Beitrag oben entfernt. Ich glaube trotzdem, dass die Frage "wie kann ich einen double sicher auf einen int casten?" für den allgemeinen Fall falsch gestellt ist. Es gibt halt Werte von double, für die es prinzipbedingt nicht gehen kann, das finde ich die eigentlich wichtige Erkenntnis hier, weil es einen nämlich befähigt zu erkennen, dass man ein Problem haben könnte wenn man eins haben könnte.
Undefined Behavior bedeutet durchaus mehr als undefiniert, was in der Variable landet.
Nach meinem Verständnis bedeutet es, dass der Standard nicht definiert - daher der Name - wie das Programm sich verhalten muss. Da ich kein C oder C++ Programmierer bin, ist das nicht meine Welt. Ist es echt so, dass man C oder C++ quasi nur mit dem Standard in der Hand programmieren kann? Ich denke dass man bei vielen (den meisten?) Sprachen ein gutes Verständnis entwickeln kann, wie sie sich verhalten, auch ohne jemals die Sprachdefinition zu Rate zu ziehen. [Vor allem glaube ich dass das ist wie die meisten Menschen programmieren, selbst wenn es theoretisch besser wäre in die Defintion zu schauen].

Ich weiß nicht was der richtige Rat für starcow ist, aber er scheint mir in möglichst großem Detail lernen zu wollen wie das alles ist, ohne praktisch in der Situation zu sein wo er es braucht. Meiner Erfahrung nach hat diese Art von Theorielernen dann doch irgendwann Grenzen, insbesondere wenn man nicht mehr 25 ist und das Gehirn langsam nachlässt (Menschen unter 25: wartet ab, euch erwischt es auch noch!). Mit dem Buch in der Hand dazusitzen und sich zu wundern ist eine beachtenswerte Geisteshaltung. Viele Menschen kämen vermutlich nichtmal auf die Idee sich diese Fragen zu stellen. Aber eines ist es eben gerade nicht: "ein Programm mit Floating-Point Exceptions gewartet [haben]".

Es kommt natürlich auch darauf an, in welcher Domäne man unterwegs ist. Die Anzahl an Situationen in meinem Leben in denen ich ein double nach int casten wollte ist vielleicht noch einstellig und die Anzahl an Situationen in denen das damit zusammen fiel, dass int nicht reichte (int ist nicht sooo klein für die meisten Domänen) ist vielleicht sogar nullstellig. Ich erinnere mich an einen einzigen Overflow, der ganz grob in das Muster passt, aber ich bin nichtmal sicher ob es nicht vielleicht doch eher zwischen int und long war. Das war aber jedenfalls in geerbtem Code, also dass ich selbst double nach int casten wollte und einen overflow hatte, ist tatsächlich noch nicht vorgekommen.
Benutzeravatar
Krishty
Establishment
Beiträge: 8227
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: C - sicherer cast von double nach int?

Beitrag von Krishty »

Alexander Kornrumpf hat geschrieben: 01.09.2022, 23:09Ich glaube trotzdem, dass die Frage "wie kann ich einen double sicher auf einen int casten?" für den allgemeinen Fall falsch gestellt ist. Es gibt halt Werte von double, für die es prinzipbedingt nicht gehen kann, das finde ich die eigentlich wichtige Erkenntnis hier, weil es einen nämlich befähigt zu erkennen, dass man ein Problem haben könnte wenn man eins haben könnte.
Absolut. Wollte nur mal anmerken, dass ich tatsächlich ein Mal in meiner Karriere mit einem so außergewöhnlichen System zu tun hatte und die Auswirkungen von UB dort halt wirklich nicht nur theoretischer Natur waren.

Übrigens sind hier viele Leute anderer Meinung als wir beide – du wirst oft hören (und viele Implementierungen dazu sehen), dass es ja „natürliches“ Verhalten wäre, den besonderen Wert XYZ entstehen zu lassen. Nur ob das dann 0, -1, INT_MAX, INT_MIN, oder std::domain_error ist, darin sind sich diese Leute dann komischerweise nicht mehr einig ;)
Ist es echt so, dass man C oder C++ quasi nur mit dem Standard in der Hand programmieren kann?
Nein, überhaupt nicht. Das Programm kann auch mit UB gut laufen; es ist dann nur nicht zukunftssicher. Die desaströse Situation von C/C++ im Bezug auf Fragen wie „könnten diese beiden Objekte überlappen“ macht automatisierte High-Level-Optimierungen fast unmöglich (siehe, dass Polly sich seit 15 Jahren kaum bewegt). Die Compiler-Entwickler sind in Mangel dessen auf Low-Level-Optimierungen ausgewichen wie „bei diesem Cast kann keine Zahl größer 2³² rauskommen, also kann auch keine größer 2³² reingehen, also lass mal gucken, was wir unter dieser Annahme 20 Zeilen weiter oben löschen können!“

Wenn ich alte Spiele kompiliere (siehe andere Threads), kracht es öfter mal an Stellen, die definitiv schon immer UB waren, aber seit irgendwann um 2010 von Compilern drastisch anders (aggressiver!) interpretiert werden. Ich befürchte ehrlich gesagt, dass das in den nächsten zehn Jahren nicht besser wird.

tl;dr Das Programm kann auch mit UB gut laufen; es ist dann nur nicht zukunftssicher oder portabel. Es ist ein Bisschen wie, ein Kochrezept in der eigenen Handschrift hinzuklatschen. Schwer zu sagen, ob dein Schwager nächstes Silvester 3 Teelöffel liest oder 8.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Lord Delvin
Establishment
Beiträge: 574
Registriert: 05.07.2003, 11:17

Re: C - sicherer cast von double nach int?

Beitrag von Lord Delvin »

Krishty hat geschrieben: 02.09.2022, 00:04 Wenn ich alte Spiele kompiliere (siehe andere Threads), kracht es öfter mal an Stellen, die definitiv schon immer UB waren, aber seit irgendwann um 2010 von Compilern drastisch anders (aggressiver!) interpretiert werden.
Das ist halt schon so der Punkt, zusammen mit für C und C++ gibt es halt echte und teure Optimierungen, bei denen Leute halt auch einfach aggressiv ausnutzen, dass sie jetzt bei definiert undefiniertem Verhalten einfach sagen können, da wird jetzt der passende Wert rauskommen, z.B. immer 4, egal was da reinging. Teilweise gibt's dann den Druck der Straße, der dazu führt, dass man da Sachen zurückbaut oder das macht, was man intuitiv erstmal erwarten würde. Aber punktuell bedeutet es genau das, was Krishty sagt.
Alexander Kornrumpf hat geschrieben: 01.09.2022, 23:09 Es kommt natürlich auch darauf an, in welcher Domäne man unterwegs ist. Die Anzahl an Situationen in meinem Leben in denen ich ein double nach int casten wollte ist vielleicht noch einstellig und die Anzahl an Situationen in denen das damit zusammen fiel, dass int nicht reichte (int ist nicht sooo klein für die meisten Domänen) ist vielleicht sogar nullstellig.
Privat mache ich sowas auch nicht aber auf der Arbeit bestimmt im Schnitt mindestens einmal die Woche. Ist Java und ich könnte dir auswendig auch nicht sagen, was da passiert, wenn man +Inf in einen int konvertiert. Wenn man das macht kommt man in der Regel damit durch. Ich hatte das in meiner Karriere nur mal in Ada mit NaN.

Weil's mich jetzt doch interessiert hat, mal kurz geschaut und das gefunden:
https://stackoverflow.com/questions/587 ... on-in-java

Im Prinzip wie Krishty schon gesagt hat: Ich würde mich der konkreten Entscheidung nicht anschließen können. 0 würde bei dem Code den ich kenne ziemlich Probleme machen.

Vielleicht könnte die Moral der Diskussion sein, dass man vor einem Cast das macht, was in Java Double.isFinite() prüft und in den Fällen manuell reagiert. Ist nie dasselbe richtig.
XML/JSON/EMF in schnell: OGSS
Keine Lust mehr auf C++? Versuche Tyr: Get & Get started
Benutzeravatar
starcow
Establishment
Beiträge: 522
Registriert: 23.04.2003, 17:42
Echter Name: Mischa Schaub
Kontaktdaten:

Re: C - sicherer cast von double nach int?

Beitrag von starcow »

@Alexander Kornrumpf
Deine Beobachtung ist sicher nicht ganz falsch. :-)
Ich gehöre wohl zur Sorte von Leuten, die sich solche Fragen auch ohne konkrete Realsituation stellen.
Hier trifft es jedoch nicht zu, denn ich hatte in meinem letzten Code tatsächlich genau eine solche Situation.

Mein Standard-Datentyp für Fliesskomma war double (aus Genauigkeitsgründen) - und mein Standard-Datentyp für Ganzzahl int.

Es gab dann eine Stelle im Code, wo ich die Position eines Objektes (double) in einen Index für ein Grid (ein Array fürs Spatial Hashing) umrechnen musste.
Da habe ich dann erst gerundet und dann gecastet. Soweit ich mich erinnere habe ich den double zuvor mittels clamp zurecht gestutzt.
Wobei ich meine ausser Acht gelassen zu haben, dass der Wert - zumindest theoretisch - auch hätte NaN sein können.

Wenn ich eure Antworten jetzt richtig verstanden habe, müsste ich also vor dem expliziten cast einen Test auf NaN machen und dann in einem zweiten Schritt sicherstellen, das die Grenzbereiche nicht übertreten werden (min, max oder unter C++ clamp). Inf und -Inf müssten ja durch die Funktionen min, max richtig "gestutzt" werden. Das natürlich alles, bevor ich explizit caste.
Freelancer 3D- und 2D-Grafik
mischaschaub.com
Benutzeravatar
Krishty
Establishment
Beiträge: 8227
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: C - sicherer cast von double nach int?

Beitrag von Krishty »

starcow hat geschrieben: 02.09.2022, 19:08Wenn ich eure Antworten jetzt richtig verstanden habe, müsste ich also vor dem expliziten cast einen Test auf NaN machen und dann in einem zweiten Schritt sicherstellen, das die Grenzbereiche nicht übertreten werden (min, max oder unter C++ clamp). Inf und -Inf müssten ja durch die Funktionen min, max richtig "gestutzt" werden. Das natürlich alles, bevor ich explizit caste.
Du kannst dir dabei zueigen machen, dass IEEE 754 (der Standard, an den sich float und double halten) vorschreibt, dass Vergleiche mit NaN immer false liefern müssen.

Wenn du also schreibst

  bool canCastToInt(double d) {
    return INT_MIN <= d && d <= INT_MAX; // d im Bereich [INT_MIN, INT_MAX]?
  }


… dann würde NaN schon am ersten Vergleich false liefern und die Funktion wäre NaN-fest. Die andere Schreibweise

  return !(d < INT_MIN || d > INT_MAX); // d nicht zu klein oder zu groß?

… würde hingegen mit NaN bei beiden Vergleichen false liefern, das Ganze invertieren, und insgesamt true zurückgeben – dich also nicht vor NaN schützen. Da bräuchtest du – wie von dir beschrieben – erst noch einen zusätzlichen Vergleich.

Die Lehre ist, dass man bei Gleitkommazahlen höllisch mit den Vergleichen aufpassen muss. Ein scheinbar überflüssiges ! in einem Ausdruck könnte entweder Dummheit des Entwicklers sein, oder sehr gut durchdachte Logik, um NaN korrekt zu behandeln. Und dass sowas wie if(d != d) kein Tippfehler ist, sondern eine Prüfung auf NaN.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Lord Delvin
Establishment
Beiträge: 574
Registriert: 05.07.2003, 11:17

Re: C - sicherer cast von double nach int?

Beitrag von Lord Delvin »

Und NaN != NaN, selbst wenn die bitmuster gleich sind.
XML/JSON/EMF in schnell: OGSS
Keine Lust mehr auf C++? Versuche Tyr: Get & Get started
Benutzeravatar
Schrompf
Moderator
Beiträge: 4831
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: C - sicherer cast von double nach int?

Beitrag von Schrompf »

Meiner Meinung nach aber müsste das höchstens ein assert sein. Niemand, ich wiederhole, NIEMAND will jemals mit INF oder NAN korrekt weiterrechnen. Das Auftauchen solcher Werte ist einfach ein Bug, und man muss die Ursache für dieses Auftauchen finden, anstatt NAN-sichere Int-Konvertierung zu schreiben.

Unabhängig davon gibt's in irgendnem neueren C++-Standard jetzt std::is_nan() und Freunde. Unbedingt die nehmen, der GCC dreht Dir sonst garantiert irgendwo nen Strick aus solchen "offensichtlich falschen" Bedingungen.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
Krishty
Establishment
Beiträge: 8227
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: C - sicherer cast von double nach int?

Beitrag von Krishty »

Schrompf hat geschrieben: 03.09.2022, 13:37GCC dreht Dir sonst garantiert irgendwo nen Strick aus solchen "offensichtlich falschen" Bedingungen.
Garantiert nicht. Es sei denn, du kompilierst mit dem äußerst zweifelhaften ffast-math.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Schrompf
Moderator
Beiträge: 4831
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: C - sicherer cast von double nach int?

Beitrag von Schrompf »

Stimmt, das war mit dem signed int und dessen Überlauf. Oder dessen Bitshift. Aber ich würde mich trotzdem nicht drauf verlassen, so genau kenn ich den C++-Standard nun auch nicht.

Und ja, ich kompiliere immer mit fast-math :-) Brachte 20%-30%, als ich das das letzte Mal verglichen habe. Was zugegebenermaßen schon >10 Jahre her ist
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
Lord Delvin
Establishment
Beiträge: 574
Registriert: 05.07.2003, 11:17

Re: C - sicherer cast von double nach int?

Beitrag von Lord Delvin »

Schrompf hat geschrieben: 03.09.2022, 13:37 Meiner Meinung nach aber müsste das höchstens ein assert sein. Niemand, ich wiederhole, NIEMAND will jemals mit INF oder NAN korrekt weiterrechnen. Das Auftauchen solcher Werte ist einfach ein Bug, und man muss die Ursache für dieses Auftauchen finden, anstatt NAN-sichere Int-Konvertierung zu schreiben.
Oder du nimmst das NaN, weil du z.B. keinen Wert an der Stelle hast. Floats werden auch noch für andere Sachen als Vektoren und Matrizen verwendet :)
XML/JSON/EMF in schnell: OGSS
Keine Lust mehr auf C++? Versuche Tyr: Get & Get started
Benutzeravatar
starcow
Establishment
Beiträge: 522
Registriert: 23.04.2003, 17:42
Echter Name: Mischa Schaub
Kontaktdaten:

Re: C - sicherer cast von double nach int?

Beitrag von starcow »

Ok, das leuchtet absolut ein. An der entsprechenden Stelle in meinem Programm wäre +inf, -inf oder NaN tatsächlich ein Zustand, der nicht auftreten dürfte.
Was wäre hier ein sauberer C-Code? (also C, nicht C++).

Code: Alles auswählen

assert(isfinite(d)); 
Bei assert würde die Prüfung bei einem Release-Build ja raus fliegen...
Vielleicht mit

Code: Alles auswählen

if(!isfinite(d)){exit();} 
kontrolliert beenden?

Und danach vor dem cast mit min, max zurecht stutzen?

Code: Alles auswählen

 x = (int)min(max(d, (double)INT_MIN), (double)INT_MAX);
Edit:
Ich habe noch einen cast von INT_MIN und INT_MAX nach double vor dem aufrufen der Funktion eingefügt...
Zuletzt geändert von starcow am 04.09.2022, 19:39, insgesamt 4-mal geändert.
Freelancer 3D- und 2D-Grafik
mischaschaub.com
udok
Beiträge: 40
Registriert: 01.02.2022, 17:34

Re: C - sicherer cast von double nach int?

Beitrag von udok »

Wenn NAN oder INF ein Problem ist, dann musst du halt eine Abfrage einbauen...

Wenn ein Cast zu Integer überläuft, dann wird auf der x86 CPU
der Integer auf 0x8000000, also die größte negative Zahl gesetzt.
Sonst passiert da nichts, egal ob der cast explizit oder implizit ist.
Floating Point exceptions sind per default ausgeschaltet.

Die Floating Point Zahl wird übrigens nicht gerundet, sondern abgeschnitten.
Es gibt die Funktionen floor, ceil, rint, lrint. Die letzen zwei runden richtig,
oder du addiert 0.5 für positive floating point Zahlen vor dem cast.

Alternativ kannst du die FPU Exceptions anmachen, die normalerweise aus sind.
Dann gibt es eine Exception, wenn NAN auftritt. Das geht auch für Underflow, Overflow, Inexact, Denormalized oder Invalid result.
Du kannst aber auch die Sticky Flags in der FPU abfragen, die werden gesetzt wenn einer dieser Fälle auftritt.

Du kannst auch das Verhalten bei sehr kleinen Zahlen (Denormalized) steuern.
Wenn du Denormalized Zahlen auf 0 abschneidest, dann ist bei gerade älteren CPUs die Geschwindigkeit um ein vielfaches höher.
Bei FIR Filtern mit langen ausklingenden Signalen macht das einen deutlichen Unterschied.

Ich glaube aber, das der übliche Weg der ist, solche Spezialfälle schlichtweg zu ignorieren...
Gerade in Spielesoftware ist der numerische Bereich ja sehr beschränkt.
In den wenigen Fällen, wo numerische Probleme auftreten, muss man halt schauen, wie man den Algorithmus stabiler hinbekommt.

Die SSE FPU Flags kannst du mit den intrinsic Funktionen _mm_getscr und _mm_setssr setzen. Alternativ gibt es auch offizielle Funktionen
in der C-Runtime. Anbei ein Beispielcode. Doku findest du in der offiziellen "Intel 64 and IA-32 Architektures Software Developer's Manual".

Gruss,
Udo

Code: Alles auswählen


/*********************************************************************************
 * Functions for setting and reading the SSE control and status register (MXCSR)
 *********************************************************************************/
/*
 * Interrupt Status Bits:
 * Bit 0-5 of the MXCSR register indicate whether a SIMD floating-point exception
 * has been detected.  They are sticky flags.
 * That is, after a flag is set, it remains set until explicitly cleared.
 */
#define MXCSR_IS_MASK       0x003f
#define MXCSR_IS_INVALID    0x0001
#define MXCSR_IS_DENORM     0x0002
#define MXCSR_IS_DIVZERO    0x0004
#define MXCSR_IS_OVERFLOW   0x0008
#define MXCSR_IS_UNDERFLOW  0x0010
#define MXCSR_IS_INEXACT    0x0020

/* Set denormal source numbers to zero */
#define MXCSR_DENORMAL_ZERO 0x0040

/*
 * Interrupt Enable Bits:
 * Bit 7-12 provide individual mask bits for SIMD floating-point exceptions.
 * An exception is masked if the corresponding mask bit is set,
 * and it is unmasked if the bit is clear.
 */
#define MXCSR_IE_MASK       0x1f80
#define MXCSR_IE_INVALID    0x0080
#define MXCSR_IE_DENORM     0x0100
#define MXCSR_IE_DIVZERO    0x0200
#define MXCSR_IE_OVERFLOW   0x0400
#define MXCSR_IE_UNDERFLOW  0x0800
#define MXCSR_IE_INEXACT    0x1000

/* Rounding Control */
#define MXCSR_RC_MASK       0x6000
#define MXCSR_RC_NEAR       0x0000
#define MXCSR_RC_DOWN       0x2000
#define MXCSR_RC_UP         0x4000
#define MXCSR_RC_CHOP       0x6000

/* Flush denormal numbers to zero */
#define MXCSR_FLUSH_ZERO    0x8000

#define MXCSR_DEFAULT_FLAGS 0x1f80      /* Flags compatible to IEEE rules */
#define MXCSR_FAST_FLAGS    0x9fc0      /* Flush denormalized values to zero */

/* Return the SSE control and status register. */
INLINE UINT get_mxcsr(VOID) { return _mm_getcsr(); }

/* Set the SSE control and status register (MXCSR).
 * Note: Bits 31 to 16 must be zero.
 * Note: The SSE control and status register must be set for each thread. */
INLINE VOID set_mxcsr(UINT new_mxcsr) { _mm_setcsr(new_mxcsr); }

/* Set the SSE control and status word to reset default value. */
INLINE VOID set_mxcsr_default(VOID) { _mm_setcsr(MXCSR_DEFAULT_FLAGS); }

/* Set the SSE control and status word to fast default value.
 * Note: Denormalized floats are flushed to zero. */
INLINE VOID set_mxcsr_fast(VOID)    { _mm_setcsr(MXCSR_FAST_FLAGS); }
Benutzeravatar
starcow
Establishment
Beiträge: 522
Registriert: 23.04.2003, 17:42
Echter Name: Mischa Schaub
Kontaktdaten:

Re: C - sicherer cast von double nach int?

Beitrag von starcow »

udok hat geschrieben: 04.09.2022, 18:52 Du kannst auch das Verhalten bei sehr kleinen Zahlen (Denormalized) steuern.
Wenn du Denormalized Zahlen auf 0 abschneidest, dann ist bei gerade älteren CPUs die Geschwindigkeit um ein vielfaches höher.
Bei FIR Filtern mit langen ausklingenden Signalen macht das einen deutlichen Unterschied.

Code: Alles auswählen

#define MXCSR_IS_DENORM     0x0002
Sehr interessant, dass du das erwähnst - Danke. Ich bin kürzlich auf Wikipedia im Artikel über IEEE 754 auf folgenden Absatz gestossen - und ich glaube, das ist genau dieser Punkt.
Wikipedia hat geschrieben: Prozessorseitig sind denormalisierte Zahlen aufgrund ihres proportional seltenen Auftretens mit wenig Priorität implementiert und führen deswegen zu einer deutlichen Verlangsamung der Ausführung, sobald sie als Operand oder als Ergebnis einer Berechnung auftauchen. Um Abhilfe (z. B. für Computerspiele) zu schaffen, bietet Intel seit SSE2 die nicht IEEE-754-konforme Funktionalität an, denormalisierte Zahlen vollständig zu deaktivieren (MXCSR-Optionen „flush to zero“ und „denormals are zero“). Gleitkommazahlen, die in diesen Bereich gelangen, werden auf 0 gerundet.
Ich prüfe in meinem Programm jetzt einfach über isfinite(), ob die Zahl nicht +-INF oder NaN ist und dann stutze ich mittels INT_MIN, INT_MAX den Bereich, ehe ich nach int caste.
Freelancer 3D- und 2D-Grafik
mischaschaub.com
Benutzeravatar
Krishty
Establishment
Beiträge: 8227
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: C - sicherer cast von double nach int?

Beitrag von Krishty »

Denk bei Flush-to-Zero, dass es eine globale Änderung ist – also stell den originalen Modus wieder her, bevor du fremden Code aufrufst. (D3D8 hatte AFAIK Flush-to-Zero aktiviert, und viele Entwickler haben sich „gefreut“, dass ihr Gleitkomma-Code in der Engine plötzlich andere Ergebnisse liefert als in den Unit Tests / der Mesh-Import in der Engine andere Koordinaten als im Modeling-Tool / usw.)
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
starcow
Establishment
Beiträge: 522
Registriert: 23.04.2003, 17:42
Echter Name: Mischa Schaub
Kontaktdaten:

Re: C - sicherer cast von double nach int?

Beitrag von starcow »

Uff, guter Hinweis! Danke! :-)
Freelancer 3D- und 2D-Grafik
mischaschaub.com
Benutzeravatar
starcow
Establishment
Beiträge: 522
Registriert: 23.04.2003, 17:42
Echter Name: Mischa Schaub
Kontaktdaten:

Re: C - sicherer cast von double nach int?

Beitrag von starcow »

Wiedermal passend zum Thema (Ein Artikel von Golem) :-)
Tausende Python-Pakete könnten falsche Berechnungen liefern
...
...
Ursache für den Fehler ist demnach, dass eine dynamische Bibliothek mit der Compiler-Option -ffast-math erstellt und von dem genutzten Python-Programm geladen wird. Die Compiler-Option führt standardmäßig dazu, dass die CPU-Flags für FTZ (Flush to Zero) und DAZ (Denormals are zero, Nichtnormalisierte als Null) gesetzt werden, und zwar insbesondere auch dann, wenn die Bibliothek dynamisch geladen wird. Dadurch ändert sich aber das Verhalten des gesamten Prozesses, nicht nur der Bibliothek.
https://www.golem.de/news/fpu-tausende- ... 68134.html
Freelancer 3D- und 2D-Grafik
mischaschaub.com
Benutzeravatar
Krishty
Establishment
Beiträge: 8227
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: C - sicherer cast von double nach int?

Beitrag von Krishty »

starcow hat geschrieben: 08.09.2022, 13:30Wiedermal passend zum Thema (Ein Artikel von Golem) :-)
https://www.golem.de/news/fpu-tausende- ... 68134.html
Ja, der passt hevorragend. Ich konnte ihn wegen Paywall nicht lesen, aber das Thema ging ja auch durch zahlreiche andere Portale.

Ich wurde mehrfach gefragt, was denn nun die schlimme Auswirkung sein soll. Schließlich will man Denorms ja sowieso nicht haben, und langsam sind sie auch. Betroffen sind Algorithmen, die darauf beruhen, dass eine Gleitkommaberechnung gegen ein bestimmtes Ergebnis konvergiert. Mit dem stark betroffenen Bereich der Numerik habe ich gerade zu wenig Berührungspunkte, aber ein allgemeines Beispiel sind Konvertierungen zwischen Floats und Strings, wie etwa bei printf("%f", some_double).

Darüber wurden ganze Aufsätze geschrieben (der letzte bahnbrechende war Ryū). Für das Problem braucht man theoretisch Arithmetik mit viel mehr Bits als double bietet. Nun kann man dem aber entgehen, indem man das Ergebnis nur annähert, die Abweichung berechnet, und entsprechend korrigiert. Dann schießt man vielleicht über das Ziel hinaus, berechnet aber wieder die Abweichung und … irgendwann hat man’s.

Es sei denn, Flush-to-Zero ist aktiv. Dann wird die Abweichung viel zu schnell Null und man bleibt mit dem falschen Ergebnis stehen.

Viele Allzweckimplementierungen sind dagegen gerüstet, allerdings zum Preis erhöhter Komplexität. Im weit verbreiteten dtoa.c wird Flush-to-Zero bspw. überall behandelt, wo das Makro Sudden_Underflow auftaucht.

Edit: Huch, da habe ich mir ja ein schönes Ei gelegt. dtoa.c ist mit FTZ sogar einfacher als zuvor, weil einige Denorm-Prüfungen wegfallen. Aber ich würde darin trotzdem keinen Fehler suchen wollen, der durch die bloße Existenz fremden Codes in meinem Prozess ausgelöst wird.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Antworten