Wie können zwei Versionen derselben Funktion, die sich nur darin unterscheiden, dass eine inline ist und die andere nicht, unterschiedliche Werte zurückgeben? Hier ist ein Code, den ich heute geschrieben habe und ich bin mir nicht sicher, wie er funktioniert.
#include <cmath>
#include <iostream>
bool is_cube(double r)
{
return floor(cbrt(r)) == cbrt(r);
}
bool inline is_cube_inline(double r)
{
return floor(cbrt(r)) == cbrt(r);
}
int main()
{
std::cout << (floor(cbrt(27.0)) == cbrt(27.0)) << std::endl;
std::cout << (is_cube(27.0)) << std::endl;
std::cout << (is_cube_inline(27.0)) << std::endl;
}
Ich würde erwarten, dass alle Ausgaben gleich sind 1
, aber es gibt dies tatsächlich aus (g ++ 8.3.1, keine Flags):
1
0
1
anstatt
1
1
1
Edit: clang ++ 7.0.0 gibt Folgendes aus:
0
0
0
und g ++ -Ofast this:
1
1
1
==
Gleitkommawerte nicht immer etwas unvorhersehbar?-Ofast
Option festgelegt, die solche Optimierungen ermöglicht?cbrt(27.0)
den Wert von zurück,0x0000000000000840
während die Standardbibliothek zurückgibt0x0100000000000840
. Die Doppel unterscheiden sich in der 16. Zahl nach dem Komma. Mein System: archlinux4.20 x64 gcc8.2.1 glibc2.28 mit Karo dieser . Ich frage mich, ob gcc oder glibc richtig ist.Antworten:
Erläuterung
Einige Compiler (insbesondere GCC) verwenden eine höhere Genauigkeit, wenn Ausdrücke zur Kompilierungszeit ausgewertet werden. Wenn ein Ausdruck nur von konstanten Eingaben und Literalen abhängt, kann er zur Kompilierungszeit ausgewertet werden, auch wenn der Ausdruck keiner constexpr-Variablen zugewiesen ist. Ob dies geschieht oder nicht, hängt ab von:
Wenn ein Ausdruck wie im ersten Fall explizit angegeben wird, weist er eine geringere Komplexität auf, und der Compiler wird ihn wahrscheinlich zur Kompilierungszeit auswerten.
Wenn eine Funktion als inline markiert ist, wird sie vom Compiler eher zur Kompilierungszeit ausgewertet, da Inline-Funktionen den Schwellenwert erhöhen, bei dem die Auswertung erfolgen kann.
Höhere Optimierungsstufen erhöhen auch diesen Schwellenwert, wie im Beispiel -Ofast, bei dem alle Ausdrücke aufgrund der Bewertung der Kompilierungszeit mit höherer Genauigkeit auf gcc als wahr ausgewertet werden.
Wir können dieses Verhalten hier im Compiler-Explorer beobachten. Bei der Kompilierung mit -O1 wird zur Kompilierungszeit nur die inline markierte Funktion ausgewertet, bei -O3 werden beide Funktionen zur Kompilierungszeit ausgewertet.
-O1
: https://godbolt.org/z/u4gh0g-O3
: https://godbolt.org/z/nVK4SoNB: In den Compiler-Explorer-Beispielen verwende ich
printf
stattdessen iostream, da dies die Komplexität der Hauptfunktion verringert und den Effekt sichtbarer macht.Dies
inline
hat keinen Einfluss auf die LaufzeitauswertungWir können sicherstellen, dass keiner der Ausdrücke zur Kompilierungszeit ausgewertet wird, indem wir den Wert von der Standardeingabe erhalten. Wenn wir dies tun, geben alle drei Ausdrücke false zurück, wie hier gezeigt: https://ideone.com/QZbv6X
Im Gegensatz zu diesem Beispiel , in dem wir dieselben Compilereinstellungen verwenden, aber den Wert zur Kompilierungszeit angeben, führt dies zu einer genaueren Auswertung der Kompilierungszeit.
quelle
Wie beobachtet, hat die Verwendung des
==
Operators zum Vergleichen von Gleitkommawerten zu unterschiedlichen Ausgaben mit unterschiedlichen Compilern und mit unterschiedlichen Optimierungsstufen geführt.Ein guter Weg, um Gleitkommawerte zu vergleichen, ist der im Artikel beschriebene relative Toleranztest : Gleitkommatoleranzen überarbeitet .
Wir berechnen zuerst den Wert
Epsilon
(die relative Toleranz ), der in diesem Fall wäre:Und verwenden Sie es dann sowohl in der Inline- als auch in der Nicht-Inline-Funktion auf folgende Weise:
Die Funktionen sind jetzt:
Jetzt wird die Ausgabe wie erwartet (
[1 1 1]
) mit verschiedenen Compilern und auf verschiedenen Optimierungsstufen erfolgen.Live-Demo
quelle
max()
Anrufs? Ist per Definitionfloor(x)
kleiner oder gleichx
,max(x, floor(x))
wird also immer gleich seinx
.max
nur dasfloor
des anderen ist, ist es nicht erforderlich. Aber ich habe einen allgemeinen Fall betrachtet, in dem Argumentemax
Werte oder Ausdrücke sein können, die unabhängig voneinander sind.operator==(double, double)
genau so sein, prüfen Sie, ob der Unterschied kleiner als ein skaliertes Epsilon ist? Etwa 90% der Gleitkomma-Fragen zu SO würden dann nicht existieren.Epsilon
Wert abhängig von seiner speziellen Anforderung angeben kann.