pow() scheint hier um eins raus zu sein

Lesezeit: 5 Minuten

Was ist denn hier los:

#include <stdio.h>
#include <math.h>
int main(void) {
    printf("17^12 = %lf\n", pow(17, 12));
    printf("17^13 = %lf\n", pow(17, 13));
    printf("17^14 = %lf\n", pow(17, 14));
}

Ich bekomme diese Ausgabe:

17^12 = 582622237229761.000000
17^13 = 9904578032905936.000000
17^14 = 168377826559400928.000000

13 und 14 stimmen nicht überein Wolfram Alpha vgl:

12: 582622237229761.000000
    582622237229761

13: 9904578032905936.000000
    9904578032905937

14: 168377826559400928.000000
    168377826559400929

Außerdem ist es nicht um einen seltsamen Bruchteil falsch – es ist genau um einen falsch!

Wenn dies an mir liegt, stoße ich an die Grenzen dessen, was pow() kann für mich tun, gibt es eine Alternative, die dies berechnen kann? Ich brauche eine Funktion, die rechnen kann x^ywo x^y ist immer kleiner als ULLONG_MAX.

  • Vielleicht musst du es wissen Was jeder Informatiker über Gleitkommaarithmetik wissen sollte?

    – Irgendein Programmierer-Typ

    30. Januar 2014 um 9:43 Uhr

  • Fun Fact: Diese Schleife endet nicht: for (float f = 0; f < INT_MAX; f++) { }

    – ntoskrnl

    30. Januar 2014 um 12:59 Uhr

  • Das l in %lf tut nichts. Sie drucken doubles, und ob Ihre Kompilierungsplattformen zuordnen double zum Double-Precision-Format von IEEE 754, werden Sie niemals drucken 9904578032905937.0 auf diese Weise, da es keine solche Zahl mit doppelter Genauigkeit gibt.

    – Pascal Cuoq

    30. Januar 2014 um 14:36 ​​Uhr

  • @ntoskrnl: könnte nicht beenden. Es tat auf Crays und Systemen wo int war 16 Bit.

    – MSalter

    30. Januar 2014 um 15:07 Uhr

  • Mit einer double, können Sie sich immer auf die ersten 15 Dezimalstellen (signifikante Stellen) verlassen. Mit 17**12, das sind alle Ziffern bis zum Punkt. Mit 17**13, Sie können der letzten Ziffer nicht vertrauen. Dass double würde normalerweise (ohne Formatierung) geschrieben werden als 9.90457803290594E+15. Und 17**14 mit dieser Notation ist 1.68377826559401E+17.

    – Jeppe Stig Nielsen

    30. Januar 2014 um 16:25 Uhr

pow arbeitet mit double Zahlen. Diese stellen Zahlen der Form s * 2^e dar, wobei s eine 53-Bit-Ganzzahl ist. Deswegen double kann alle ganzen Zahlen unter 2^53 speichern, aber nur etwas Ganzzahlen über 2^53. Insbesondere kann es nur gerade Zahlen > 2^53 darstellen, da für e > 0 der Wert immer ein Vielfaches von 2 ist.

17^13 benötigt 54 Bits zur genauen Darstellung, also wird e auf 1 gesetzt und somit wird der berechnete Wert zu einer geraden Zahl. Der korrekte Wert ist ungerade, daher ist es nicht verwunderlich, dass er um eins abweicht. Ebenso benötigt 17^14 58 Bits zur Darstellung. Dass es auch um eins geht, ist ein glücklicher Zufall (solange man nicht zu viel Zahlentheorie anwendet), es ist einfach zufällig eins gegen a Vielfaches von 32das ist die Granularität, bei der double Zahlen dieser Größenordnung werden gerundet.

Für eine exakte ganzzahlige Potenzierung sollten Sie durchgehend ganze Zahlen verwenden. Schreibe dein Eigenes double-freie Potenzierungsroutine. Verwenden Sie die Potenzierung durch Quadrieren von if y kann groß sein, aber ich nehme an, es ist immer weniger als 64, was dieses Problem strittig macht.

  • @ Trideceth12 Das ist der offensichtliche Weg, den Sie tun sollten. Und es ist zu einfach, das Rad neu zu erfinden. Da Sie auf den Bereich einer 64-Bit-Ganzzahl und beschränkt sind x und y ganze Zahlen sind, gibt es weder Genauigkeitsprobleme zu lösen noch große Leistungsherausforderungen, die ausgeklügelte Algorithmen erfordern (10, 20 oder sogar 60 Multiplikationen sind billig, billiger als zum Beispiel jede I/O).

    Benutzer395760

    30. Januar 2014 um 9:58 Uhr

  • Ein bisschen modulare Arithmetik: 17 % 16 = 1, also 17^n % 16 = 1 (was bedeutet, dass die vier niederwertigsten Bits in der binären Darstellung von 17^n immer 0001 sein werden).

    – tom

    30. Januar 2014 um 10:10 Uhr


  • @tom Das habe ich gemeint, indem ich zu viel Zahlentheorie +1 angewendet habe. Dies ist jedoch keine vollständige Erklärung, da es 1 (im Gegensatz zu 17) Modulo 32 sein muss, um um eins ausgeschaltet zu sein. Es gibt jedoch wahrscheinlich kein großes Muster, 17 ^ 15 ist mehr als eins.

    Benutzer395760

    30. Januar 2014 um 10:22 Uhr


  • @delnan Ich habe 17 ^ 14 mit fünf abgeschnittenen Bits verpasst. Aber 17^2 % 32 = 1, also 17^14 % 32 = (17^2)^7 % 32 = 1.

    – tom

    30. Januar 2014 um 10:40 Uhr

  • @ trideceth12 Es gibt den Square-and-Multiply-Algorithmus, der im Exponenten eine logarithmische statt einer linearen Laufzeit hat.

    – CodesInChaos

    30. Januar 2014 um 16:00 Uhr

Benutzeravatar von M Oehm
M Öhm

Die Zahlen, die Sie erhalten, sind zu groß, um mit a dargestellt zu werden double genau. Eine Gleitkommazahl mit doppelter Genauigkeit hat im Wesentlichen 53 signifikante Binärziffern und kann alle ganzen Zahlen bis darstellen 2^53 oder 9.007.199.254.740.992.

Bei höheren Zahlen werden die letzten Ziffern abgeschnitten und das Ergebnis Ihrer Berechnung wird auf die nächste Zahl gerundet, die als a dargestellt werden kann double. Zum 17^13, die nur geringfügig über der Grenze liegt, ist dies die nächste gerade Zahl. Für Zahlen größer als 2^54 dies ist die nächste Zahl, die durch vier teilbar ist, und so weiter.

  • Ahh .. die nächste gerade Zahl macht Sinn

    – jsj

    30. Januar 2014 um 9:47 Uhr

Benutzeravatar von barak manos
barak manos

Wenn Ihre Eingabeargumente nicht negative Ganzzahlen sind, können Sie Ihre eigenen implementieren pow.

Rekursiv:

unsigned long long pow(unsigned long long x,unsigned int y)
{
    if (y == 0)
        return 1;
    if (y == 1)
        return x;
    return pow(x,y/2)*pow(x,y-y/2);
}

Iterativ:

unsigned long long pow(unsigned long long x,unsigned int y)
{
    unsigned long long res = 1;
    while (y--)
        res *= x;
    return res;
}

Effizient:

unsigned long long pow(unsigned long long x,unsigned int y)
{
    unsigned long long res = 1;
    while (y > 0)
    {
        if (y & 1)
            res *= x;
        y >>= 1;
        x *= x;
    }
    return res;
}

  • Die rekursive Version ist nutzlos, da sie die Antwort nicht wiederverwendet. Ein rekursiver Aufruf genügt: root = y ? pow(x, y / 2) : 1; return root * root * (y&1 ? x : 1);

    – tom

    30. Januar 2014 um 10:32 Uhr

  • @chux: Habe gerade bemerkt, dass das fehlt if (y == 1)… Ich kann nicht glauben, dass es schon so lange da ist, ohne dass ich es merke. Danke vielmals!!!!! 🙂

    – barak manos

    26. August 2014 um 17:19 Uhr

  • @tom Recht hast du. Verpasste die “eventuell multipliziert mit pow (0, 1)”.

    – chux – Wiedereinsetzung von Monica

    6. September 2014 um 14:39 Uhr

Benutzeravatar des Konstrukteurs
Konstrukteur

Eine kleine Ergänzung zu anderen guten Antworten: Unter x86-Architektur ist es normalerweise verfügbar x87 80-Bit erweitertes Formatdas von den meisten C-Compilern über die unterstützt wird long double Typ. Dieses Format erlaubt den Betrieb mit ganzen Zahlen bis zu 2^64 ohne Lücken.

Es gibt ein Analogon von pow() in <math.h> die für den Betrieb vorgesehen ist long double Zahlen – powl(). Es sollte auch beachtet werden, dass der Formatbezeichner für die long double Werte ist anders als für double Einsen – %Lf. Also das richtige Programm mit der long double Typ sieht so aus:

#include <stdio.h>
#include <math.h>
int main(void) {
    printf("17^12 = %Lf\n", powl(17, 12));
    printf("17^13 = %Lf\n", powl(17, 13));
    printf("17^14 = %Lf\n", powl(17, 14));
}

Wie Stephen Canon in den Kommentaren feststellte, gibt es keine Garantie dafür, dass dieses Programm genaue Ergebnisse liefert.

1401180cookie-checkpow() scheint hier um eins raus zu sein

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

Privacy policy