![]() |
kaufmännisch runden
Hallo,
inder Code-Lib gibt es eine Funktion fürs kaufmännische Runden von shmia: ![]() Leider musste ich fesstellen, dass diese Funktion nicht zuverlässig funktioniert. Im folgenden Beispiel erhalte ich 2 unterschiedliche Werte (einmal wird ab- und einmal wird aufgerundet):
Delphi-Quellcode:
Der zweite Trunc-Befehl in RoundUp() gibt 0 anstatt 1 zurück.
var
x,y : Extended; resx, resy: Extended; begin y := 17.325; resy := RoundX(y,2); //ergibt 17.325 x := ((275 - 27.5) / 100) * 7 ; resx := RoundX(x,2); showmessage(FloatToStr(resx) + ' | ' + FloatToStr(resy)); end; function RoundUp(X: Extended): Extended; begin Result := Trunc(x) + Trunc(Frac(x) * 2); end; function RoundX(const Value:Extended; const nk:Integer): Extended; var multi: Extended; begin multi := IntPower(10, nk); Result := RoundUp(Value*multi) / multi; end; Ursache ist wohl die Genauigkeit von Extended. Wie lässt sich das am besten vermeiden? |
AW: kaufmännisch runden
Seine Funktion funktioniert richtig, aber deine Berechnung rechnet "falsch" => Rundungsfehler
Delphi-Quellcode:
var
x,y : Extended; resx, resy: Extended; begin y := 17.325; resy := RoundX(y, 2); // ergibt 17.325 .... nein, ergibt 17,324999999999999998... // (bzw. 17,324999999999999998265276524023 = 17,325 - 0,00000000000000000173472347597681) x := ((275 - 27.5) / 100) * 7 ; resx := RoundX(x, 2); ShowMessage(FloatToStr(resx) + ' | ' + FloatToStr(resy) + ' | ' + FloatToStr(x - y) + ' = ' + BoolToStr(x = y, True)); end; Zitat:
Delphi-Quellcode:
, läßt sich vermutlich binär nicht genau darstellen.
247.5 / 100
|
AW: kaufmännisch runden
Nicht unbedingt die überwältigende Performance, aber geht:
Delphi-Quellcode:
resx := StrToFloat(Format('%1.*f', [2, x])); Alternativ hilft manchmal auch ein Zwischenschritt über Currency:
Delphi-Quellcode:
resz := FloatToCurr(x);
resz := RoundX(resz,2); |
AW: kaufmännisch runden
Mmmmhh, das blöde ist, das mir aber 17.325 angezeigt werden wenn ich x ausgebe (oder debugge)... Kann man das irgendwo
einstellen? Den weg über Currency habe ich auch schon in Betracht gezogen. Allerdings bin ich mir nicht sicher ob ich mir damit an anderer Stelle Probleme einhandele... |
AW: kaufmännisch runden
Delphi-Quellcode:
:gruebel:
resz := FloatToCurr(x);
resz := RoundX(resz, 2); // dürfte eigentlich Folgendem entsprechen resz := RoundX(x, 4); // FloatToCurr(x); resz := RoundX(resz, 2); |
AW: kaufmännisch runden
Zitat:
Nicht ganz:
Delphi-Quellcode:
kommt eher hin.
resz := RoundX(x, 4); // FloatToCurr(x);
resz := RoundX(resz, 2); |
AW: kaufmännisch runden
Der Trick ist ganz einfach und gilt für alles was mit FLOAT Typen arbeitet (ob Delphi oder SQL Server) und liegt in der Natur des Aufbaus.
Addiere einfach einen ganz kleinen Wert zu deiner Variablen, die Du Runden willst. Also z.B.: x:=Roundx(blablub+0.00000001,2); Danach werden deine Probleme sich lösen:thumb: |
AW: kaufmännisch runden
Nur mal aus Interesse: gibt es einen plausiblen Anwendungsfall dafür, eine Fließkommazahl kaufmännisch zu runden? Mir fällt so spontan nämlich keiner ein...
|
AW: kaufmännisch runden
Praxis: jede Rechnung, Angebot oder Kassenbon ….
|
AW: kaufmännisch runden
Vielleicht will Morphie damit aber auch nur sagen, daß für kaufmännische Anwendungsfälle der Datentyp Currency die bessere Wahl ist?
|
AW: kaufmännisch runden
siehe Code-Library:
![]() |
AW: kaufmännisch runden
Zitat:
|
AW: kaufmännisch runden
Zitat:
|
AW: kaufmännisch runden
Allgemein wird in der praktischen Anwendung technisch-wissenschaftlicher Auswertung mit Augenmaß hantiert. Das bedeutet, das 3-4 (seltener 5) signifikante Stellen als vollkommen ausreichend erachtet werden, um Kenngrößen darzustellen. 3-4 Stellen bedeutet eine Genauigkeit von 2% - 0.2%. Und das entspricht im wahrsten Sinne des Wortes dem maximal möglichen Augenmaß.
Currency kann hier geeignet sein, muß aber nicht (wegen der festen 4 Nachkommastellen). Man kann schon mit Double/Extended arbeiten und die Ungenauigkeiten des Datentyps in Kauf nehmen, denn diese bewegen sich i.A. weit jenseits der geforderten Darstellungsgenauigkeit. Sofern man also keine aufwändigen Iterationen betreibt oder sich in der Atomphysik tummelt (wo mit sehr großen und sehr kleinen Zahlen hantiert wird), kann man sich auf Double verlassen. Iterationen würden die Rundungsungenauigkeiten 'nach links verschieben' und beim Rechnen mit sehr großen und (absolut) sehr kleinen Zahlen passiert das Gleiche. Man muss nur wissen, das Vergleichsoperationen immer im Kontext der Genauigkeit durchgeführt werden müssen, in der man sich gerade befindet. Bei Geldbeträgen ist die geforderte Genauigkeit 1 Cent. Ergo werde ich auf 2 Stellen nach dem Komma runden und Vergleiche so formulieren, das die Ungenauigkeit von 0.5 ct berücksichtigt wird. Also ist ein Wert 0.00$, wenn er kleiner als 0.005$ ist (absolut), und A>B genau dann, wenn A-B > 0.005 ct ist usw. Das reicht vollkommen aus. Bei der Berechnung von Zinsen, Steuern usw. wird gemäß den Vorgaben des Finanzamts auf Centbeträge gerundet. Wenn nach dem Runden die o.g. Vergleichslogik angewandt wird, kann nichts passieren. Pfennigfuchser können noch den arnof-Trick verwenden, der zwar innerlich wehtut, aber genau die datentypbedingte Ungenauigkeit bedingt, mit der man (im Übrigen in allen Programmiersprachen, die Fließkommazahlen verwenden) leben muss. Eine Alternative dazu ist der Datentyp 'Currency', der jedoch einen limitierten Wertebereich besitzt und fest vier Nachkommastellen hat. Dafür rechnet man damit punktgenau. Für jegliche Geldberechnungen sollte dieser Datentyp ausreichen, denn 50 Stellen reichen für sämtliches auf der Welt befindliche Geld, egal in welcher Währung. Allgemein gesehen wäre der BCD-Datentyp hier ideal, aber soweit ich das sehe, existiert hier kein nativer Datentyp in Delphi (in C# existiert 'decimal'). Allerdings kann man sich mit Klassen behelfen, die mit beliebig großen Zahlen umgehen können. |
AW: kaufmännisch runden
Ich hatte vor einiger Zeit auch einen lästigen Rundungsfehler und
![]() |
AW: kaufmännisch runden
Egal welche Datentypen man verwendet oder das benutzte System bietet, wenn man eine Kleinigkeit hinzuaddiert, so bekommt man das Floatproblem in den Griff.
Zur Info für die Allgemeinheit: alle Floattypen sind nur Näherungswerte und aus 0.015 (was gerundet dann 0.02 währe kann schon mal im echten Leben folgendes werden: 0.014999999 was gerundet nun mal 0.01 ergibt. Das ist schon großen Firmen passiert :lol: |
AW: kaufmännisch runden
Vielen Dank an alle die Licht ins Dunkel gebracht haben!
Ich habe mich jetzt erstmal für die Lösung von MrSpock entschieden. Merke: 1 ist nicht immer gleich 1 ;) |
AW: kaufmännisch runden
Zitat:
Ja, wenn 0.015 rauskommen sollte, das aber durch 0.01499999 approximiert wird, dann passt die Lösung mit +epsilon. Was aber wenn das Ergebnis der Rechnung 0.01499999 ist? Dann wird das Ergebnis verfälscht, weil auf 0.02 gerundet wird, obwohl 0.01 korrekt wäre. Egal unter welchen Umständen man Gleitkommazahlen verwendet, es wird immer Probleme geben wenn man genaue Kommastellen braucht. Die einzige Lösung ist, einen für das Problem passenden Datentyp zu verwenden. (Siehe auch Furtbichlers Antwort) |
AW: kaufmännisch runden
Zitat:
Also ich habe noch keine Rechnung mit 0.01499999 gesehen. Ich mache das schon 25 Jahre und weiß wovon ich spreche. Man kann diesen Tipp annehmen oder auch nicht! Wenn man das nicht so macht, so kann ich Dir versprechend, das in bestimmten Branchen Dir die Software um die Ohren fliegt, wenn der Kunde anfängt nachzurechnen! Natürlich ist die große Kunst nur an den richtigen Stellen zu runden, den gerundete Werte sind falsche Werte ;-) |
AW: kaufmännisch runden
Zitat:
Zitat:
Zitat:
Ein +epsilon beim Runden eliminiert nicht die Ursache des Problems, sondern deckt nur die Symptome in den häufigsten Fällen ab. Zitat:
|
AW: kaufmännisch runden
@JasonDX: hat Du hier die Frage gestellt ?
Wenn Du einen Sinnvollen Beitrag hast, dann schreibe Ihn. Wenn Du meinst, das mein Beitrag falsch ist, dann hoffe ich für deine Kunden, das Du nichts mit WAWIS/ERP-System usw in der Entwicklung zu tun hast! |
AW: kaufmännisch runden
Zitat:
Die zweite Frage im Beitrag war die was passiert, wenn eine Lösung verwendet wird, welche alle Fälle korrekt abdeckt. Ich bin noch keine 25 Jahre dabei, insofern ist es durchaus interessant zu wissen, wieso eine Lösung der anderen vorgezogen wird. |
AW: kaufmännisch runden
Jeder muss wissen wie er es löst, den Gedanken was dazuzurechnen ist erstmal ungewöhnlich und macht sicher auf den ersten Blick komisch und mag falsch erscheinen. Andere Datentypen zu nehmen ist sicher auch eine Lösung, ich möchte mich aber nicht nur auf EMBA verlassen, manchmal ist man da halt auch verlassen 8-)
Ich kann nur aus meiner Erfahrung sagen, da ich mich in meinen Programmiererleben sicher viele Monate damit beschäftigt habe. Vor dem runden einfach 0.000000000001 oder sowas in der Art addieren, dann klapp es auch mit dem Runden auf 2 Stellen :thumb: |
AW: kaufmännisch runden
Hallo,
habe normalerweise nicht viel mit Fließkommaoperationen zu tun, aber vor Jahren einen Code aus der Computersteinzeit (COBOL) gesehen. Dort wurden für Beträge erstmal grundsätzlich nur ganze Zahlen gespeichert, also z.B. Beträge in Cent. Bei jeglicher Prozentrechnung oder Division wurde dem Ergebnis 0.5 hinzuaddiert und dann der Nachkommaanteil einfach abgeschnitten. Hat wohl zufriedenstellend funktioniert (stammte angeblich aus der Versicherungsbranche). Den Code habe ich leider nicht mehr, erinnere mich aber noch gut an diese seltsame Methode ... hth |
AW: kaufmännisch runden
Weil es im weitesten Sinne zum Thema passt:
![]() |
AW: kaufmännisch runden
@billa:
Das von Dir beschriebene Verfahren ist nicht ungewöhnlich, in COBOL gibt es aber die Ergänzung "ROUNDED" für Multiplikationen, Divisionen und "Computations", die so etwas automatisch erledigen. Die Rundung nach ähnlicher Methode habe ich selbst in kaufmännischen Anwendungen selbst schon in C++ und in Assembler benutzt. @arnof, JasonDX: Rechnungen mit Fließkommazahlen sind nunmal ungenau und deshalb generell nicht für kaufmännische Zwecke geeignet. Das Addieren von einem noch so kleinen "epsilon" ist und bleibt ein Hack. Wenn einem kein "Dezimal"-Datentyp zur Verfügung steht, ist es besser, bei COBOL abzugucken und alle Rechnungen mit Ganzzahlarithmetik durchzuführen - die ist nämlich genau und man hat Rundungsvorgänge selbst in der Hand. |
AW: kaufmännisch runden
Wie man wo rundet, ist vermutlich auch anwendungsspezifisch.
Bei durch Menschen nachvollziehenden Rechnungen, kann ich mir gut vorstellen, dass das Runden nach jeder Operation (Zinsen berechnen) passiert und das Auf-/Abrunden durch Verträge geregelt ist. Wenn ein komplexes Modell eine bestimmten Erwartungswert für die interne Verwendung ausspucken soll, wird vielleicht nur am Ende gerundet. Das Runden durch Addieren von 5 auf der ersten nicht-signifikanten Stelle finde ich nicht so ungewöhnlich. Im Grunde realisiert man damit das normale Runden auf eine bestimmte Stelle ohne irgendwelche Sprünge. |
AW: kaufmännisch runden
Zitat:
Das Runden hat man selbst im griff, wenn man eine eigene Rundungfunktion generell benutzt, was ich immer mache. Es gibt ja auch andere Länder in denen komplett anders zu Runden ist:Beispiel Schweiz, da ist die kleinste Einheit 5 Rappen :wink: |
AW: kaufmännisch runden
Zitat:
Zitat:
|
AW: kaufmännisch runden
Hallo,
irgendwie kehrt die Diskussion über Rundung immer wieder auf. Hier muss man wohl unterscheiden was gerundet werden soll und nach welcher Regel. Mathematisch, Kaufmännisch oder Banker's Rounding. Und ob es ein positiver oder negativer Betrag ist. Auch in der kaufmännischen Rundung spielt die 4 oder 5 Stelle nach dem Komma durchaus eine Rolle, wenn man mal Zinsdifferenzgeschäfte bei einer Bank betrachten würde. Im "Tante Emma" Laden eher weniger wenn's um "drei fünfundneunzig" geht... Ganz gut im kaufmännischen Bereich bin ich mit folgender Funktion gefahren (hab ich mal aufgeschnappt oder zusammengestrickt oder wie auch immer.. ist lange her, daher kenne ich auch den Urheber nicht mehr der den Ansatz/Lösung gebaut hat, also nicht meine Federn...)
Code:
HIH
function RoundX( value: extended; const n: integer): extended;
var exp: extended; mode: TFPURoundingMode; begin value := StrToFloat(FloatToStr(value)); mode := GetRoundMode; exp := IntPower(10, n); if value > 0 then begin SetRoundMode(rmUp); result := trunc(value * exp + 0.5) / exp; end else begin SetRoundMode(rmDown); result := trunc(value * exp - 0.5) / exp; end; SetRoundMode(mode); end; |
AW: kaufmännisch runden
Zitat:
Zitat:
![]() Deine Fragestellung ist mir etwas zu allgemein gehalten, denn als Anfänger mit nur lückenhaften Grundkenntnissen hat man eigentlich immer mit Problemen zu kämpfen. Da kommt man nicht drumrum, da muß man hindurch. Zitat:
|
AW: kaufmännisch runden
Zitat:
Aaaber: In Delphi geht das kaum anders (bei Geld gibts -zum 1000x- den 'Currency' Datentyp, der mit einigen Datenbanken kompatibel is, sowit ich weiß) Schau dir mal (aka bilde dich weiter ;-) ) Artikel über Floating Point, Fließkommazahlen usw. an. Das ist das gängige Format, indem Zahlen mit Dezimalanteil verarbeitet werden. Wieso gängig? Weil die Operationen darauf, also +,-,*,/, Sin etc. hochoptimiert durchgeführt werden können. Pferdefuß: Damit können so "einfache" Zahlen wie "0,1" genaugenommen nicht dargestellt werden, dafür aber (z.B.) so wichtige Zahlen wie "0,09999999999643" und "0,100000000000367". Warum das so ist, wirst Du sehen, wenn Du die entsprechenden Artikel liest. Beide Zahlen, also die 0,09999... und die 0,10000.. werden aber beim Debuggen in Delphi als 0,1 angezeigt (gemein, was?). Ein Vergleich mit "0,1" ergibt dann aber FALSE. Und ein Vergleich auf ">0,1" unter umständen auch. Und obwohl Delphi im Debugger die Zahl vielleicht als 0,1 anzeigt, wird sie in der Datenbank als 0,99999999999999643 abgelegt (oder eben als 0,1000000000000367). Aber so etwas will man ja nicht unbedingt auf einer Rechnung sehen, oder als Preisschild im Supermarkt (die wären ganz schön lang, die Teile): Also muss man runden. Das muss man sowieso, wenn man prozentuale Anteile (Rabatt, VAT usw) ausrechnet. So, und beim Runden muss man dann erstens 0,005 dazuaddieren, damit man kaufmännisch rundet und dann blöderweise nochmals 0,0000000000001 um diesem Format ein Schnippchen zu schlagen. Jeder Delphi-Programmierer geht da durch und stellt die gleichen Fragen. Und jeder gute Delphi-Programmierer weiß darauf irgendwann die Antwort. Ich gehe davon aus, das Du einer derjenigen sein wirst, die dem nächsten Rookie das ganz genau erklären kann. Und für Leute, die erst Fahrrad fahren lernen ist es echt nervig, von den Fahrradfahrern, die an einem vorbeifahren, erklärt zu bekommen, wie leicht das doch ist. |
AW: kaufmännisch runden
Zitat:
1,344+0,005=1,349 letzte Stelle streichen => 1,34 Was ist daran befremdlich? Du willst einen gerundeten Wert bekommen, der sieht Deinem ursprünglichen Wert nur ähnlich hat aber ansonsten garnichts mit diesem zu tun! Das ist nur für die Darstellung. Zitat:
Mein Tip dazu: wenn 16Bit reichen sollten, dann nimm 32Bit weil es kommt immer mehr als man glaubt. Gruß K-H |
AW: kaufmännisch runden
![]() |
AW: kaufmännisch runden
Zitat:
An allem ist Delphi schuld. :roll: Man kann natürlich alles umständlich selber machen, oder ![]() ![]() |
AW: kaufmännisch runden
Jetzt mal ernsthaft
gerundet werden soll auf 2 Stellen hinterm Komma, gegeben ist 0,44444444444444444444444445 was kommt raus? 0,45 oder 0,44? Gruß K-H |
AW: kaufmännisch runden
0,44 ... egal ob mathematisch oder kaufmännisch gerundet
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 14:13 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz