Rundungsfehler

Alle Fragen um die Programmierung, die sich sonst nicht kategorisieren lassen. Von Makro bis Codeblock, von IF bis ENDIF

Moderator: Moderatoren

Antworten
Benutzeravatar
Wolfgang Ciriack
Der Entwickler von "Deep Thought"
Der Entwickler von "Deep Thought"
Beiträge: 2932
Registriert: Sa, 24. Sep 2005 9:37
Wohnort: Berlin
Hat sich bedankt: 13 Mal
Danksagung erhalten: 34 Mal
Kontaktdaten:

Rundungsfehler

Beitrag von Wolfgang Ciriack »

Das macht mich fertig. Mein Ausgangszenario (Set decimals ist standardmäßig auf 2 gesetzt):

Code: Alles auswählen

for i := 1 to len(::aSteuer)
   ::SumSteuer += ::aSteuer[i, 1] * ::aSteuer[i, 2] / 100
next
::SumSteuer := Round(::SumSteuer, 2)
Das ergab bei mir einen falschen Steuerwert für 14,29 € netto mit 19% Steuer (::aSteuer = {14.29, 19.00}), nämlich 2.71 (müsste 2.72 sein).
Dann mal geändert auf:

Code: Alles auswählen

for i := 1 to len(::aSteuer)
   SET DECIMALS TO 4
   v := ::aSteuer[i, 1] * ::aSteuer[i, 2] / 100  --------> 2,7151
   v := Round(v, 2)----------------------------------------> 2,71
   :: SumSteuer += v
   SET DECIMALS TO 2
next
Warum macht ein Round(v, 2) aus 2.7151 das Ergebnis 2.71 und nicht 2.72 ???????????
Viele Grüße
Wolfgang
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15688
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 65 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Re: Rundungsfehler

Beitrag von brandelh »

weil es nur die Anzeige betrifft, nicht den internen Wert !

Ich habe damals immer den Monatszins Betrag berechnet (volle Anzahl), gerundet auf 2 Stellen und danach summiert.
Es sollte also ein Unterschied machen wenn du die komplexe += Berechnung auf Einzelschritte mit Rundung aufteilst.

2.7151 sollte aber immer auf 2.72 gehen, bei 2.7150 sehen das andere anders, aber wie gesagt, das sehen wir so, intern in der CPU sieht das anders aus.
Gruß
Hubert
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15688
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 65 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Re: Rundungsfehler

Beitrag von brandelh »

Ich habe mal auf meiner Platte nachgesehen, die Rundungsfehler sind bekannt und wurden nur teilweise behoben.
Es macht auch einen Unterschied, ob die Werte aus einer Variablen oder einem Feld stammen. Hier eine Funktion von Andreas Gehr Pals

Code: Alles auswählen

Function RoundX(nValue, nDecimals, nPrecission)
   LOCAL nInitial
   if empty(nPrecission)
      nInitial := nDecimals + 1
   else
      nInitial := nPrecission
   endif
return (Round(Round(nValue, nInitial), nDecimals))

// And here is a sample program to show you how and why this works: Andreas Gehr Pals ...

Procedure Main()
LOCAL nValue := 0.3 * 1.65
   set alternate to Round_RX.TXT additive
   set alternate on
   ?
   ? "Round_RX() on Xbase++ ", version()
   ? "nValue (0.3 * 1.65)         ", nValue
   ? "       0.3 * 1.65        ==>", str(       nValue       , 22, 20)
   ? "Round (0.3 * 1.65, 2)    ==>", str( Round(nValue, 2)   , 22, 20)
   ? "RoundX(0.3 * 1.65, 2)    ==>", str(RoundX(nValue, 2)   , 22, 20)
   ? "RoundX(0.3 * 1.65, 2, 2) ==>", str(RoundX(nValue, 2, 2), 22, 20)
   ? "RoundX(0.3 * 1.65, 2, 3) ==>", str(RoundX(nValue, 2, 3), 22, 20)
   ? "RoundX(0.3 * 1.65, 2, 4) ==>", str(RoundX(nValue, 2, 4), 22, 20)
   ? "RoundX(0.3 * 1.65, 2, 8) ==>", str(RoundX(nValue, 2, 8), 22, 20)
   ?
return
Ergebnis:

Code: Alles auswählen

Round_RX() on Xbase++  Xbase++ (R) Version 2.00
nValue (0.3 * 1.65)                   0,495   // das ist nur die Anzeige !
       0.3 * 1.65        ==> 0.49499999999999990000 // das ist der interne Wert
Round (0.3 * 1.65, 2)    ==> 0.49000000000000000000
RoundX(0.3 * 1.65, 2)    ==> 0.50000000000000000000
RoundX(0.3 * 1.65, 2, 2) ==> 0.49000000000000000000
RoundX(0.3 * 1.65, 2, 3) ==> 0.50000000000000000000
RoundX(0.3 * 1.65, 2, 4) ==> 0.50000000000000000000
RoundX(0.3 * 1.65, 2, 8) ==> 0.50000000000000000000
Ich habe eigene Abwandlungen, aber alle machen das Gleiche, zuerst auf die zu bewertende Nachkommastelle runden, dann auf 2.
Dateianhänge
Round-Err.zip
(4.27 KiB) 182-mal heruntergeladen
Gruß
Hubert
Benutzeravatar
Wolfgang Ciriack
Der Entwickler von "Deep Thought"
Der Entwickler von "Deep Thought"
Beiträge: 2932
Registriert: Sa, 24. Sep 2005 9:37
Wohnort: Berlin
Hat sich bedankt: 13 Mal
Danksagung erhalten: 34 Mal
Kontaktdaten:

Re: Rundungsfehler

Beitrag von Wolfgang Ciriack »

Danke, Hubert, mit RoundX wird richtig gerundet und ich bekomme meinen Wert 2,72 =D>
Viele Grüße
Wolfgang
Benutzeravatar
Tom
Der Entwickler von "Deep Thought"
Der Entwickler von "Deep Thought"
Beiträge: 9345
Registriert: Do, 22. Sep 2005 23:11
Wohnort: Berlin
Hat sich bedankt: 100 Mal
Danksagung erhalten: 359 Mal
Kontaktdaten:

Re: Rundungsfehler

Beitrag von Tom »

Set decimals ist standardmäßig auf 2 gesetzt
Das ist ein Missverständnis, das sich hält.

Set(_SET_DECIMALS) hat - genauso wie Set(_SET_FIXED) - absolut nichts mit der Rechengenauigkeit, der Repräsentation von Zahlen (also der internen Anzahl von Nachkommastellen usw.) oder ähnlichem zu tun, sondern nur mit der Darstellung von Zahlenwerten.
Herzlich,
Tom
Benutzeravatar
Wolfgang Ciriack
Der Entwickler von "Deep Thought"
Der Entwickler von "Deep Thought"
Beiträge: 2932
Registriert: Sa, 24. Sep 2005 9:37
Wohnort: Berlin
Hat sich bedankt: 13 Mal
Danksagung erhalten: 34 Mal
Kontaktdaten:

Re: Rundungsfehler

Beitrag von Wolfgang Ciriack »

Hallo Tom,
ich hatte das auch nur gesetzt, um im Debugger mal zu sehen, wie die Nachkommastellen sind.
Aber danke, dass du nochmal darauf hingewiesen hast.
Viele Grüße
Wolfgang
Dieter
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 237
Registriert: Do, 14. Aug 2008 14:59
Wohnort: Straelen
Hat sich bedankt: 2 Mal
Danksagung erhalten: 3 Mal

Re: Rundungsfehler

Beitrag von Dieter »

Hallo Hubert,

der Standardfall ohne den dritten Parameter in der geposteten Funktion (in Anlehnung an den Quellcode von Andreas Gehr Pals) kann durchaus zu Fehlern führen!

Code: Alles auswählen

Function RoundX(nValue, nDecimals, nPrecission)
   LOCAL nInitial
   if empty(nPrecission)
      nInitial := nDecimals + 1
   else
      nInitial := nPrecission
   endif
return (Round(Round(nValue, nInitial), nDecimals))
Ich habe deshalb die Funktion RoundX() wie folgt abgewandelt:

Code: Alles auswählen

Function RoundX(nValue, nDecimals)
return (Round(Round(nValue, nDecimals+2), nDecimals))
RoundX(0.7348, 2) in deiner Version führt zum Ergebnis 0.74!
In der Regel will man so arbeiten wie von Round() gewohnt.
Viele Grüße

Dieter

Was man nicht versteht, besitzt man nicht.
Antworten