Was mir als IT Consultant so alles an nützlichen und skurrilen Dingen über den Weg läuft, halte ich hier fest.
In unzähligen Fällen, in denen Firmen Geld verloren ging oder zuviel auftauchte, stellte ich fest - das muss nicht immer mit den Panzerknackern zu tun haben. Programmiersprachen erledigen das ganz von selbst - gesetzt des Falles, dass sie nicht richtig angewandt werden.
Im nachfolgenden Beispiel, stelle ich dar, wie einem realen Unternehmen Fehlrechnungen unterliefen, die sie sich zunächst nicht erklären konnten.
Natürlich werden weder Namen, noch Zeit und Ort genannt. Der Fall könnte gestern passiert sein oder vor 5 Jahren - das soll keine Rolle spielen. Geschäftsdetails sind nicht enthalten und ich habe den Fall mit einem privaten Beispiel nachgestellt.
Viel Spaß beim Lesen.
P.S.
Solche Sachen werden gelegentlich verfilmt. Wer mal sehen will, wie ich und meinesgleichen so leben und was sie alles erdulden müssen, sollte sich diesen Film ansehen:
Alles Routine (DE)
Rechnen mit Java in der Finanzwelt
von: Tim Harder
Immer wieder stellt man bei der Sichtung von Software fest, dass das Rechnen in Java zu Problemen führt, wenn primitive Datentypen verwendet werden.
Hier ein aktuelles Beispiel eines Rundungsfehlers, bei einem Finanz Dienstleister, der beliebig hohe Summen zuviel oder zuwenig berechnet, ohne, dass es gleich auffällt:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| // GIVEN final Integer price = - 595 ; final Integer someUnit = 90 ; { // WHEN int multiplicationResult = price * someUnit; // = -53550 Integer resultAmount = multiplicationResult / 100 ; // = -535 // …Division fehlgeschlagen, // Nachkommastellen ignoriert... // THEN assertTrue(resultAmount == - 535 ); // Falsch. Es müsste -536 sein. } |
…und was passiert, wenn man mit Datentypen rechnet, die Zahlenräume korrekt abbilden können:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| // GIVEN final Integer price = - 595 ; final Integer someUnit = 90 ; { // WHEN BigDecimal priceBD = new BigDecimal(price); BigDecimal someUnitBD = new BigDecimal(someUnit); BigDecimal hundred = new BigDecimal( 100 ); BigDecimal multiplicationResult = priceBD.multiply(someUnitBD); // = -53550 // = -535.5 bedeutet, alles sieht gut aus BigDecimal resultAmountBD = multiplicationResult.divide(hundred); final int noDecimalPlaces = 0 ; // rounding mode essentiell für das Ergebnis final RoundingMode roundingMode = RoundingMode.HALF_UP; // Nachkommastellen definieren BigDecimal result = resultAmountBD.setScale(noDecimalPlaces, roundingMode); // THEN assertThat(result.toBigInteger().intValue(), equalTo(- 536 )); // Richtig! } |
Warum ist das so?
Laut Standard IEEE 754 wird für Exception Handling und Zahlenräume definiert, wie float und double rechnen.
Floating Points sind dafür geeignet, Annäherungen zu beschreiben, schnell, aber ohne die für ein exaktes Resultat notwendige Präzision.
BigDecimal arbeitet intern mit String Repräsentationen und arbeitet sich Stück für Stück vor, weshalb dieser Datentyp zum einen unabhängiger von definierten, sprich begrenzten Zahlenräumen ist.
Außerdem kann die Präzision der Berechnung genauestens definiert werden.
Das ermöglicht die Erstellung von Kontrakten zum Thema Berechnung: Wird aufgerundet, oder abgerundet? Wieviele Nachkommastellen? Welche Darstellung im System?
So haben Banken in den meisten Fällen den „Bank Rounding Mode“. Alle Systeme, die miteinander im Kontext eines Anwendungsfalles sprechen, einigen sich auf diese Form der Berechnung.
Warum ist das so wichtig?
In einem aktuellen Fall nimmt ein System innerhalb eines Kontexts nur zwei Nachkommastellen an.
Es berechnet daraus Summen und schickt diese zurück. Dadurch entsteht eine Differenz zwischen den Eingangswerten und darauf basierenden Annahmen und den zusätzlichen Ergebnissen aus diesem System. Das System ist eine Blackbox und es kann nicht mehr festgestellt werden, was genau dort berechnet wurde. Hätte man von vornherein einen Kontrakt definiert, der nicht nur die Präzision der Nachkommastellen, sondern auch die Rundungs Modi definiert, rechneten alle Systeme gleich.
So etwas führt mitunter zu beliebig hohen Summen, die akkumuliert zuviel oder zuwenig berechnet werden und welche nachher buchhalterisch zu Verlusten führen, da man mit Geld hantiert, welches es genau genommen gar nicht gibt.
Keine Kommentare:
Kommentar veröffentlichen