Posts tagged: Modulo

Modulo großer Zahlen berechnen

By , 23. Juli 2015

Hallo,
bei einer Berechnung von Modulo-Werten, also dem ganzzahligen Rest einer Division, bin ich auf ein Problem gestoßen, dessen Lösung ich euch nicht vorenthalten möchte.
Angenommen ihr möchtet den Modulo-Wert einer sehr großen Zahl berechnen. Dann stößt Access schnell an die Grenze des Longinteger-Wertebereichs, der bekanntlich bis 2147483647 reicht.
Beispiel:
2147483647 Mod 97 Ergebnis 65
2147483648 Mod 97 Ergebnis Überlauf.

Erinnert man sich wie der Modulo-Wert (ganzzahliger Rest einer Division) berechnet wird:
2147483648 geteilt durch 97 ergibt 22139006,86
Multipliziert man 22139006 mit 97, und zieht das Ergebnis von 2147483648 ab, so erhält man als Modulo-Wert 66

Und genau so bauen wir eine kleine Hilfsfunktion auf, die uns den Modulo-Wert berechnet:

Public Function Modulo(ByVal Dividend As Double, ByVal Devisor As Double) As Long
    Dim GanzzahlErgebnis As Long
    If Devisor = 0 Then Exit Function
    
    GanzzahlErgebnis = Fix(Dividend / Devisor)
    Modulo = Dividend - GanzzahlErgebnis * Devisor
End Function

Der Wert von GanzzahlErgebnis muss wirklich die abgeschnittene ganze Zahl der Division sein, deswegen muss hier die Funktion fix() verwendet werden.
Da wir immer den ganzzahligen Rest als Ergebnis möchten, genügt es die Prozedur als Long zu deklarieren.

Kopiert die Funktion in ein Modul und ruft sie z.B. wie folgt auf:

MsgBox Modulo(2200000000, 97)

Als Antwort wird die Zahl 36 ausgegeben.

Bis dahin
© 2015 Andreas Vogt

Big Numbers

By , 30. Juni 2015

Hallo,
heute möchte ich mal über richtig große Zahlen berichten, denn VBA hat genau mit denen Probleme diese zu verarbeiten.
Gerade in der Finanzwelt z.B. bei der Berechnung von Prüfziffern werden diese benötigt. Es sind dann schon spezielle Fälle, aber man sollte wissen wie man damit umgehen kann.

Schauen wir uns mal die Datentypen an, die uns in VBA zur Verfügung stehen:
– der Long-Datentyp ist bereits mit etwas über 2,1 Milliarden überfordert.
– Double schaft es immerhin auf 15 Stellen, bevor in die gerundete Exponential-Schreibweise gewechselt wird.
– eine weitere Möglichkeit ist der wenig bekannte Dezimal-Datentyp. Dieser lässt sich mit der Funktion CDec() herstellen, und damit haben wir schon 29 Stellen mit denen wir genau rechnen können.

Aber darüber hinaus? Nichts, Nada, Niente.

Angenommen Sie möchten von einer 36-Stelligen Zahl den Modulo nehmen, denn so groß kann maximal der BBAN (Basic Bank Account Number) nämlich werden: 30 Stellen Kontoidentifikation + 4 Stellen konvertiertes Länderkürzel + „00“

Mit dividieren so wie in den vorherigen Beitrag zum Modulo kommt man da nicht weit.

Aber es gibt für solche Fälle spezielle Rechenvorschriften, und eine möchte ich euch vorstellen, die Neun-Stellen-Regel.
Diese Regel besagt folgendes:

1. Von einer großen Zahl werden von links weg 9 Ziffern genommen werden, davon der Modulo genommen.
2. Es wird eine neue Zahl gebildet die mit dem berechneten Modulo beginnt, 
   und mit Ziffern der großen Zahl bis auf 9 Stellen aufgefüllt wird.
3. Von dieser neuen Zahl wird wieder der Modulo genommen
4. Die Vorgänge 2. und 3. werden so lange wiederholt, bis alle Ziffern der ursprünglichen 
   großen Zahl verarbeitet wurden.
5. Der Modulo, der bei der letzten Berechnung herauskommt, ist das Endergebnis.

Machen wir mal ein Beispiel.
Unsere große Zahl sei diese: 661400580’9000014’5786179’5321700’131400
Der Übersichtlichkeit ist diese in die verwendeten Blöcke eingeteilt.
Davon wollen wir den Modulo 97 bestimmen:

661400580 Mod 97 = 66
669000014 Mod 97 = 35
355786179 Mod 97 = 73
735321700 Mod 97 =  8
8131400   Mod 97 = 84

Der Modulo 97 userer großen Zahl ist also 84.

Um nun nicht ständig von Hand zu rechnen, erstellen wir aus diesen Informationen eine Prozedur.
Da wir von unserer großen Zahl (Dividend) nur Ziffern abschneiden, und nicht mit ihr rechnen, können wir diese als String-Parameter übergeben.

Public Function Modulo(ByVal Dividend As String, ByVal Devisor As Long)
    Dim NeueZahl As Long
    Dim ZwischenModulo As Variant

    Do While Len(Dividend) > 0
        NeueZahl = CLng(ZwischenModulo & Left(Dividend, 9 - Len(CStr(ZwischenModulo))))
        Dividend = Mid(Dividend, 10 - Len(CStr(ZwischenModulo)))
        ZwischenModulo = NeueZahl Mod Devisor
    Loop
    Modulo = ZwischenModulo
End Function

Was euch sicherlich Kopfzerbrechen macht ist diese Zeile:
NeueZahl = CLng(ZwischenModulo & Left(Dividend, 9 – Len(CStr(ZwischenModulo))))

Im ersten Durchlauf der Schleife ist ZwischenModulo (Typ Variant) Leer bzw. Empty. Daher ist Len(CStr(Zwischenmodulo)) = 0. Es werden also genau 9 Stellen von der großen Zahl genommen.
Im zweiten und weiteren Durchläufen ist ZwischenModulo mit dem zuvor berechneten Modulo besetzt. Die NeueZahl bildet sich also aus ZwischenModulo & 9-Len(CStr(ZwischenModulo)) Stellen der großen Zahl, um wieder maximal 9 Stellen zu erhalten.

Die Zeile: Dividend = Mid(Dividend, 10 – Len(CStr(ZwischenModulo))) schneidet einfach nur die verwendeten Ziffern von der großen Zahl ab.
Da die Mid()-Funktion 1-Basiert ist, muss man Mid(Dividend,10) schreiben um 9 Stellen vom Dividend abzuschneiden, also alles richtig.

Vieleicht noch der Hinweis warum ZwischenModulo als Variant deklariert ist und nicht als Long: Wäre er als Long deklariert dann wäre im ersten Schleifendurchgang der Wert nicht Leer sondern 0, und die Länge von CStr(0) ist nunmal 1 und nicht 0.

OK, ich denke ich konnte euch die Problematiken von richtig großen Zahlen – den Big Numbers – ein wenig näher bringen. Und die neue Modulo-Funktion könnt ihr auch sofort in euren Projekten einsetzen und ihr läuft nicht Gefahr dass irgendwann der Wertebereich nicht ausreicht oder dass falsche Ergebnisse durch die gerundete Exponential-Darstellung von Double auftreten, denn diese können in der Finanzwelt schnell sehr teuer werden.

Bis dahin
© 2015 Andreas Vogt

OfficeFolders theme by Themocracy