Home > Artikel > Ausgabe 5/2016 > Währungen runden

Währungen runden

Achtung: Sie sind nicht angemeldet. Wenn Sie Abonnent sind und sich anmelden, lesen Sie den kompletten Artikel, laden das PDF herunter oder probieren die Beispieldatenbank aus (sofern vorhanden).

Im Finanzsektor wird alles sehr genau genommen. Wird über kleinere Rundungsfehler an anderer Stelle gern einmal hinweggesehen, so sind hier Inkonsistenzen in der Regel absolut inakzeptabel. Einzelne Cent-Beträge können sich über Millionen von Datensätzen zu größeren Summen aufaddieren. Wohin Sie genau schauen müssen, um solche Umstände zu vermeiden, untersuchen die folgenden Ausführungen.

Beispieldatenbank

Die Beispiele dieses Artikels finden Sie in der Datenbank 1605_Runden.accdb

Geldbeträge im Tabellenentwurf

Für Zahlen mit Nachkommastellen kommen unter Access im Prinzip vier Datentypen infrage: Single, Double, Currency und Decimal, also Fließkommazahl einfacher und doppelter Genauigkeit, sowie Währungs- und Dezimaltyp.

Single scheidet wegen der geringen Genauigkeit aus. Hier werden zur Speicherung nur vier Bytes verwendet, während der Double-Typ acht Bytes benötigt. Wie groß der Unterschied letztlich ist, können Sie mit einem kleinen Test im VBA-Direktfenster ermitteln. Lassen Sie sich die Ergebnisse dieser beiden Zeilen ausgeben:

  CSng (Now)

CDbl (Now)

Die Funktion Now gibt das aktuelle Datum samt Uhrzeit aus, wobei diesem Date-Typ tatsächlich ein Double-Wert unterliegt. CSng wandelt den Wert in einen Single-Typ um, CDbl in einen Double-Typ. Sie erhalten nun etwa

42516,4 (Single)

42516,4020486111 (Double)

Wären dies Geldbeträge, so betrüge die Differenz bereits circa 0,2 Cent!

Die große Zahl an Nachkommastellen lässt Double als für Währungen geeignet erscheinen. Doch immerhin muss es einen Grund geben, warum der Typ Currency zusätzlich zur Verfügung steht. Einerseits führt sein Einsatz dazu, dass in Tabellen auch gleich automatisch das Währungszeichen erscheint. Zudem ist die Zahl der Nachkommastellen gleich zwei. Andererseits speichert Currency aber Zahlen auch nach einem anderen System. Liegt dem Double-Typ mathematisch ein Exponentialsystem zugrunde, hantiert Currency mit einer festen Zahl von ganzzahligen Stellen, nämlich 19. Die Umwandlung in einen Fließkommatyp geschieht hier rein optisch durch Setzen des Kommas an die viertletzte Stelle, also über Division der Ganzzahl durch 10.000. Ergo stehen grundsätzlich vier Nachkommastellen zur Verfügung, während bei Double und Single die Zahl der Nachkommastellen variabel ist und von der Größe des Betrags abhängt.

Decimal schließlich ähnelt im Ergebnis Currency, weil der angezeigt Wert sich durch Division mit einer Zehnerpotenz ergibt. Die Genauigkeit ist sogar noch höher, als bei Currency. Doch auch hier gilt das gleiche, wie bei Double und Single: Die Anzahl der Nachkommastellen ist variabel. Außerdem kann VBA mit diesem Datentyp schlecht umgehen. Eine Variable vom Typ Decimal lässt sich erst gar nicht deklarieren! Der Typ scheidet damit für Währungsrechnungen weitgehend aus.

Für eine empirische Studie wurde die Tabelle tblBetraege in der Beispieldatenbank angelegt, deren Entwurf sich aus Bild 1 ergibt. Sie simuliert eine Bestelltabelle, wo es zu jeder Position die Anzahl gibt, den eigentlichen Betrag und die Umsatzsteuer (Ust, Prozentzahl, Double). Der Betrag ist für unseren Versuch über drei Varianten abgebildet. BetragDbl ist ein Feld vom Typ Double, BetragCur ist ein Currency-Typ und BetragDez von Typ Decimal. Zur Kontrolle wurden noch drei Berechnete Felder eingefügt. SummeDbl ist dabei folgender Ausdruck hinterlegt:

Entwurf der Tabelle tblBetraege mit ihren drei Währungsfeldern, sowie darauf bezogenen berechneten Feldern

Bild 1: Entwurf der Tabelle tblBetraege mit ihren drei Währungsfeldern, sowie darauf bezogenen berechneten Feldern

[Anzahl] * [BetragDbl] * (1 + [Ust])

Ohne eine Abfrage bemühen zu müssen, wird damit die Positionssumme gleich im Datenblatt angezeigt. In produktiven Datenbanken sollten Sie von diesen Berechneten Feldern aber eher absehen, weil die Anzeige direkter Tabelleninhalte dort zu vermeiden ist. SummeCur und SummeDez berechnen nach der identischen Formel, beziehen sich aber jeweils auf die Felder BetragCur und BetragDez.

Um die Tabelle mit Daten zu füllen, gibt es die Prozedur GeneriereBetraege in der Datenbank. Sie legt 10.000 Datensätze an und schreibt nach Zufallsprinzip Zahlen in die drei Felder Anzahl, Betrag... und Ust. Beim Betrag wird über String-Verarbeitung darauf geachtet, dass nur exakt zwei Nachkommastellen generiert werden:

Dim dBetrag As Double

Dim sBetrag As String

dBetrag = 100 * Rnd

sBetrag = CStr(Val(dBetrag * 100) / 100)

rs!BetragDbl.Value = sBetrag

Die genaue Gestaltung der Routine interessiert hier weniger, als ihr Ergebnis, welches in Bild 2 dargestellt ist. Im Datenblatt wurde hier zusätzlich die Aggregatzeile eingeblendet. An die Aggregatzeile kommen Sie über diese Schritte:

Das ist die Datenblattansicht der Tabelle tblBetraege, unten mit der hinzugefügten Aggregatzeile für die Summen

Bild 2: Das ist die Datenblattansicht der Tabelle tblBetraege, unten mit der hinzugefügten Aggregatzeile für die Summen

  • Öffnen Sie die Tabelle in der Datenblattansicht
  • Aktivieren Sie im Ribbon die Schaltfläche Start | Datensätze | Summen. Die Aggregatzeile erscheint unten in der Ansicht.
  • Gehen Sie in die Aggregatzelle einer Datenspalte und wählen Sie aus dem nun erscheinenden Kombinationsfeld die gewünschte Aggregatfunktion aus – hier jeweils Summe. Je nach Datentyp des Felds ist allerdings nur ein Teil der Aggregatfunktionen verfügbar.

Die Summen der drei Betragsspalten sind alle identisch. Das gilt auch für die Summen der berechneten Spalten, wobei hier SummeDbl drei Nachkommastellen zeigt, obwohl für die Formatierung des Felds dezidiert nur zwei Stellen im Entwurf festgelegt wurden. Aber die Formatierung der Aggregatzeile selbst lässt sich nun mal nicht beeinflussen.

Immerhin wird deutlich, dass sich Double bei Berechnungen anders verhält, als Currency und Decimal.

Dennoch kann man sich fragen, warum Double für Währungen nicht auch in Betracht kommt, wo doch das Summenergebnis trotz der hohen Zahl von Datensätzen nicht von der Währungsspalte abweicht?

Fügen wir der Tabelle noch zwei weitere berechnete Felder hinzu (siehe Bild 3). Dbl_x_1000 multipliziert den Betrag mit Tausend, schneidet die Kommastellen per Int-Funktion ab, und dividiert wieder durch Tausend:

Erweiterung der Tabelle tblBetraege um zwei zusätzliche berechnete Felder

Bild 3: Erweiterung der Tabelle tblBetraege um zwei zusätzliche berechnete Felder

Int(1000 * [BetragDbl])/1000

Mit der Currency-Spalte wird analog verfahren. Da die Geldbeträge – auch intern! – nur zwei Nachkommastellen aufweisen, kann die Multiplikation mit 1000 nur zu einer Ganzzahl führen und die anschließende Division wieder nur zu zwei Nachkommastellen. Bild 4 zeigt das Ergebnis, wobei für die neuen Felder ebenfalls die Summen in der Aggregatzeile aufgerufen wurden. Und siehe da, die Summen unterscheiden sich nun plötzlich. Statt 63 Cent findet sich im berechneten Feld der Nachkommaanteil 54,9 Cent. Eine Gruppierungsabfrage auf die Tabelle würde übrigens das gleiche Ergebnis zeitigen. Die Differenz ist nicht mehr akzeptabel.

Die Summen geben in Dbl_x_1000 das Problem wieder, welches durch Währungsfelder des Typs Double verursacht wird

Bild 4: Die Summen geben in Dbl_x_1000 das Problem wieder, welches durch Währungsfelder des Typs Double verursacht wird

Zustande kommt sie durch die Multiplikation des Betrags mit 1000. Hierdurch verringern sich Zahl der Nachkommastellen des Exponentialtyps Double intern. In der Folge kommt es zu Ungenauigkeiten, die bei Summierung unangenehm zu Buche schlagen.

Fazit: Vermeiden Sie den Typ Double bei Geldbeträgen. Leider reicht das Konvertieren eines Double-Felds in Currency nachträglich nicht aus, um die Ungenauigkeiten zu eliminieren, da Currency intern mit vier Nachkommastellen arbeitet. Die Ungenauigkeiten bleiben deshalb unter Umständen in der dritten und vierten Stelle erhalten.

Optisches Runden von Geldbeträgen

Immer wieder gern gemacht und doch fehleranfällig: das Runden von Beträgen auf zwei Nachkommastellen. Auf den ersten Blick ist es plausibel. Da multiplizieren Sie einen Betrag mit einer Prozentzahl, wie der Umsatzsteuer, und kommen so zu mehr, als zwei, Nachkommastellen. Also werden diese mit einer geeigneten Funktion gerundet.

Spätestens beim Summieren dieser errechneten Beträge kommt es zu deutlichen Fehlern. Vor allem dann, wenn Sie von der für diesen Zweck vorgesehenen VBA-Funktion Round Gebrauch machen. Denn diese rundet nach mathematischen Regeln, nicht nach kaufmännischen! Ein einfaches Beispiel:

  Round (0.325, 2)

> 0,32

Das ist mathematisch zwar nicht falsch, aber weitgehend ungebräuchlich, schon gar bei Finanzrechnungen. Man würde wohl eher das Ergebnis 0,33 erwarten.

Ohne hier weiter auszuführen, welcher Algorithmus dieser VBA-Rundungsfunktion zugrunde liegt, stellen wir eine Routine vor, welche das kaufmännische Runden vornimmt. Die Funktion RoundX im Modul mdlRound der Beispieldatenbank gibt Listing 1 wieder.

Public Function RoundX(ByVal Number As Variant, ByVal Digits As Integer) As Variant

     RoundX = Int(Number * 10 ^ Digits + 0.5@) / 10 ^ Digits

End Function

Public Function RoundX2(ByVal Number As Variant, ByVal Digits As Integer) As Variant

     Dim Hoch10 As Currency

     Select Case Digits

     Case 2: Hoch10 = 100

     Case 1: Hoch10 = 10

     Case 3: Hoch10 = 1000

Sie haben das Ende des frei verfügbaren Teil dieses Artikels erreicht!

Wenn Sie mehr lesen und auf viele weitere Artikel zugreifen möchten, melden Sie sich als Abonnent unter Login an. Falls nicht, bestellen Sie doch einfach ein Jahresabonnement!