Warum kann der Compiler die Gleitkommaaddition mit 0 nicht optimieren? [duplicate]

Lesezeit: 5 Minuten

Ich habe vier Identitätsfunktionen, die im Wesentlichen nichts tun. Nur Multiplikation mit 1 könnte von clang zu einem einzigen optimiert werden ret Aussage.

float id0(float x) {
    return x + 1 - 1;
}

float id1(float x) {
    return x + 0;
}

float id2(float x) {
    return x * 2 / 2;
}

float id3(float x) {
    return x * 1;
}

Und die folgende Compilerausgabe ist: (clang 10, at -O3)

.LCPI0_0:
        .long   1065353216              # float 1
.LCPI0_1:
        .long   3212836864              # float -1
id0(float):                                # @id0(float)
        addss   xmm0, dword ptr [rip + .LCPI0_0]
        addss   xmm0, dword ptr [rip + .LCPI0_1]
        ret
id1(float):                                # @id1(float)
        xorps   xmm1, xmm1
        addss   xmm0, xmm1
        ret
.LCPI2_0:
        .long   1056964608              # float 0.5
id2(float):                                # @id2(float)
        addss   xmm0, xmm0
        mulss   xmm0, dword ptr [rip + .LCPI2_0]
        ret
id3(float):                                # @id3(float)
        ret

Ich kann verstehen, warum id0 und id2 kann nicht optimiert werden. Sie erhöhen den Wert, der sich dann in positive Unendlichkeit verwandeln könnte und die zweite Operation würde ihn nicht zurückverwandeln.

Aber warum nicht id1 optimiert werden? Eine Addition mit Unendlich würde Unendlich ergeben, eine Addition mit einer beliebigen regulären Zahl würde diese Zahl ergeben und eine Addition mit NaN ergäbe NaN. Warum ist es also keine “echte” Identitätsoperation wie * 1.

Beispiel mit Compiler Explorer

  • Das ist eine wirklich gute Frage. Ich verstehe nicht, warum ich der einzige Upvoter bin.

    – Bathseba

    1. Juni 2020 um 9:42 Uhr

  • Gilt nicht für diese spezifischen Beispiele, aber bitte beachten Sie, dass diese Ergebnisse nur von garantiert werden Operatorassoziativität. Das heißt, die additiven Operatoren + und – werden garantiert von links nach rechts ausgewertet. Daher erfolgt die implizite Konvertierung in Float dort, wo sie sollte, möglicherweise durch Glück (?). x + 1 - 1 hat eine andere Bedeutung als 1 - 1 + x . Ersteres ist äquivalent zu (x + (float)1) - (float)1 und letzteres zu (float)((int)1 - (int)1) + x;. Um solche Fehler zu vermeiden, verwenden Sie Float-Konstanten 1.0f.

    – Ludin

    1. Juni 2020 um 10:01 Uhr


Benutzer-Avatar
Elfeal

IEEE 754-Gleitkommazahlen haben zwei Nullwerte, einen negativen und einen positiven. Zusammengenommen ergibt sich das positive Ergebnis.

So id1(-0.f) ist 0.fnicht -0.f.
Beachten Sie, dass id1(-0.f) == -0.f Weil 0.f == -0.f.

Demo

Beachten Sie auch, dass das Kompilieren mit -ffast-math in GCC führt die Optimierung durch und ändert das Ergebnis.

  • Verdammt, du warst schneller. Hier ein Gottriegel zur Veranschaulichung.

    – n314159

    1. Juni 2020 um 9:35 Uhr

  • Schön geschrieben. Ich fing an, eine Antwort zu schreiben, aber Sie waren zuerst fertig, weil Sie die Situation klarer ausgedrückt haben.

    – Jerry Sarg

    1. Juni 2020 um 9:35 Uhr

Benutzer-Avatar
t.niese

“Ich habe vier Identitätsfunktionen, die im Wesentlichen nichts tun.”

Das ist nicht wahr.

Für Fließkommazahlen x + 1 - 1 ist nicht gleich x + 0es ist gleich (x + 1) - 1. Also wenn man zB einen sehr kleinen hat x dann verlieren Sie diesen sehr kleinen Teil in der x + 1 Schritt, und der Compiler kann nicht wissen, ob das Ihre Absicht war oder nicht.

Und im Fall von x * 2 / 2das x * 2 aufgrund der Gleitkommapräzision möglicherweise auch nicht genau, so dass Sie hier einen ähnlichen Fall haben, der Compiler weiß nicht, ob Sie aus irgendeinem Grund den Wert von ändern möchten x auf diese Weise.

Diese wären also gleich:

float id0(float x) {
    return x + (1. - 1.);
}

float id1(float x) {
    return x + 0;
}

Und diese wären gleich:

float id2(float x) {
    return x * (2. / 2.);
}

float id3(float x) {
    return x * 1;
}

Das gewünschte Verhalten könnte sicherlich auch anders definiert werden. Aber wie bereits von Nelfeal erwähnt, muss diese Optimierung explizit mit aktiviert werden -ffast-math

Aktivieren Sie den schnellen mathematischen Modus. Diese Option lässt den Compiler aggressive, potenziell verlustbehaftete Annahmen über Gleitkomma-Mathematik treffen. Diese beinhalten:

  • Fließkomma-Mathematik gehorcht regulären algebraischen Regeln für reelle Zahlen (z. B. + und * sind assoziativ, x/y == x * (1/y) und (a + b) * c == a * c + b * c) ,
  • Operanden für Gleitkommaoperationen sind nicht gleich NaN und Inf und
  • +0 und -0 sind austauschbar.

fast-math ist für clang und gcc eine Sammlung von Flags (hier das von clang aufgelistete):

  • -fno-Ehre-Unendlichkeiten
  • -fno-honor-nans
  • -fno-math-fehlernr
  • -endliche-math
  • -fassoziativ-math
  • -reziproke-math
  • -fno-vorzeichenbehaftete Nullen
  • -fno-trapping-math
  • -ffp-Vertrag=schnell

  • x * 2 ist möglicherweise nicht genau, aber nicht wegen der Gleitkommagenauigkeit, sondern wegen seines Bereichs: x * 2 könnte eine Unendlichkeit erzeugen, wenn x es ist zu groß. Dies ist weniger wahrscheinlich, für x * 2. wie 2. Typ hat double und double hat normalerweise eine größere Reichweite als float.

    – chqrlie

    1. Juni 2020 um 10:40 Uhr

Benutzer-Avatar
Basile Starynkevitch

Lies das float-number-gui.de Webseite, mehr dazu IEEE754der C11-Standard n1570dem C++11-Standard n3337.

float id1(float x) {
    return x + 0;
}

Wenn x passiert, um eine Signalisierung zu sein NaNdein id1 vielleicht sogar nicht zurückkehren (und wahrscheinlich sollte nicht Rückkehr).

Wenn x ist also ein ruhiges NaN id1(x) != x seit NaN != NaN (wenigstens NaN == NaN sollte falsch sein).

Im etwas Fälle, die Sie teuer wollen Arithmetik mit beliebiger Genauigkeit. Dann erwägen Sie die Verwendung GMPlib.

PS. Fließkommazahlen können Ihnen nach Ihrer Wahl Albträume oder einen Doktortitel bescheren. Sie manchmal Leute töten oder zumindest große finanzielle Katastrophen anrichten (z. B. ein Verlust von mehreren hundert Millionen US$ oder €).

  • Interessante Links – Besonders den “Menschen töten”-Link muss ich mir merken. Hier ist ein weiterer verwandter: stackoverflow.com/questions/3730019/…

    – RobertS unterstützt Monica Cellio

    1. Juni 2020 um 10:24 Uhr

  • Sie töten manchmal Menschen oder verursachen zumindest große finanzielle Katastrophen (z. B. ein Verlust von mehreren hundert Millionen US-Dollar oder €). Das klingt nach einer persönlichen Erfahrung, (ça sent le vécu), haben Sie an der Untersuchung über das Scheitern der teilgenommen Erster Start der Ariane 5?

    – chqrlie

    1. Juni 2020 um 10:30 Uhr

  • Wenn x ist ein ruhiger NaN, der zurückkehrt x produziert immer noch id1(x) != x. Das eigentliche Problem ist minus Null wo 1 / id1(x) != 1 / x

    – chqrlie

    1. Juni 2020 um 10:31 Uhr


  • @chrqlie: Das Labor, in dem ich in CEA LIST arbeite, hatte Zugriff auf Ariane 5-bezogene Dokumentation, aber ich persönlich hatte keinen Zugriff.

    – Basile Starynkevitch

    1. Juni 2020 um 10:49 Uhr

1298610cookie-checkWarum kann der Compiler die Gleitkommaaddition mit 0 nicht optimieren? [duplicate]

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy