Warum gibt meine ganzzahlige Mathematik mit std::pow die falsche Antwort?

Lesezeit: 8 Minuten

Warum gibt meine ganzzahlige Mathematik mit stdpow die falsche Antwort
Benutzer1257

Betrachten Sie den folgenden Codeabschnitt:

#include <iostream>
#include <cmath>

int main() {
    int i = 23;
    int j = 1;
    int base = 10;
    int k = 2;
    i += j * pow(base, k);
    std::cout << i << std::endl;
}

Es gibt “122” statt “123” aus. Ist es ein Fehler in g++ 4.7.2 (MinGW, Windows XP)?

  • Ich habe das Gefühl, dass dies eine gute Zeit für die Alten ist Was jeder Informatiker über Gleitkommaarithmetik wissen sollte Hinweis.

    – Daniel Kamil Kozar

    6. April 2013 um 13:50 Uhr


  • Ich habe Übung §6.6 gemacht[16] aus Bjarne Stroustrups Buch über C++, in dem es heißt, eine Funktion zu schreiben, die die Zeichenfolgendarstellung von Ganzzahlen in Ganzzahlen umwandelt. Ich habe der Funktion 123 eingegeben und sie hat 122 zurückgegeben, also habe ich begonnen, das Problem zu untersuchen.

    – Benutzer1257

    6. April 2013 um 14:26 Uhr

  • Ihr Beispiel würde mit einer korrekt gerundeten funktionieren pow() Fließkommafunktion. Hier findest du einen: lipforge.ens-lyon.fr/www/crlibm . Ihr Ansatz würde jedoch bei Berechnungen mit größeren Ganzzahlen, die möglicherweise nicht genau als Gleitkommazahlen dargestellt werden (und dann gibt es nicht einmal eine korrekt gerundete Zahl), an ihre Grenzen stoßen pow() Funktion tun kann).

    – Pascal Cuoq

    6. April 2013 um 14:36 ​​Uhr


  • @sellibitze: Qualifikation pow mit std:: ergibt auch 122.

    – Benutzer1257

    6. April 2013 um 14:50 Uhr

  • @DanielKamilKozar Dieser Artikel ist für die Frage nicht relevant, da er nur die Funktion behandelt PositivePower(x,n) (Funktion, die das Beispiel des OP zum Laufen bringen würde, wenn es verwendet würde). Der Artikel tut es nicht besprechen Sie das Allgemeine pow(), noch warum es um mehr als 1 ULP falsch sein sollte. Sie haben eine gute Heuristik mit „Zitieren Sie jedes Mal Goldberg, wenn etwas Seltsames mit Fließkomma passiert“, aber es ist nur eine Heuristik.

    – Pascal Cuoq

    6. April 2013 um 20:07 Uhr

Warum gibt meine ganzzahlige Mathematik mit stdpow die falsche Antwort
Andy Prowl

std::pow() arbeitet mit Gleitkommazahlen, die keine unendliche Genauigkeit haben, und wahrscheinlich die Implementierung der von Ihnen verwendeten Standardbibliothek implementiert pow() auf eine (schlechte) Weise, die diesen Mangel an unendlicher Präzision relevant werden lässt.

Sie könnten jedoch leicht Ihre eigene Version definieren, die mit ganzen Zahlen arbeitet. In C++11 ist das sogar möglich constexpr (damit das Ergebnis nach Möglichkeit zur Kompilierzeit berechnet werden kann):

constexpr int int_pow(int b, int e)
{
    return (e == 0) ? 1 : b * int_pow(b, e - 1);
}

Hier ist ein Live-Beispiel.


Tail-rekursive Form (Dank an Dan Nissenbaum):

constexpr int int_pow(int b, int e, int res = 1)
{
    return (e == 0) ? res : int_pow(b, e - 1, b * res);
}

  • Aber normalerweise würden Sie erwarten, dass Gleitkomma-Arithmetik gut funktioniert, wenn sowohl die Eingaben als auch die Ausgabe (kleinere) ganze Zahlen sind. (z. B. 1,0 + 1,0 == 2,0, ohne Rundungsfehler). Ich kann dem OP also nicht die Schuld geben, dass es verwirrt ist, dass Rundungsfehler eingeführt wurden Das Sonderfall (d.h. pow(10.0, 2.0) != 100.0)

    – jalf

    6. April 2013 um 13:54 Uhr


  • Diese Antwort kann das Kernproblem nicht identifizieren. Darin heißt es: „std::pow() arbeitet mit Fließkommazahlen, die keine unendliche Genauigkeit haben“, aber mangelnde Genauigkeit ist nicht der Grund dafür pow liefert das falsche Ergebnis. Die Parameter und der Rückgabetyp von sind mehr als ausreichend präzise pow um ein genaues Ergebnis zurückzugeben. Das eigentliche Problem ist eine schlechte Qualität pow Implementierung. Eine gute pow Implementierung gibt genau 1024 für zurück pow(10., 2.). Der Gleitkomma-Arithmetik die Schuld dafür zu geben, ist wie der Integer-Arithmetik if die Schuld zu geben 45 % 7 zurückgegeben 2.

    – Eric Postpischil

    6. April 2013 um 20:55 Uhr


  • @DanNissenbaum: Gemäß IEEE-Standard für Gleitkommaarithmetik (754-2008), Klausel 3.3, stellen Gleitkommaformate (-1)**s•b dare•m, wobei s 0 oder 1 ist, e eine ganze Zahl in einem Intervall je nach Format ist und m eine Zahl ist, die je nach Format durch eine bestimmte Ziffernfolge dargestellt wird (entspricht einer ganzen Zahl in einem bestimmten Bereich dividiert durch a bestimmte Leistung der Basis), plus +unendlich, -unendlich, ruhiges NaN und signalisierendes NaN. **Gar nichts Im Standard heißt es, dass Gleitkommaobjekte Intervalle darstellen. Das ist eine falsche allgemeine Überlieferung, die von Gerüchten weitergegeben wird, keine Spezifikation.

    – Eric Postpischil

    6. April 2013 um 22:57 Uhr

  • @DanNissenbaum: Warum spuckst du immer noch diese Verwirrung aus? Ich habe es dir schon einmal gesagt und du weigerst dich zuzuhören; Die Implementierung kümmert sich nicht darum, was Sie beabsichtigen. Es ist nicht im Gedankenlesen-Geschäft. Es sieht 123,0 und weiß, dass es verrückt wäre, 123,0 auf 122 zu runden. Wie ich bereits sagte, sind Fließkommazahlen nicht unbedingt Annäherungen und stellen keine Bereiche reeller Zahlen dar. Jeder repräsentiert eine einzelne reelle Zahl. Moderne FPUs machen keine Fehler. Bitte, bitte, bitte lernen Sie, wie Gleitkommazahlen funktionieren.

    – tmyklebu

    7. April 2013 um 2:55 Uhr

  • @DanNissenbaum: Nein, verdammt. pow ist das Problem. Es ist illegal, eine Implementierung zu konvertieren 100.0 auf etwas anderes als 100. Es gibt offensichtlich 99, was das bedeutet pow kamen nicht zurück 100.0.

    – tmyklebu

    7. April 2013 um 3:06 Uhr

Alle anderen Antworten verfehlen oder tanzen um das einzige Problem in der Frage:

Die pow in Ihrer C++-Implementierung ist von schlechter Qualität. Es gibt eine ungenaue Antwort zurück, wenn dies nicht erforderlich ist.

Holen Sie sich eine bessere C++-Implementierung oder ersetzen Sie zumindest die mathematischen Funktionen darin. Die eine, auf die Pascal Cuoq hingewiesen hat ist gut.

  • Sie denken also, dass es völlig in Ordnung ist, sich auf QoI zu verlassen? Ich würde seinen Code wegen der Rundungsprobleme nicht in einer Überprüfung akzeptieren.

    – Sellibitze

    6. April 2013 um 22:14 Uhr

  • @sellibitze: Ich habe nichts darüber geschrieben, auf die Qualität der Umsetzung zu setzen. Engineering besteht unter anderem darin, Ergebnisse aus Spezifikationen abzuleiten. Wenn Sie eine vertrauenswürdige Spezifikation haben, dass Ihre Bibliothek exakte Ergebnisse liefert, wenn sie darstellbar sind, dann können Sie sich darauf verlassen. Wenn Sie eine solche Spezifikation nicht haben, dann dürfen Sie sich nicht darauf verlassen.

    – Eric Postpischil

    6. April 2013 um 23:06 Uhr


  • Sicher, Sie schlagen vor, die pow-Implementierung zu ersetzen, anstatt den Code zu reparieren.

    – Sellibitze

    7. April 2013 um 11:31 Uhr

  • Wollen Sie damit sagen, dass der ISO-C++-Standard Implementierungen von pow erfordert, um das genaue Ergebnis zurückzugeben, wenn es ausdrückbar ist? Wenn ja: Können Sie mir diese Garantie nennen?

    – Sellibitze

    7. April 2013 um 12:15 Uhr


  • @sellibitze: Zu „Ersetzen Sie die pow-Implementierung, anstatt den Code zu reparieren“: Ersetzen der pow Implementierung repariert den Code. Sie scheinen eine Ahnung zu haben, dass Sie mit der C++-Implementierung festsitzen, die Sie gerade verwenden, oder nur mit den Garantien des C++-Standards und mit keinen anderen. Beides ist nicht wahr. Es steht Ihnen frei, Ihre C++-Implementierung zu ändern oder zu ändern, und es steht Ihnen frei, andere Garantien als die im C++-Standard sicherzustellen.

    – Eric Postpischil

    28. März 2020 um 17:30 Uhr

1646319616 377 Warum gibt meine ganzzahlige Mathematik mit stdpow die falsche Antwort
Chris Seymour

Zumindest nicht bei mir:

$ g++ --version | head -1
g++ (GCC) 4.7.2 20120921 (Red Hat 4.7.2-2)

$ ./a.out 
123

IDEone läuft auch Version 4.7.2 und gibt 123.


Unterschriften von pow() von http://www.cplusplus.com/reference/cmath/pow/

     double pow (      double base,      double exponent );
long double pow ( long double base, long double exponent );
      float pow (       float base,       float exponent );
     double pow (      double base,         int exponent );
long double pow ( long double base,         int exponent );

Du solltest einstellen double base = 10.0; und double i = 23.0.

  • Ich habe die Windows-Version (MinGW).

    – Benutzer1257

    6. April 2013 um 13:44 Uhr

  • Alles, was Sie gezeigt haben, ist, dass es mindestens eine Version von GCC gibt, die sich wie vom OP erwartet verhält. Das beantwortet nicht die Frage, ob die OP-Version einen Fehler hat oder das Verhalten nach dem Standard zulässig ist

    – jalf

    6. April 2013 um 13:50 Uhr

  • @jalf Eigentlich hat er gezeigt, dass es in der geht gleich Version als OP (sofern das OP die Versionsnummer angegeben hat).

    – J Bentley

    6. April 2013 um 14:06 Uhr

  • @JBentley zeigt, dass es mit derselben GCC-Version auf einer möglicherweise anderen CPU, einem anderen Betriebssystem, einer anderen Standardbibliothek oder einem anderen GCC-Build funktioniert. was beweist das?

    – Johannes Schaub – litb

    6. April 2013 um 14:27 Uhr


  • Tatsächlich konnte ich diese Überladungen in der C++11-Spezifikation nicht mehr finden. Die Überladungen mit ganzzahligen Exponenten scheinen entfernt worden zu sein.

    – Sellibitze

    7. April 2013 um 12:04 Uhr

Warum gibt meine ganzzahlige Mathematik mit stdpow die falsche Antwort
Matt Petersson

Ihr Problem ist kein Fehler in gcc, das ist absolut sicher. Es kann sich um einen Fehler in der Implementierung handeln powaber ich denke, Ihr Problem ist wirklich einfach die Tatsache, dass Sie verwenden pow was ein ungenaues Gleitkommaergebnis liefert (weil es als so etwas wie implementiert ist exp(power * log(base)); und log(base) wird nie absolut genau sein [unless base is a power of e].

  • Der Standard lässt das ziemlich sicher zu cmath Funktionen im globalen Namensraum definiert werden. Ja, das ist scheiße.

    – Konrad Rudolf

    6. April 2013 um 14:01 Uhr

  • @KonradRudolph: Ich sehe keinen Widerspruch. Der Standard erfordert sicherlich keine Implementierungen, um die mathematischen C++-Funktionen im globalen Bereich zu deklarieren. Nur eine einzige pow-Funktion zu haben, die Doubles aus der in diesem Bereich verfügbaren C-Bibliothek nimmt, ist ein möglicher Implementierungseffekt.

    – Sellibitze

    6. April 2013 um 14:03 Uhr


  • Gut pow Implementierungen werden nicht mit implementiert exp(log(base)*power)und sie sind dafür gut genug implementiert pow(10., 2.) gibt genau 100 zurück. Das Problem hier ist nicht die Funktionsauflösung oder Gleitkommaarithmetik; es ist ausschließlich die Qualität der Implementierung.

    – Eric Postpischil

    6. April 2013 um 21:04 Uhr

  • @EricPostpischil: “Das Problem ist nicht die Funktionsauflösung” – Nun, es scheint, dass das “Problem” aus mehreren Gründen besteht. Ich persönlich würde nicht erwarten, dass eine pow(double,double)-Funktion so genau wie möglich ist. Aber ich erwarte, dass pow(double,int) so implementiert wird, dass pow(10.0,2) genau 100 ergibt. Offensichtlich, wenn pow(double,int) auf diese Weise implementiert wurde und die Funktionsauflösung diese Funktion ausgewählt hat, sollte das OP dies tun habe 100 als Ergebnis bekommen. Aber es scheint, dass seine std::pow(double,int)-Überladung nicht genau 100,0 ergeben kann.

    – Sellibitze

    6. April 2013 um 22:07 Uhr

  • @ EricPostpischil: Scheint in C ++ 98 zu sein, zusammen mit std::pow(float, int) und std::pow(long double, int). Sehen lib.c.math.

    – tmyklebu

    7. April 2013 um 1:20 Uhr

924560cookie-checkWarum gibt meine ganzzahlige Mathematik mit std::pow die falsche Antwort?

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

Privacy policy