Wie runde ich in C?



  • @Michael E: Was würden wir ohne deine geballte Kompetenz und die Kartoffeln im Keller bloss machen? 😕 😕 😕



  • Oje...

    Also GNU-C kennt kein fabsl , dass "eindeutig C" stimmt zumindest nicht mit meiner gcc Version.

    Aber um einen "Dreher" aufzuklären: <cmath> bindet <math.h> ein. Zumindest (falls es bei deinem Compiler anders sein sollte) bei gcc.


  • Mod

    Yamakuzure schrieb:

    Aber um einen "Dreher" aufzuklären: <cmath> bindet <math.h> ein. Zumindest (falls es bei deinem Compiler anders sein sollte) bei gcc.

    Darauf kann man sich nicht verlassen - auch beim GCC nicht.



  • Yamakuzure schrieb:

    Also GNU-C kennt kein fabsl , dass "eindeutig C" stimmt zumindest nicht mit meiner gcc Version.

    Sorry, hab nur nachgesehen, dass fabsl nicht zum C++-Standard gehört. Es gehört aber anscheinend auch nicht zum C-Standard, was den Code nochmals degradiert.

    Aber um einen "Dreher" aufzuklären: <cmath> bindet <math.h> ein. Zumindest (falls es bei deinem Compiler anders sein sollte) bei gcc.

    Aber eine C90-Version von math.h. Diese unterscheidet sich von der C99-Version.



  • SeppJ schrieb:

    Yamakuzure schrieb:

    Aber um einen "Dreher" aufzuklären: <cmath> bindet <math.h> ein. Zumindest (falls es bei deinem Compiler anders sein sollte) bei gcc.

    Darauf kann man sich nicht verlassen - auch beim GCC nicht.

    Und warum sollte man sich darauf verlassen? Schließlich bindet man cmath ein. Ob der nun math.h einbindet oder nicht ist unwichtig.


  • Mod

    Yamakuzure schrieb:

    SeppJ schrieb:

    Yamakuzure schrieb:

    Aber um einen "Dreher" aufzuklären: <cmath> bindet <math.h> ein. Zumindest (falls es bei deinem Compiler anders sein sollte) bei gcc.

    Darauf kann man sich nicht verlassen - auch beim GCC nicht.

    Und warum sollte man sich darauf verlassen? Schließlich bindet man cmath ein. Ob der nun math.h einbindet oder nicht ist unwichtig.

    Tschuldige, ich hatte nicht gesehen, worauf du hinaus willst. Ich dachte du wolltest berniebutts Verwendung der Steinzeitheader rechtfertigen.



  • SeppJ schrieb:

    Ich dachte du wolltest berniebutts Verwendung der Steinzeitheader rechtfertigen.

    Lassen wir mal die Diskussion um die ´Steinzeit´ beseite. Wer einen Werkzeugkasten für häufig gebrauchte Dinge hat, greift darauf zurück. In diesem Fall ist der Einsatz von floor / floorl mit absolut gleichem Ergebnis tatsächlich schneller. Bei getesteten 10 Mio. Durchläufen für beide Methoden jedoch irrelevant. Ich reduziere mit 10 Mio. mal Runden (wer braucht das?) von 2.34 auf 1.40 sec auf einer heute als langsam geltenden Kiste. Man kann woanders viel mehr unnütze Performance verbraten. Dennoch danke für den kleinen Performance-Gewinn!

    Aber, ging es nicht um die Frage "Wie mache ich das überhaupt?" 😕


  • Mod

    😮 Das glaube ich dir jetzt einfach nicht, dass dein Code nur 2x so langsam ist wie floor. Wie hast du das gemessen? Dein Code ist unendlich umständlich. Und falsch, wie Michael E. schon beschrieben hat. Wenn ich ihn korrigiere, so dass er überhaupt läuft (d.h. str vergrößern (sonst stürzt das Programm immer ab) und ltoa ersetzen (weil es das bei mir nicht gibt), dann bekomme ich einen Faktor 6-8 Unterschied!

    Hier das Programm (ich habe deinen Namen wieder gekürzt):

    #include <iomanip>
    #include <iostream>
    #include <fstream>
    #include <cstdlib>
    #include <sys/time.h>
    #include <cmath>
    #include <cstdio>
    #include <cstring>
    
    using namespace std;
    
    double round(double value, unsigned int decPlaces)
    {
        double factor = std::pow(10.0, static_cast<double>(decPlaces));
        return std::floor(value * factor + 0.5) / factor;
    }
    
    void Runden(double von,double *nach,int nk)
    //   --------------------------------------------------------------------------
    //   Fliesskommazahl (double) runden
    //   --------------------------------------------------------------------------
    //   von        übergebene Fliesskommazahl
    //   *nach      Zeiger auf gerundete Fliesskommazahl
    //   nk         Anzahl der Nachkommastellen
    //   test       Stream für Kontrollausdrucke (extern festgelegt, z.B. auf eine
    //              Datei)
    //   Rückgabe:  keine. Wer will, kann hier die Anzahl der digits (Vor- und
    //              Nachkommastellen) zurück liefern.
    //   --------------------------------------------------------------------------
    //   DOUBLE     mit #define festgelegter Typ (double oder long double)
    //   Achtung:   Die Anzahl der möglichen Stellen (digits) richtet sich vor
    //              allem nach der Anzahl der Vorkommastellen. Diese ist durch den
    //              zwischenzeitlich benutzten Typ long int begrenzt auf 8, max 9.
    //              Bei Werten über einer Mrd ist die darstellbare Genauigkeit
    //              überschritten!
    //   --------------------------------------------------------------------------
    {
         long int   lvon;
         int        irest,i,faktor,minus=0;
         int        digits;
         double     wert,rest,ziel;
         char       str[16];
    //   test << "--> Runden - von-nk  "  << von << "  " << nk << endl;
    //   ... positiver oder negativer Wert? ...
         wert = von;
         if(wert < 0.00)
         {
            minus = 1;
            wert = fabsl(wert);
         }
    //   test << "... wert    " << wert << endl;
    //   ... Genauigkeitsbereich prüfen ...
    //   if(wert > 1000000000.)  // 1 Mrd
    //   {
    //      test << "... Genauigkeitsbereich überschritten!" << endl;
    //      ... die Grösse wert müsste jetzt in 2 Teile aufgeteilt werden, die
    //          dann einzeln zu interpretieren sind ...
    //   }
    //   ... Nachkommastelen sind verlangt ...
         if(nk)
         {
            faktor = 1;
            for(i=0;i<nk;i++) faktor *=10;
            lvon  = (long int) wert;
            snprintf(str, 16, "%ld", lvon);
            digits = strlen(str);
            rest  = wert - (double)lvon + 0.5/faktor;  // Rundung in nk+1
            irest = rest * faktor;
            ziel = (double)lvon + (double)(irest) / faktor;
    //      test << "... faktor    " << faktor << endl;
    //      test << "... lvon      " << lvon << endl;
    //      test << "... str      :" << str << ":  " << digits << endl;
    //      test << "... rest      " << rest << endl;
    //      test << "... irest     " << irest << endl;
         }
    //   ... keine Nachkommastellen verlangt ...
         else
         {
            lvon  = (long int) wert;
            ziel = lvon;
         }
    //   ... gerundetes Ergebnis ...
         if(minus) ziel = -ziel;
         *nach = ziel;
    //   test << "--> Runden exit - ziel  " << setprecision(nk) << ziel << endl;
         return;
    }
    
    int main()
    {
      srand(time(0));
    
      double val=1.0*rand()/RAND_MAX;
      cout<<val<<endl;
      {
        double d=val;
        timespec tS;
        tS.tv_sec = 0;
        tS.tv_nsec = 0;
        clock_settime(CLOCK_PROCESS_CPUTIME_ID, &tS);
        for(long unsigned i = 0; i < 100000000; ++i)
          {
            d+=round(d,5);
            d*=0.5;
          }
        clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tS);
        cout<<"Antioptimierung: " << d<<endl;
        cout << "Time taken is: " << tS.tv_sec*1000000000+ tS.tv_nsec << endl;
    
      }	
      {
        double d=val;
        timespec tS;
        tS.tv_sec = 0;
        tS.tv_nsec = 0;
        clock_settime(CLOCK_PROCESS_CPUTIME_ID, &tS);
        for(long unsigned i = 0; i < 100000000; ++i)
          {
            double temp;
            Runden(d,&temp,5);
            d+=temp;
            d*=0.5;
          }
        clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tS);
        cout<<"Antioptimierung: " << d<<endl;
        cout << "Time taken is: " << tS.tv_sec*1000000000+ tS.tv_nsec << endl;
    
      }	
    }
    

    Ein typischer Durchlauf auf meiner Kiste ist:

    0.174169
    Antioptimierung: 0.17417
    Time taken is: 4517522501
    Antioptimierung: 0.17417
    Time taken is: 27369923064
    

    Die sehen alle so aus. Compiliert mit g++ runden.cc -Wall -Wextra -O3 -xHost -lrt



  • @SeppJ: Die Performance messe ich über die clocks und clocks per second, was bei BCB1 mit #include <time.h> eine zuverlässige Methode ist. Bei anderen Compilern gibt es sicher etwas ähnliches. Um auf einen Performance-Vergleich zu kommen, muss man jedoch alle uvergleichbaren extra Dinge in der Funktion herausstreichen wie die langsamen String-Funktionen oder sprintf für die Ermittlung der digits, was floor/floorl auch nicht macht.

    Immerhin, auch ein Faktor maximal 2 bei sonst peanuts kann ein Gewinn sein. Nur viel Zeit für die Suche danach scheint mir in diesem Fall auch Verschwendung zu sein!



  • berniebutt schrieb:

    muss man jedoch alle uvergleichbaren extra Dinge in der Funktion herausstreichen wie die langsamen String-Funktionen oder sprintf für die Ermittlung der digits

    oder der Cast auf long 🤡

    Edit:

    BCB1

    WTF? 😮 Der ist von 1997...



  • Also gut, die Funktion round() von Michael E. ist mit gleichem Ergebnis schneller. Man kann sie noch einmal beschleunigen, wenn man pow10 / pow10l statt pow / powl einsetzt. Reduktion bei 10 Mio. Einsätzen von 1.36 auf 0.87 sec. Damit wäre mit C aber Schluss bei der Jagd auf die Performance oder man setzt eine vergleichbare Standardfunktion des C-Compilers ein (habe ich nicht)!


Anmelden zum Antworten