Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi kaufmännisch auf 0,5 oder 1 runden (https://www.delphipraxis.net/114161-kaufmaennisch-auf-0-5-oder-1-runden.html)

juergen 20. Mai 2008 20:28


kaufmännisch auf 0,5 oder 1 runden
 
Hallo zusammen,

ich versuche nun schon seit einiger Zeit ein bestimmtes Rundungsergebnis zu erreichen.
Habe auch schon vieles hier gelesen und auch ausprobiert, aber ich konnte mein Problem damit leider noch nicht selbstständig lösen. :oops:
Generell benötige ich eine Rundung, wo als Ergebnis immer nur eine Null oder eine 5 hinter dem Komma stehen soll; abhängig von dem Ergebnis der kaufmännischen Rundung.
Quasi in 0,5er Einheiten kaufmännisch runden, anstelle der gewohnten 1er Einheiten.
Beispiele:
12,2499 -> 12,0
12,25 -> 12,5
12,49 -> 12,5
12,5 -> 12,5
12,74 -> 12,5
12,75 -> 13,0
13,24454645 -> 13,0

Könnte mir hier jemand weiterhelfen?

Vielen Dank schon mal im Voraus!

Hansa 20. Mai 2008 20:33

Re: kaufmännisch auf 0,5 oder 1 runden
 
Die 12,50 ist ein Knackpunkt. Aber das hier auch ? :shock:

Zitat:

Zitat von juergen
12,49 -> 12,5


JasonDX 20. Mai 2008 20:37

Re: kaufmännisch auf 0,5 oder 1 runden
 
Delphi-Quellcode:
round(x*2)/2
müsste hinhaun ;)

greetz
Mike

juergen 20. Mai 2008 21:12

Re: kaufmännisch auf 0,5 oder 1 runden
 
@Hansa,
ja, so soll/ muss es sein sein :wink:

@JasonDX,
Round hatte ich hier bei meiner kaufmännischen Rundung auf Grund des Verhaltens (Bankers Rounding) gar nicht in Betracht gezogen.
Dein Vorschlag funktioniert schon fast perfekt, vielen Dank! :thumb:
Allerdings wird z.B. bei 12,25 auf 12 abgerundet, ich benötige allerdings genau ab 12,25 das Ergebnis -> 12,5, also eine Aufrundung.
Als Workaround könnte ich 0.0000000001 vor dem Round hinzuzählen...

Hat noch jemand einen Lösungsansatz?

grenzgaenger 20. Mai 2008 21:16

Re: kaufmännisch auf 0,5 oder 1 runden
 
Zitat:

Zitat von juergen
12,74 -> 12,5
12,75 -> 13,0

kaufmännisch runden? :gruebel: :gruebel: :gruebel:

sag mal, was und wie willste genau runden? nur kaufmännisch runden, ist das nicht... :pale: :pale:

juergen 20. Mai 2008 21:37

Re: kaufmännisch auf 0,5 oder 1 runden
 
@grenzgaenger,
du hast Recht, das richtige kaufmännische Runden ist es natürlich nicht.
Ich möchte im Prinzip in 0,25er Schritten kaufmänisch runden, anstelle in 0,5er Schritten.

Mein Anwendungsfall:
Ermittlung von Urlaubsanspruch beim Arbeitsbeginn im lfd.Jahr. :wink:
Es gibt u.a. die Regelung, dass der Urlaubsanspruch kaufmännisch auf halbe Tage gerundet wird.
Wenn dann z.B. ein neuer Mitarbeiter am 15.Mai anfängt und er einen Ganzjahres-Urlaubsanspruch von 28 Tagen hat, so ergäbe das einen Urlaubsanspruch für dieses "angebrochene" Jahr von 17,672131147541 Tagen, "kaufmännisch" auf halbe Tage gerundet wären es dann 17,5 Tagen.

Der Knackpunkt ist eben das kaufmännische Runden auf halbe Tage.

grenzgaenger 20. Mai 2008 21:53

Re: kaufmännisch auf 0,5 oder 1 runden
 
willst du jetzt in halbe schritte runden (x := trunc(x * 2) / 2) oder in 1/4 (x := trunc(x * 4) / 4)?

PS: ggf. kannst du noch 'n hundertstel dazu zählen... oder round hernehmen

Marc. 25. Mai 2008 14:09

Re: kaufmännisch auf 0,5 oder 1 runden
 
Hi juergen!
Was hälst Du denn von einer simplen If-Abfrage?
Delphi-Quellcode:
function myround(z: real): real;
var r: real;
begin
   r := frac(z);

   if (r >= 0.25) and (r < 0.75) then result := trunc(z) + 0.5 else
   if (r >= 0.75)               then result := trunc(z) + 1   else
                                      result := trunc(z);
end;
Cheers,
Marc

[edit] Funktion korrigiert [/edit]

Der.Kaktus 25. Mai 2008 14:11

Re: kaufmännisch auf 0,5 oder 1 runden
 
Altes kaufm. Runden geht mit "Wert *50 /100" (wennch mich richtig erinner*g*)

grenzgaenger 25. Mai 2008 14:35

Re: kaufmännisch auf 0,5 oder 1 runden
 
:-)

fast hat normal immer das selbe schema

abschneiden(betrag * faktor) / faktor

wenn aufgerundet werden soll, wird dann noch 'n kleiner betrag hinzugezählt, damit aufgerundet wird, bevor die die stellen abgeschnitten werden...

also

abschneiden(betrag * faktor + addon) / faktor

bei einem hundertstel (zwei dezimalstellen), x = trunc(23,501 * 100 + 0.5) / 100 --> 2350,1 + 0.5 --> 2550,6 --> 2550 --> 25,50

auf diese weise durfte man immer in basic runden... ;-)

bei rundung aufs fünferl, ist der faktor 20 ... :-)

juergen 25. Mai 2008 16:03

Re: kaufmännisch auf 0,5 oder 1 runden
 
Hallo zusammen,

im Moment verwende ich nachfolgendes, weil am genauesten:
JasonDX's Vorschlag:
round((StrToFloat(s) * 2 + 0.00000000000001)) / 2) Edit1.Text:= '12,4999999999999999999' -> 12,5 -> erst ab der 19.Stelle falsch

Marc seine Funktion:
myround(StrToFloat(Edit1.Text)) Edit1.Text:= '12,2499999999999999' -> 12,5 -> ab der 16.Stelle falsch

grenzgaenger seine Funktion:
trunc((StrToFloat(Edit1.Text)) * 2 + 0.5) / 2 Edit1.Text:= '12,4999999999999999' -> 12,5 -> ab der 16.Stelle falsch

Prinzipiell lässt sich diese Ungenauigkeit mit diesem Konzept von Stringkonvertierung und Real wohl nicht lösen.
Ich vermute man müsste expliziet nur mit Integer und dessen Funktionen arbeiten um genauere Ergebnisse zu erreichen.

Da ich keine Summierungen verwende wo sich dann die evtl. Ungenauigkeiten summieren könnten, kann ich damit leben.

Danke an alle für die Unterstützung! :thumb:

grenzgaenger 25. Mai 2008 16:12

Re: kaufmännisch auf 0,5 oder 1 runden
 
wenn du genaue ergebnisse haben möchtest, darfst du keine gleitkommazahlen (single, double, real, extented, float, etc. pp.) verwenden. hier kommen nur integer werte in betracht. also, z.b. integer, word, longint, int64, currency.

weshalb rechnest du die zahlen nicht erst in ein datenformat um, welches auch einigermassen valide ist? ansonsten hast du immer die probleme, dass sie in die eine oder andere richtung, willkürlich, kippen. damit wirst du nie glücklich werden...

wenn du auf assembler ebene schaust, schau dir mal "binary coded decimal" (BCD) an. das format wurde extra erfunden um die rundungsprobleme zu vermeiden.. die CPU hat hierfür auch einen extra modus ... :-) (COBOL kann das von haus auf..., 'ne bilanz macht einfach keinen spass, wenn aktiva und passiva nicht aufgehen ;-) )

Der.Kaktus 25. Mai 2008 16:19

Re: kaufmännisch auf 0,5 oder 1 runden
 
Zitat:

Zitat von juergen
Hallo zusammen,

im Moment verwende ich nachfolgendes, weil am genauesten:
JasonDX's Vorschlag:
round((StrToFloat(s) * 2 + 0.00000000000001)) / 2) Edit1.Text:= '12,4999999999999999999' -> 12,5 -> erst ab der 19.Stelle falsch

Marc seine Funktion:
myround(StrToFloat(Edit1.Text)) Edit1.Text:= '12,2499999999999999' -> 12,5 -> ab der 16.Stelle falsch

grenzgaenger seine Funktion:
trunc((StrToFloat(Edit1.Text)) * 2 + 0.5) / 2 Edit1.Text:= '12,4999999999999999' -> 12,5 -> ab der 16.Stelle falsch

Prinzipiell lässt sich diese Ungenauigkeit mit diesem Konzept von Stringkonvertierung und Real wohl nicht lösen.
Ich vermute man müsste expliziet nur mit Integer und dessen Funktionen arbeiten um genauere Ergebnisse zu erreichen.

Da ich keine Summierungen verwende wo sich dann die evtl. Ungenauigkeiten summieren könnten, kann ich damit leben.

Danke an alle für die Unterstützung! :thumb:

haste kein Vertrauen zur primitivsten Version? *sfg* s.o.

grenzgaenger 25. Mai 2008 16:27

Re: kaufmännisch auf 0,5 oder 1 runden
 
Zitat:

Zitat von Der.Kaktus
haste kein Vertrauen zur primitivsten Version? *sfg* s.o.

denke, das hat nix mit vertrauen zu tun, so lange er keinen datentyp nimmt, welcher keine rundungsfehler erzeugt, wird er immer probleme haben. nicht umsonnst sollte man die floats vermeiden... ;-)

Der.Kaktus 25. Mai 2008 16:31

Re: kaufmännisch auf 0,5 oder 1 runden
 
Zitat:

Zitat von grenzgaenger
Zitat:

Zitat von Der.Kaktus
haste kein Vertrauen zur primitivsten Version? *sfg* s.o.

denke, das hat nix mit vertrauen zu tun, so lange er keinen datentyp nimmt, welcher keine rundungsfehler erzeugt, wird er immer probleme haben. nicht umsonnst sollte man die floats vermeiden... ;-)

na da sagen wir mal nix gelle ? ;-)

grenzgaenger 25. Mai 2008 16:35

Re: kaufmännisch auf 0,5 oder 1 runden
 
sehe grad, dass delphi einen eigenen BCD datentyp mit an board hat :-)
Delphi-Quellcode:
TBcd

grenzgaenger 25. Mai 2008 16:37

Re: kaufmännisch auf 0,5 oder 1 runden
 
Zitat:

Zitat von Der.Kaktus
Zitat:

Zitat von grenzgaenger
Zitat:

Zitat von Der.Kaktus
haste kein Vertrauen zur primitivsten Version? *sfg* s.o.

denke, das hat nix mit vertrauen zu tun, so lange er keinen datentyp nimmt, welcher keine rundungsfehler erzeugt, wird er immer probleme haben. nicht umsonnst sollte man die floats vermeiden... ;-)

na da sagen wir mal nix gelle ? ;-)

wenn juergen das für sich privat macht, ist das in ordnung. wenn allerdings kunden sein programm nehmen sollen, wird es ihm irgendwann um die ohren fliegen, wenn er mit groben approximationen arbeitet, wie im vorliegenden post... aber man gönt sich ja sonst nix ... :-)

Der.Kaktus 25. Mai 2008 16:48

Re: kaufmännisch auf 0,5 oder 1 runden
 
@grenzgaenger

Jep, son Rechnungslegungsprogramm, was net richtig rundet, kommt net so gut beim Kunden.*fg*

bit4bit 25. Mai 2008 19:23

Re: kaufmännisch auf 0,5 oder 1 runden
 
Grundsätzlich würde ich das ursprüngliche Problem so lösen :

Delphi-Quellcode:
xr := trunc(x * 20 + 5) / 20

Dein Anwendungsfall muss aber IMHO anders gelöst werden!

Laut Gesetz ...

BUrlG § 5 Teilurlaub

Absatz 2 : Bruchteile von Urlaubstagen, die mindestens einen halben Tag ergeben, sind auf volle Urlaubstage aufzurunden.

... werden also Werte von x,5 bis x,999999... auf x+1 aufgerundet !

Von kaufmännischem Runden ( schon gar nicht auf halbe Tage ) steht da nix.

Man kann dem Arbeitnehmer natürlich mehr Urlaub geben als ihm eigentlich zusteht, wenn z.B. einen Anspruch von x,000000001 bis x,499999... auf x,5 aufgerundet würde:

Delphi-Quellcode:
xr := trunc(x * 20 + 9) / 20
Gesetzlich müsste man tatsächlich z.B. nur 0,1 Tage Urlaub gewähren *lol*

Übrigens: Dein Beispiel mit dem 15. Mai ergibt nur einen Urlaubsanspruch von 16,333... Tagen und nicht 17,672131147541 Tage , weil angebrochene Monate nicht zählen!

juergen 25. Mai 2008 20:32

Re: kaufmännisch auf 0,5 oder 1 runden
 
Liste der Anhänge anzeigen (Anzahl: 1)
@ bit4bit,
8)
Für deine Beispiele hast du Recht.
Da mein "Urlaubsrechner" aber nicht nur für Angestellte/ Beamte gelten soll, sondern vorwiegend im industriellen Bereich, musste ich alle möglichen Varianten abdecken...
Zur Veranschaulichung habe ich das Programm mal angehängt.

Ps.:
Angewandtes Tarifrecht...
Es ist erstaunlich wieviel Spielraum/ Interpretation Gesetzestext in der Praxis so zulässt.

grenzgaenger 25. Mai 2008 22:11

Re: kaufmännisch auf 0,5 oder 1 runden
 
Zitat:

Zitat von juergen
Ps.: Es ist erstaunlich wieviel Spielraum/ Interpretation Gesetzestext in der Praxis so zulässt.

alles was den mitarbeiter nicht schlechter stellt, als das gesetz, ist erlaubt. sofern ich das noch recht in erinnerung habe :-)

solltest aber dennoch gucken, dass du deinen auftraggeber nicht, aufgrund eigenwilliger stundenrundung, verärgerst. auch wenn es zum vorteil gereicht, irgendjemand fühlt sich sicherlich auf die füsse getreten und macht stress. von daher, gilt der tip nach wie vor, lieber vorher runden, z.b. alles in dezimalminuten eingeben lassen oder in zeitminuten und sich das anschliessend bestätigen lassen. aufgrund der bestätigten daten, wird dann ohne rundung weitergerechnet. dann kann sich niemand beschweren, dass es anschliessend nicht passt :-)

denke, die manuelle rundungsmethode (mit trunc...) habe ich vorhin genügend ausgeführt, so dass du auch die exotischsten rundungen problemlos hinbekommst. wenn deine zeit sekundengenau sein sollen, siehe dir doch mal das unix datetime format an... ansonsten kannst du ja problemlos 'n einges kreieren auf der basis deiner kleinsten zu berücksichtigten zeiteinheit... denke longint oder int64 sollten eigentlich reichen... sonst der currency typ :-)

viel erfolg GG

juergen 25. Mai 2008 23:37

Re: kaufmännisch auf 0,5 oder 1 runden
 
Hallo grenzgaenger,
naja, die Aufgabenstellung war in diesem Fall eindeutig:
- alles bezieht sich nur auf Tage. Ergebisse immer in ganze Tage oder halbe Tage (je nach Firma).
Mein Problem war ja, die Personalsachbearbeiterin rechnet mit dem Taschenrechner nach und erhält ein
ganz einfaches Ergebnis -> z.B. 12,25. Das soll dann auf 12,5 gerundet werden.
Da wurde in meinem Fall durch die verwendeten Datentypen "falsch" gerechnet. Die jetzige Ungenauigkeit (16 Stellen hinter dem Komma) kann man aber in meienm Fall vernachlässigen, Ergebnis soll ja immer nur halbe oder ganze Tage sein.

Es gibt bei einigen Firmen auch noch die Urlaubbsberechnungen in Stunden:Minuten (allerdings ziemlich selten).
Hierfür hätte ich was in petto, weil ich mich schon mal ausführlich mit Stunden- und Minutenberechnungen auseinander gesetzt hatte.
Dort verwende ich auschließlich Integer als Basis (2:30 Std = Integerwert von 150).

Deine Erläuterungen zur Trunc-Funktion/Berechnung waren übrigens sehr gut!

Gute Nacht!

alzaimar 26. Mai 2008 07:16

Re: kaufmännisch auf 0,5 oder 1 runden
 
So ist es allgemein:
Delphi-Quellcode:
Function RoundToGranularity(aValue, aGranularity: Double): Double;
Begin
  Result := Trunc(aValue / aGranularity + 0.5 + 1E-10) * aGranularity
End;
In deinem Fall ist 'aGranularity' = 0.50.

Die Ergebnisse sind zwar (weil Floating-Point) nicht genau, aber die Darstellungsfehler in der 15.ten Stelle sind irrelevant, weil Du ja formatiert ausgibst. Dafür kannst Du dann in Ruhe mit den Werten rechnen. Die Rundungsfehler schaukeln sich nur bei extremen Werten und/oder langen Iterationen langsam hoch.

Wichtig ist hier die formatierte Ausgabe auf eine Nachkommastelle, aber das sollte man ohnehin machen.
Zitat:

Zitat von grenzgaenger
nicht umsonnst sollte man die floats vermeiden... ;-)

Äh.. nö. Man muss nur bei der Ausgabe etwas aufpassen. Floating Point Arithmetik ist nunmal um ein Vielfaches schneller und wird deshalb verwendet. Und Rundungsfehler gibt es auch bei BCD, nur keine Darstellungsfehler.

QuickAndDirty 26. Mai 2008 07:59

Re: kaufmännisch auf 0,5 oder 1 runden
 
Zitat:

Zitat von juergen
Ps.:
Angewandtes Tarifrecht...
Es ist erstaunlich wieviel Spielraum/ Interpretation Gesetzestext in der Praxis so zulässt.

OHH JA. Ich mache seit 5 Jahren hier an der Entwicklung Zeiterfassung mit....trotz Flächentarifvertrgägen
und gesetzten gibt es keine 2 Firmen mit den selben Zeitmodellen....aber jede kommt und sagt:
"Wie das muss man einstellen? Das ist doch Tarif standard!" (Von wegen....)
Deswegen haben wir seit 12 Jahren einen Makrointerpreter und zig Makro Einsprungpunkte falls mal "ab und zu"
jemand "seltsame" Regelungen umgesetzt braucht.



@alzeimer:
Wenn du zwei große Floats von einander abziehst kann schon im ersten
Schritt ein echt interessantes Ergebnis bei rauskommen.

alzaimar 26. Mai 2008 08:43

Re: kaufmännisch auf 0,5 oder 1 runden
 
Zitat:

Zitat von QuickAndDirty
Wenn du zwei große Floats von einander abziehst kann schon im ersten Schritt ein echt interessantes Ergebnis bei rauskommen.

:gruebel: Was soll daran interessant sein (klappt übrigens auch schon mit zwei kleinen Zahlen, z.B. 0.1)? Das ist dann aber ein Darstellungsfehler. Der Rundungsfehler liegt immer innerhalb der Spezifikation für Floating Points (8-20 Stellen, je nach verwendetem Datentyp). Wir wollen hier nicht über die Eigenheiten von Floatingpointarithmetik und -Darstellung diskutieren (das wurde hier schon bis zum Erbrechen durchgekaut), aber wenn man weiss,
1. wo die Darstellungsfehler herkommen
2. was man überhaupt ausrechnet und
3. welche Genauigkeit man benötigt,
dann kann man Floatingpointarithmetik bestimmungsgemäß und fehlerfrei verwenden. Wer die o.g. Punkte nicht richtig zu beantworten weiss, der muss zur Nachhilfe: Was interessieren mich Fehler in der 10 Stelle, wenn ich -im physikalischen Umfeld- nie mehr als 8 signifikante Stellen anzeigen muss (Ein Fehler von 0,00001%, das ist kleiner als fast jede Messeinrichtung).

Im hier diskutierten Fall liegt die benötigte Genauigkeit bei genau einer Dezimalstelle bzw. 3-6 signifikanten Stellen. Und *das* schafft ein Double mit links, hier würde sogar ein Single ausreichen.

Nicht verwechseln: Rundungsfehler und Darstellungsfehler. Ersteres kommt bei Computerrechnungen im Zahlenraum der reelen Zahlen doch öfter vor, letzteres ist eine Eigenheit der Floatingpoint-Datentypen.


Alle Zeitangaben in WEZ +1. Es ist jetzt 12:25 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