Wie runde ich in C?
-
math.h oder cmath total egal! Mit dem winzigen Unterschied, das cmath im namespace std liegt. Klasse das bringts echt wenn ich using namespace std mache!
Ist wie die Frage: "Was ist besser Gauda oder Mortadella?" Käse ist und bleibt Käse.
Die Tatsache das cmath in c++ beliebter ist heißt nicht dass man da jedesmal drauf rumreiten muss. Wenn du weitere von mir unbekannte Unterschiede kennst, dann nenne sie und sage nicht nur "nein".
Seppel schrieb:
seine Frage war aber: "Wie rundet man in C"
Er weiß bloß nicht, dass er C++ macht. Sein erstes Codebeispiel hat dies aber deutlich gemacht.Ja ich weiß siehe Thread vor verschieben.
-
HighLigerBiMBam schrieb:
Wenn du weitere von mir unbekannte Unterschiede kennst, dann nenne sie und sage nicht nur "nein".
cmath steht im Standard. math.h gibts bei dir nur, weils dein Compiler gut mit dir meint.
-
Michael E. schrieb:
HighLigerBiMBam schrieb:
Wenn du weitere von mir unbekannte Unterschiede kennst, dann nenne sie und sage nicht nur "nein".
cmath steht im Standard. math.h gibts bei dir nur, weils dein Compiler gut mit dir meint.
Oder weil er auch einen C Compiler installiert hat (die beiden kommen ja meistens im Doppelpack) und diese sich ein Includeverzeichnis teilen.
-
Auch wenn man das von einigen Compiler-Funktionen (vorhanden ja/nein?) machen kann, stelle ich einmal eine seit langem dafür eingesetzte allgemeine Funktion runden() bereit. Diese findet sich auf meiner HP unter http://berniebutt.npage.de unter Programmieren -> Downloads.
-
berniebutt schrieb:
Auch wenn man das von einigen Compiler-Funktionen (vorhanden ja/nein?) machen kann, stelle ich einmal eine seit langem dafür eingesetzte allgemeine Funktion runden() bereit. Diese findet sich auf meiner HP unter http://berniebutt.npage.de unter Programmieren -> Downloads.
Ich kann nur vor diesem Code warnen.
// Runden 06.11.2010 // Dann gilt die Ausrede "war früher so" schonmal nicht #include <iomanip.h> // gibts nicht, brauchst du auch nicht #include <iostream.h> // gibts nicht, brauchst du auch nicht außer für deine Debug-Ausgabe #include <fstream.h> // gibts nicht, brauchst du auch nicht #include <stdlib.h> // Ach, es soll C sein. Dann gibts die vorherigen drei Header erst recht nicht. void Runden(DOUBLE von,DOUBLE *nach,int nk) // Warum nicht als Return-Wert? Warum Zeiger? // -------------------------------------------------------------------------- // Fliesskommazahl (double) runden // -------------------------------------------------------------------------- // von übergebene Fliesskommazahl // *nach Zeiger auf gerundete Fliesskommazahl // nk Anzahl der Nachkommastellen // Warum signed? // 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) // wunderschön // 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! // WTF? Absolut unnötige Einschränkung, die durch deine komische Berechnung kommt. Desweiteren stimmt deine Berechnung nur für 32-bit-long, also z. B. nicht auf nem 64-Bit-Linux. // -------------------------------------------------------------------------- { long int lvon; int irest,i,faktor,minus=0; int digits; DOUBLE wert,rest,ziel; char str[10]; // hier gibts dann nen schönen Überlauf bei 64-Bit-long // test << "--> Runden - von-nk " << von << " " << nk << endl; // Streams? Eindeutig C++. // ... positiver oder negativer Wert? ... wert = von; if(wert < 0.00) { minus = 1; wert = fabsl(wert); // fabsl gibts in C++ nicht => eindeutig C } // test << "... wert " << wert << endl; // ... Genauigkeitsbereich prüfen ... // if(wert > 1000000000.) // 1 Mrd // Tolle Condition: selbst bei 32 Bits kannst du die Schranke mehr als verdoppeln. Aber ist ja nur ne Debug-Ausgabe... // { // 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) // if(nk > 0), da nk signed { faktor = 1; for(i=0;i<nk;i++) faktor *=10; lvon = (long int) wert; // absolut unnötig, hier Ganzzahlen zu verwenden ltoa(lvon,str,10); // nochmal absolut unnötig, hier ein char-Array zu verwenden, das dabei noch viel zu klein ist; achja, ltoa gibts in C++ nicht 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; }
Bei der weiteren Berechnung hatte ich keine Lust mehr, mir zu überlegen, was da alles wegen der Fließkommaeigenschaften schiefgehen kann.
Was soll überhaupt der ganze Code? Was spricht gegen sowas? Ist standardkonform und klappt sogar bei Zahlen > 1E10
#include <cmath> 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; }
-
Übrigens: Wenn ich decPlaces bei mir signed mache, kann ich sogar auf Stellen vor dem Komma runden. Dein Parameter ist zwar signed, aber für alle negativen Eingaben verhält es sich so, als ob 0 übergeben worden wäre.
-
@Michael E.: Erfrischend und lehrreich deine kritisiche Analyse meines Codes.
Bedenke bitte:
- Funktion ist aus alten FORTRAN-Zeiten geholt und lediglich auf C angepasst
- das Datum einer Source-Datei bezieht sich bei mir immer auf den Tag der letzten Editierung
- signed und unsigned gab es bei FORTRAN nicht, ist auch nicht wichtig
- die C++ streams sind irrelevant und auskommentiert, also reines C
- <cmath> kennen ältere C-Compiler nicht, also nicht verwendet
- die Funktion läuft auch mit negativen Werten sauber
- wie man ein Ergebnis zurückgibt (Zeiger oder return) bleibt Geschmackssache
- mit 64-bit habe ich mehr als mit 32-bit. Ansonsten bleibt alles gleich, nur in anderen GültigkeitsbereichenJeder mag das machen wie er will. Der Fragesteller wusste aber nicht wie. Wenn dein Vorschlag besser ist,
soll er ihn nehmen. Ich denke jedoch, in der Zuverlässigkeit und in der Performance gibt es kaum
nennenswerte Unterschiede.Und noch ein wichtiger Hinweis: im Internet als open-source bereitgestellte Source-Vorschläge gehören im Originaltext
wiedergegeben! Ergänzungen oder Verbesserungen sind erwünscht, Kürzungen oder Verstümmelungen dagegen nicht!
-
berniebutt schrieb:
Bedenke bitte:
- Funktion ist aus alten FORTRAN-Zeiten geholt und lediglich auf C angepasst
- das Datum einer Source-Datei bezieht sich bei mir immer auf den Tag der letzten Editierung
- signed und unsigned gab es bei FORTRAN nicht, ist auch nicht wichtig
[...]
- <cmath> kennen ältere C-Compiler nicht, also nicht verwendetmath.h ist seit 1990 C-Standard. Ich will gar nicht beurteilen, wieviel Sinn der Code vor meiner Geburt in Fortran gemacht hat, aber heute ist er ... nicht mehr angebracht, um es freundlich auszudrücken.
- die Funktion läuft auch mit negativen Werten sauber
Wenn man unter "sauber laufen" versteht, dass es keinen Unterschied macht, ob man 0, -1 oder -10 übergibt...
- mit 64-bit habe ich mehr als mit 32-bit. Ansonsten bleibt alles gleich, nur in anderen Gültigkeitsbereichen
str ist zu klein. Aber die Berechnungen sind ja eh nur für die Debug-Ausgabe (weshalb sind sie nicht auskommentiert?).
Ich denke jedoch, in der Zuverlässigkeit und in der Performance gibt es kaum
nennenswerte Unterschiede.Ich behaupte kühn das Gegenteil. Zur Zuverlässigkeit: siehe 1E10. Zur Performance: sollte klar sein mit ltoa etc.
Und noch ein wichtiger Hinweis: im Internet als open-source bereitgestellte Source-Vorschläge gehören im Originaltext
wiedergegeben! Ergänzungen oder Verbesserungen sind erwünscht, Kürzungen oder Verstümmelungen dagegen nicht!Entschuldige. Das Einzige, was ich weggekürzt habe, ist
// --------------------------------------------------------------------------- // Runden 06.11.2010 // Autor: Dr. Bernd Haendel, Hercegszántó / HU // ---------------------------------------------------------------------------
Ich bin davon ausgegangen, dass du was dagegenhaben könntest, wenn ich deinen Namen mit deinem Forenaccount in Verbindung bringe, vor allem da ich deinen Code kritisiere.
-
@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.
-
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 nunmath.h
einbindet oder nicht ist unwichtig.
-
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 nunmath.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?"
-
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)!