Seite 1 von 1

Rundungsfehler

Verfasst: Mi, 31. Mär 2021 19:50
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 ???????????

Re: Rundungsfehler

Verfasst: Do, 01. Apr 2021 6:16
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.

Re: Rundungsfehler

Verfasst: Do, 01. Apr 2021 6:47
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.

Re: Rundungsfehler

Verfasst: Do, 01. Apr 2021 7:29
von Wolfgang Ciriack
Danke, Hubert, mit RoundX wird richtig gerundet und ich bekomme meinen Wert 2,72 =D>

Re: Rundungsfehler

Verfasst: Do, 01. Apr 2021 7:54
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.

Re: Rundungsfehler

Verfasst: Do, 01. Apr 2021 9:00
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.

Re: Rundungsfehler

Verfasst: Fr, 30. Jul 2021 19:18
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.