Vorheriges Thema anzeigen :: Nächstes Thema anzeigen |
Autor |
Nachricht |
steini
Anmeldungsdatum: 17.09.2004 Beiträge: 58
|
Verfasst am: 18.12.2008, 19:38 Titel: Richtig runden mit Gleitkommazahlen |
|
|
Hallo, ich schreibe gerade an einer Finanzsoftware.
Zu einem Geldbetrag soll der Rabatt berechnet werden.
Code: | CLS
INPUT "Betrag EUR: ", wert#
INPUT "Prozentsatz: ", prozentsatz#
prozentwert# = wert# * prozentsatz# / 100
PRINT USING "##.##"; prozentwert# |
Gibt man nun den Betrag 19.90 an und als Prozentsatz 5, so wird als Ergebnis auf zwei Nachkommastellen gerundet 0.99 ausgegeben. Es müssten aber genau 1 sein, da der exakte Prozentwert (0.995) ja aufgerundet wird.
Ich habe mir schon einiges zur Gleitkommaproblematik durchgelesen und auch festgestellt, dass im oben genannten code der tatsächliche Wert für prozentwert# etwa 0.99499999 sind. Das erklärt auch, weshalb abgerundet wird, wenn nur die dritte Nachkommastelle berücksichtigt wird.
Aber wieso zeigt mein Taschenrechner dann genau 0.995 an? Es muss doch möglich sein, mit einem Programm das selbe Ergebnis wie der Taschenrechner zu erhalten. Oder trixt der etwa unbemerkt? |
|
Nach oben |
|
|
Lutz Ifer Grillmeister
Anmeldungsdatum: 23.09.2005 Beiträge: 555
|
Verfasst am: 18.12.2008, 21:36 Titel: |
|
|
Computer rechnet im Gleitkommaformat IEEE754, dabei wird zwischen den Zahlenbasen 10 und 2 umgerechnet, was Ungenauigkeiten bringt.
Ein Taschenrechner, der im alternativen Gleitkommaformat IEEE854 rechnet, hat diese Ungenauigkeiten nicht (dafür Speicherplatzverschwendung von etwa 2,4%).
Implentationen von sogenannten "Finanz-Gleitkommaformaten" gibts viele, für FREEBasic allerdings nicht nativ (=In die Sprache eingebaut), sondern nur durch die Einbindung von Bibliotheken. Wie die jetzt genau heißen, bin ich allerdings überfragt, weil's mich nicht beschäftigt. _________________ Wahnsinn ist nur die Antwort einer gesunden Psyche auf eine kranke Gesellschaft. |
|
Nach oben |
|
|
dreael Administrator
Anmeldungsdatum: 10.09.2004 Beiträge: 2507 Wohnort: Hofen SH (Schweiz)
|
|
Nach oben |
|
|
steini
Anmeldungsdatum: 17.09.2004 Beiträge: 58
|
Verfasst am: 19.12.2008, 00:44 Titel: |
|
|
drael, das Problem ist ja nicht, dass ich nicht weiß, wie ich richtig runden soll, sondern dass der Ausgangswert für die Rundungen ungenau ist! In dem Link wird zwar schön erklärt, wie diese Ungenauigkeiten zustande kommen, aber nicht, wie man sie behebt. Oder hab ich da etwas übersehen? Falls ja, gin mir doch bitte nen konkreten Hinweis.
Was die "Finanz-Gleitkommaformate" betrifft, werd ich mich mal schlau machen...
danke erstmal! |
|
Nach oben |
|
|
HorstD
Anmeldungsdatum: 01.11.2007 Beiträge: 107
|
Verfasst am: 19.12.2008, 03:38 Titel: |
|
|
@steini
Versuch's mal damit:
Code: |
CLS
INPUT "Betrag EUR: ", wert#
INPUT "Prozentsatz: ", prozentsatz#
prozentwert# = wert# * prozentsatz# / 100
p2#=(prozentwert# * 100 + .5) / 100
PRINT USING "##.##"; prozentwert#
PRINT USING "##.##"; p2#
|
|
|
Nach oben |
|
|
dreael Administrator
Anmeldungsdatum: 10.09.2004 Beiträge: 2507 Wohnort: Hofen SH (Schweiz)
|
Verfasst am: 19.12.2008, 09:29 Titel: |
|
|
@steini:
Ab dem Untertitel "Entwicklung einer Rundungsroutine" gehe ich auf Lösungen ein. Zuvor ist quasi die "Theorie" gefolgt, weshalb es dieses Verhalten gibt.
An Deiner Stelle solltest Du evtl. über die dort dritte diskutierte Variante nachdenken, also intern quasi alle Beträge als beispielsweise *100 grosse Zahl (Faktor abhängig von der kleinsten Währungsstückelung) im Speicher darstellen und somit das Komma nur rein optisch bei der Ausgabe einfügen. Dazu am besten die dortigen Code-Beispiele in FUNCTION-Prozeduren verpacken. _________________ Teste die PC-Sicherheit mit www.sec-check.net |
|
Nach oben |
|
|
steini
Anmeldungsdatum: 17.09.2004 Beiträge: 58
|
Verfasst am: 19.12.2008, 19:17 Titel: |
|
|
@HorstD: Die Idee, die du da hast, war mir auch schon gekommen. Einfach einen minimalen Wert dazuaddieren. Dann wird aus den errechneten 0.994999... 0.995 und die Rundung haut hin.
ABER: Was ist, wenn bei anderen Werten rechnerisch nun wirklich 0,994999... rauskommen würde, dann würde er fälschlicherweise auf- statt abrunden.
Im Endeffekt glaube ich, verschiebt man das Problem damit nur. Die konkrete Rechnung geht dann zwar, dafür eine andere aber nicht!
Die Idee von drael, nur mit Ganzzahlen zu rechnen, finde ich echt cool!
Leider hat mein Prog bereits mehrere tausend Zeilen und zig Variablen, da mir der Rundungsfehler erst kürzlich aufgefallen ist. Jetzt nachträglich alles mit Ganzzahlen zu berechnen scheint mir leider unmöglich...
Also wenn jemand vielleicht so eine Bibliothek findet die das kann oder sonst eine ZUVERLÄSSIGE Möglichkeit weiß, würd ich mich sehr freuen! |
|
Nach oben |
|
|
nemored
Anmeldungsdatum: 22.02.2007 Beiträge: 4597 Wohnort: ~/
|
Verfasst am: 20.12.2008, 00:11 Titel: |
|
|
Prinzipiell wird es nicht so schwierig sein, eine passende Bibliothek zu erstellen, aber dann kommst du, fürchte ich, um eine Komplettüberarbeitung des Programms trotzdem nicht herum. _________________ Deine Chance beträgt 1:1000. Also musst du folgendes tun: Vergiss die 1000 und konzentriere dich auf die 1. |
|
Nach oben |
|
|
volta
Anmeldungsdatum: 04.05.2005 Beiträge: 1875 Wohnort: D59192
|
Verfasst am: 22.12.2008, 21:36 Titel: |
|
|
Hi,
ob das nun eine "ZUVERLÄSSIGE Möglichkeit" ist kann ich nicht mit Bestimmtheit sagen
Bei Experimenten mit dem Beispiel von steini fiel mir auch auf das 0.995 in der Anzeige nicht richtig gerundet wurde.
Ich habe dann eine kleine Funktion geschrieben (in FreeBasic) die einmal mit einem sehr kleinen Wert 1E-13 den Umwandlungsfehler korrigiert und dann nach kaufmännischen Gesichtspunkt auf 2 Nachkommastellen rundet.
Code: | Cls
Dim As Double wert=19.90
Dim As Double prozentsatz=5
Dim As Double rabatt
Dim As Double betrag
'http://de.wikipedia.org/wiki/Rundung#Kaufm.C3.A4nnisches_Runden
Function kauf_runden( wert As Double) As Double
If Sgn(wert) = 0 Then Return wert
Dim As Double rest = Frac(Abs(wert * 100) + 1e-13)
If rest > 0.5 Then
wert = Fix(wert * 100 + Sgn(wert))
Else
wert = Fix(wert * 100)
EndIf
Function = wert / 100
End Function
Print Using " Brutto ###.## Euro"; wert
rabatt = wert * prozentsatz / 100
Print rabatt
'rabatt = kauf_runden(rabatt)
rabatt = kauf_runden(wert * prozentsatz / 100)
Print rabatt
Print Using "-Rabatt ###.## Euro"; rabatt
betrag = wert - rabatt
Print Using " Betrag ###.## Euro"; betrag
Sleep |
_________________ Warnung an Choleriker:
Dieser Beitrag kann Spuren von Ironie & Sarkasmus enthalten.
Zu Risiken & Nebenwirkungen fragen Sie Ihren Therapeuten oder Psychiater. |
|
Nach oben |
|
|
|