log(10.0) kann kompilieren, aber log(0.0) nicht mit undefinierter Referenz?

Lesezeit: 6 Minuten

Benutzeravatar von xuhdev
xuhdev

Für den folgenden C Quellcode:

#include <math.h>

int main(void)
{
    double          x;

    x = log(0.0);

    return 0;
}

Wenn ich mit kompiliere gcc -lmIch habe:

/tmp/ccxxANVH.o: In function `main':
a.c:(.text+0xd): undefined reference to `log'
collect2: error: ld returned 1 exit status

Aber, wenn ich ersetzen log(0.0) mit log(10.0)dann kann es erfolgreich kompiliert werden.

Ich verstehe das nicht ganz, denn egal, ob sie mathematisch sinnvoll sind oder nicht, sie sollten kompilieren – es gibt keinen Syntaxfehler. Könnte das jemand erklären?

Nur für den Fall, meine gcc -v Ausgang:

Configured with: ../src/configure -v --with-pkgversion='Ubuntu 4.8.2-19ubuntu1' --with-bugurl=file:///usr/share/doc/gcc-4.8/README.Bugs --enable-languages=c,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.8 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.8 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --disable-libmudflap --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-4.8-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.8.2 (Ubuntu 4.8.2-19ubuntu1)

Beachten Sie, dass es bei dieser Frage um das ständige Falten geht, bei der vorgeschlagenen doppelten Frage jedoch um eine fehlende Verknüpfungsbibliothek.

  • Dies hat wahrscheinlich mit der ständigen Ausbreitung und dem Nicht-Spezifizieren zu tun -lm.

    – Mystisch

    18. Juni 2014 um 20:38 Uhr

  • @Cornstalks In der Tat sollte man angeben -lm nach der Quelldatei.

    – xuhdev

    19. Juni 2014 um 4:00 Uhr

  • Ich bin mit Ihrer Prämisse nicht einverstanden, dass ein undefinierter mathematischer Ausdruck kompiliert werden sollte. Da log(0) undefiniert / singulär ist, kann sein Vorhandensein in einem Programm nur das Ergebnis eines Programmierfehlers sein. Ich hätte nicht erwartet, dass der Compiler dies abfängt, aber bedenke die Tatsache, dass er a Besonderheitkein Defekt.

    – Gdalja

    25. Juni 2014 um 18:56 Uhr

  • @gdalya es sollte -Inf zurückgeben, nicht undefiniert. Sehen hier

    – xuhdev

    25. Juni 2014 um 19:35 Uhr

  • @LưuVĩnhPhúc Es sind zwei verschiedene Fragen. Siehe meine Bearbeitung in der letzten Zeile.

    – xuhdev

    20. August 2016 um 17:30 Uhr

Benutzeravatar von Shafik Yaghmour
Shafik Yaghmur

gcc Kann benutzen eingebaute Funktionen In vielen Fällen heißt es in ihrer Dokumentation:

Viele dieser Funktionen sind nur in bestimmten Fällen optimiert; wenn sie in einem bestimmten Fall nicht optimiert sind, wird ein Aufruf an die Bibliotheksfunktion ausgegeben.

also deshalb gcc muss nicht mit der mathematischen Bibliothek verknüpft werden, wenn die integrierte Funktion verwendet wird, aber seitdem log(0) ist nicht definiert es zwingt wahrscheinlichgcc um es zur Laufzeit auszuwerten, da es eine Nebenwirkung hat.

Betrachten wir die Entwurf des C99-Standards Sektion 7.12.1 Behandlung von Fehlerbedingungen im Absatz 4 es sagt (Betonung von mir):

Ein gleitendes Ergebnis läuft über, wenn die Größe des mathematischen Ergebnisses endlich, aber so groß ist, dass das mathematische Ergebnis nicht ohne außerordentliche Rundungsfehler in einem Objekt des angegebenen Typs dargestellt werden kann. Wenn ein gleitendes Ergebnis überläuft und die Standardrundung wirksam ist oder wenn das mathematische Ergebnis aus endlichen Argumenten eine exakte Unendlichkeit ist (zum Beispiel log(0.0)), dann gibt die Funktion je nach Rückgabetyp den Wert des Makros HUGE_VAL, HUGE_VALF oder HUGE_VALL zurückmit demselben Vorzeichen wie der korrekte Wert der Funktion;
wenn der ganzzahlige Ausdruck math_errhandling & MATH_ERRNO ungleich Null ist, nimmt der ganzzahlige Ausdruck errno den Wert ERANGE an; wenn der Integer-Ausdruck math_errhandling & MATH_ERREXCEPT ungleich Null ist, wird die Fließkomma-Ausnahme „Teile durch Null“ ausgelöst, wenn das mathematische Ergebnis genau unendlich ist, und andernfalls wird die Fließkomma-Ausnahme „Überlauf“ ausgelöst.

Wir können anhand eines Live-Beispiels mit sehen -S Flag zum Generieren von Assembly und grep log um Anrufe herauszufiltern log.

Im Falle des log(0.0) die folgende Anweisung wird generiert (live sehen):

call    log

aber im Fall von log(10.0) nein call log Anweisung wird generiert, (live sehen).

Wir können in der Regel verhindern gcc von der Verwendung der integrierten Funktion, indem Sie die verwenden -fno-eingebautes Flag Dies ist wahrscheinlich ein schneller Weg, um zu testen, ob ein Builtin verwendet wird.

Beachten Sie, dass -lm muss nach der Quelldatei gehen, zum Beispiel (aus der verlinkten Antwort entnommen) wenn main.c Benötigte die Mathematikbibliothek, dann würden Sie verwenden:

 gcc main.c -lm 

  • Das ist eine ausgezeichnete Antwort! Ja genau da liegt das Problem.

    – xuhdev

    18. Juni 2014 um 20:40 Uhr

  • Der Grund, warum gcc nicht auswerten kann log(0) zur Laufzeit ist schwierig. Der Standard gibt an, dass es zurückkehrt -HUGE_VALverursacht aber auch einen Bereichsfehler als Nebeneffekt (sichtbar in errno zum Beispiel), sodass der Anruf nicht eliminiert werden kann.

    – Tavian Barnes

    18. Juni 2014 um 20:45 Uhr

  • @TavianBarnes in der Tat wollte ich das auch hinzufügen.

    – Shafik Yaghmour

    18. Juni 2014 um 20:46 Uhr

  • @TavianBarnes: Nun, ein Compiler könnte ersetzen log(0.0) mit Code, der nachgibt HUGE_VAL und setzt errnoaber es ist wahrscheinlich einfacher, nur den Aufruf zu generieren (da dies sowieso für nicht konstante Argumente möglich sein muss).

    – Keith Thompson

    18. Juni 2014 um 20:56 Uhr

  • @KeithThompson Ich denke, sie wollen den eingebauten Evaluierungscode einfach halten und die Behandlung von Ausnahmen und dergleichen vermeiden, was den Laufzeitcode komplizierter macht.

    – Shafik Yaghmour

    18. Juni 2014 um 21:15 Uhr

Axels Benutzeravatar
Axel

Die Kompilierung ist in Ordnung, es ist nur der Linker-Schalter -lm das fehlt.

Die zweite Version wird wahrscheinlich kompiliert und verlinkt, weil gcc ersetzt log(10.0) mit einer Konstante, sodass kein Aufruf der Mathematikbibliothek erforderlich ist. Im zweiten Fall ist das Ergebnis mathematisch undefiniert und die Auswertung führt zu einem Domänenfehler. In diesem Fall kann der Ausdruck nicht durch eine Konstante ersetzt werden, da die Behandlung von Domänenfehlern zur Laufzeit anders sein kann.

Zitat aus der C-Norm (Entwurf):

Bei einem Domänenfehler gibt die Funktion einen implementierungsdefinierten Wert zurück; wenn der ganzzahlige Ausdruck math_errhandling & MATH_ERRNO ungleich Null ist, nimmt der ganzzahlige Ausdruck errno den Wert EDOM an; wenn der Integer-Ausdruck math_errhandling & MATH_ERREXCEPT ungleich Null ist, wird die Gleitkommaausnahme „ungültig“ ausgelöst.

Also Auswertung von log(0.0) Beide führen zur Rückgabe des Werts HUGE_VAL (nicht NAN wie ich zuvor behauptet habe) oder eine Gleitkommaausnahme.

BEARBEITEN: Ich habe meine Antwort basierend auf den erhaltenen Kommentaren korrigiert und einen Link zur Beschreibung im C-Standard hinzugefügt.

  • Vielen Dank für Ihre Antwort. Tut mir leid, dass @Shafik einen Schritt früher ist als Sie, daher kann ich Ihre Antwort nicht als akzeptierte Antwort akzeptieren.

    – xuhdev

    18. Juni 2014 um 20:42 Uhr

  • Kein Problem. Dafür ist SO da… 🙂

    – Axel

    18. Juni 2014 um 20:43 Uhr

  • Das Ergebnis ist -Inf, nicht NaN.

    – Tavian Barnes

    18. Juni 2014 um 20:47 Uhr

  • @TavianBarnes: Ich denke, das ist die definierte Implementierung. Der Standard sagt nur Folgendes: „Die Log-Funktionen berechnen den Basis-e (natürlichen) Logarithmus von x. Ein Bereichsfehler tritt auf, wenn das Argument negativ ist. Ein Bereichsfehler kann auftreten, wenn das Argument Null ist.“

    – Axel

    19. Juni 2014 um 7:02 Uhr

  • @Axel “… wenn das mathematische Ergebnis exakt unendlich ist (z. B. log(0.0)), dann gibt die Funktion je nach Rückgabetyp den Wert des Makros HUGE_VAL, HUGE_VALF oder HUGE_VALL mit demselben Vorzeichen wie zurück korrekter Wert der Funktion”

    – Tavian Barnes

    19. Juni 2014 um 12:50 Uhr

1405040cookie-checklog(10.0) kann kompilieren, aber log(0.0) nicht mit undefinierter Referenz?

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

Privacy policy