11.03 Rechnen mit großen Zahlen
Sie wissen aus den vorhergehenden Kapiteln, dass primitive Datentypen in Java nicht unendlich groß werden können, sondern dass jede primitive Zahl einem bestimmten Wertebereich entspricht. Gleichzeitig wissen Sie auch, dass Fließkommazahlen nicht in jedem Fall hundertprozentig genau rechnen. Um mit sehr großen, sehr kleinen oder sehr präzisen Zahlen zu rechnen, verwendet man in Java die Klassen java.math.BigInteger
bzw. java.math.BigDecimal
.
Einen BigInteger verwendet man immer dann, wenn man nur eine sehr große Ganzzahl benötigt. Sind zusätzlich noch Kommazahlen im Spiel, bietet sich der Einsatz eines BigDecimal an. Beide Klassen haben eine große Menge mathematischer Methoden, die hier nicht im Einzelnen besprochen werden sollen. Vielmehr möchte ich Ihnen in diesem Kapitel die wichtigsten Funktionen und deren Verwendung zeigen. Eine Beschreibung aller Methoden finden Sie in der API-Dokumentation (BigInteger, BigDecimal).
Eine große Zahl erzeugen
Hierzu wird in der Regel im Konstruktor der gewünschte Wert als String
angegeben. Alternativ kann beim BigInteger
parallel noch ein radix
angegeben werden, um Zahlen eines anderen Systems (bspw. Binär oder Hexadezimal) zu verwenden. Der BigDecimal
akzeptiert als Basis auch noch ein char
-Array, ein double, long
oder int
. Außerdem kann ein BigDecimal
auch Eingaben als 10er Exponent verarbeiten. Ansonsten gibt es auch noch jeweils drei Konstanten für die Werte 1, 10 und 0.
System.out.println(new BigInteger("42")); // 42 System.out.println(new BigInteger("2A", 16)); // 42 System.out.println(new BigDecimal("42")); // 42 System.out.println(new BigDecimal("42".toCharArray())); // 42 System.out.println(new BigDecimal(42)); // 42 System.out.println(new BigDecimal(42L)); // 42 System.out.println(new BigDecimal(42D)); // 42 System.out.println(new BigDecimal("4.2E1")); // 4.2 * (10 ^ 1) => 42 System.out.println(new BigDecimal("420E-1")); // 420 * (10 ^ -1) => 42 System.out.println(BigInteger.ZERO); // 0 System.out.println(BigDecimal.ONE); // 1 System.out.println(BigInteger.TEN); // 10
Basismathematik
Sowohl BigInteger
wie auch BigDecimal
unterstützen Basisfunktionen der Mathematik. Hierzu zählen Addition, Division, Mulitplikation, Subtraktion und Exponentialfunktionen. Da beide Klassen ebenso wie Strings
immutable sind, werden die Werte nie verändert, sonden es wird jedes Mal ein neues Objekt zurückgeliefert.
BigInteger i = BigInteger.ONE; // 1 i = i.multiply(BigInteger.TEN); // 10 i = i.add(new BigInteger("8")); // 18 i = i.divide(BigInteger.TEN); // 1 (Kommazahlen werden abgeschnitten) i = i.subtract(BigInteger.TEN); // -9 i = i.pow(2); // 81 BigDecimal d = BigDecimal.ONE; // 1 d = d.multiply(BigDecimal.TEN); // 10 d = d.add(new BigDecimal("8")); // 18 d = d.divide(BigDecimal.TEN); // 1.8 d = d.subtract(BigDecimal.TEN); // -8.2 d = d.pow(2); // 67.24
Beim Teilen eines BigDecimals
haben Sie zusätzlich noch die Möglichkeit, eine Skalierung und einen Rundungsmodus zu übergeben. Beispielsweise könnte bei einer letzten Ziffer von fünf oder größer aufgerundet, ansonsten abgerundet werden. Ist die Nachkommazahl des Ergebnisses nicht endlich (bspw. 1/3), muss zwingend eine Skalierung (und folglich auch ein Rundungsmodus) angegeben werden, da ansonsten eine ArithmeticException
geworfen wird.
try { System.out.println(BigDecimal.ONE.divide(new BigDecimal("3"))); // ArithmeticException } catch (ArithmeticException ae) { System.out.println(BigDecimal.ONE.divide( new BigDecimal("3"), // durch 3 5, // Auf 5 Nachkommastellen gerundet BigDecimal.ROUND_HALF_UP // Bei 0.5 aufrunden )); // 0.33333 }
Beim BigInteger
steht zusätzlich noch die mod
-Funktion zur Verfügung.
System.out.println(BigInteger.ONE.mod(new BigInteger("2"))); // 1
Betrag, Vergleich, Minimum und Maximum
Über max
und min
wird der größere bzw. kleinere Zahlenwert zurückgeliefert, abs
liefert den Absolutwert.
System.out.println(BigInteger.ONE.min(BigInteger.TEN)); // 1 System.out.println(BigInteger.ONE.max(BigInteger.TEN)); // 10 System.out.println(new BigInteger("-2").abs()); // 2 System.out.println(BigDecimal.ONE.min(BigDecimal.TEN)); // 1 System.out.println(BigDecimal.ONE.max(BigDecimal.TEN)); // 10 System.out.println(new BigDecimal("2").abs()); // 2
Möchten Sie zwei BigInteger
bzw. BigDecimal
miteinander vergleichen, sollten Sie unbedingt die Methode compareTo
verwenden, da equals
nur die interne Darstellung überprüft.
BigDecimal one1 = new BigDecimal("1"); BigDecimal one2 = new BigDecimal("10E-1"); System.out.println(one1.equals(BigDecimal.ONE)); // true System.out.println(one1.equals(one2)); // false System.out.println(one1.compareTo(BigDecimal.ONE) == 0); // true System.out.println(one1.compareTo(one2) == 0); // true
Bit-Manipulation
Ein BigInteger
erlaubt auch die Manipulation der Bits wie bspw. ein int
oder long
. Bei einem BigDecimal
ist das nicht möglich. Anbei eine Auflistung der Bit-Methoden eines BigIntegers
.
// and führt einen AND-Vergleich durch System.out.println(BigInteger.TEN.and(BigInteger.ONE)); // 0 // Gleichbedeutend mit System.out.println(10 & 1); // 0 // andNot führt einen AND-Vergleich mit dem negierten Übergabewert durch System.out.println(BigInteger.TEN.andNot(BigInteger.ONE)); // 10 // Gleichbedeutend mit System.out.println(10 & (-1)); // 10 // or führt einen OR-Vergleich durch System.out.println(BigInteger.TEN.or(BigInteger.ONE)); // 11 // Gleichbedeutend mit System.out.println(10 | 1); // 11 // xor führt einen XOR-Vergleich durch System.out.println(BigInteger.TEN.xor(BigInteger.ONE)); // 11 // Gleichbedeutend mit System.out.println(10 ^ 1); // 11 // clearBit setzt das Bit an der gegebenen Position auf 0 System.out.println(BigInteger.TEN.clearBit(1)); // 8 // Gleichbedeutend mit System.out.println(10 & ~(1 << 1)); // 8 // setBit setzt das Bit an der gegebenen Position auf 1 System.out.println(BigInteger.TEN.setBit(5)); // 42 // Gleichbedeutend mit System.out.println(10 | (1 << 5)); // 42 // flipBit wandelt das Bit an der gegebenen Position in eine 0, falls es // ursprünglich eine 1 war, und in eine 1, falls es ursprünglich eine 0 war System.out.println(BigInteger.TEN.flipBit(4)); // 26 // Gleichbedeutend mit: System.out.println(10 ^ (1 << 4)); // 26 // shiftLeft verschiebt die Bits um eine gegebene Stelle nach Links System.out.println(BigInteger.TEN.shiftLeft(1)); // 20 // Gleichbedeutend mit System.out.println(10 << 1); // shiftRight verschiebt die Bits um eine gegebene Stelle nach Rechts System.out.println(BigInteger.TEN.shiftRight(1)); // 5 // Gleichbedeutend mit System.out.println(10 >> 1); // testBit gibt true zurück, falls das Bit an der gegebenen Stelle gesetzt // ist System.out.println(BigInteger.TEN.testBit(1)); // true // Gleichbedeutend mit System.out.println((10 & (1 << 1)) != 0); // true[/sourcecode] <strong>Rundungsmodus und Skalierung</strong> Sie können den Rundungsmodus und die Skalierung eines <code>BigDecimal</code> - ähnlich wie beim Teilen - individuell festlegen. Mit <code>setScale(int scale)</code> erhalten Sie einen <code>BigDecimal</code> mit den angegebenen Nachkommastellen, <code>setScale(int scale, int roundingMode)</code> setzt zusätzlich noch den Rundungsmodus. Folgende Modi sind möglich: <ul> <li><strong>BigDecimal.ROUND_CEILING</strong> - rundet hin zur positiven Unendlichkeit</li> <li><strong>BigDecimal.ROUND_DOWN</strong> - rundet hin zur Null</li> <li><strong>BigDecimal.ROUND_FLOOR</strong> - rundet hin zur negativen Unendlichkeit</li> <li><strong>BigDecimal.ROUND_HALF_DOWN</strong> - rundet kaufmännisch, außer wenn die zu rundene Zahl eine fünf ist. In diesem Fall wird abgerundet</li> <li><strong>BigDecimal.ROUND_HALF_EVEN</strong> - rundet kaufmännisch, außer wenn die zu rundene Zahl eine fünf ist. In diesem Fall wird zur geraden Zahl hin gerundet</li> <li><strong>BigDecimal.ROUND_HALF_UP</strong> - rundet kaufmännisch</li> <li><strong>BigDecimal.ROUND_UNNECESSARY</strong> - gibt an, dass keine Rundung erforderlich ist</li> <li><strong>BigDecimal.ROUND_UP</strong> - rundet von der Null weg</li> </ul> Ein paar Beispiele: [sourcecode language="java"]System.out.println(new BigDecimal("1.29").setScale(1, BigDecimal.ROUND_CEILING)); // 1.3 System.out.println(new BigDecimal("-1.29").setScale(1, BigDecimal.ROUND_CEILING)); // -1.2 System.out.println(new BigDecimal("1.29").setScale(1, BigDecimal.ROUND_FLOOR)); // 1.2 System.out.println(new BigDecimal("-1.29").setScale(1, BigDecimal.ROUND_FLOOR)); // -1.3 System.out.println(new BigDecimal("1.55").setScale(1, BigDecimal.ROUND_HALF_DOWN)); // 1.5 System.out.println(new BigDecimal("1.35").setScale(1, BigDecimal.ROUND_HALF_EVEN)); // 1.4 System.out.println(new BigDecimal("1.25").setScale(1, BigDecimal.ROUND_HALF_EVEN)); // 1.2 System.out.println(new BigDecimal("-1.25").setScale(1, BigDecimal.ROUND_HALF_UP)); // -1.3 System.out.println(new BigDecimal("0.12345").setScale(5, BigDecimal.ROUND_UNNECESSARY)); // 0.12345 System.out.println(new BigDecimal("0.009").setScale(2, BigDecimal.ROUND_DOWN)); // 0.00 System.out.println(new BigDecimal("-0.001").setScale(2, BigDecimal.ROUND_UP)); // -0.01
Konvertierung in primitive Datentypen
Sie können große Zahlen auch problemlos wieder in primitive Datentypen umwandeln. BigInteger
und BigDecimal
bieten hierfür die Methoden intValue()
, longValue()
, floatValue()
und doubleValue()
an. Beachten Sie jedoch, dass ein BigInteger
oder ein BigDecimal
nicht immer verlustfrei (da zu groß oder zu klein) in einen primitiven Datentyp konvertiert werden kann.
System.out.println(new BigInteger("2").intValue()); // 2 System.out.println(new BigInteger("100000000000").intValue()); // 1215752192
Möchten Sie überprüfen, ob die Konvertierung verlustfrei abgelaufen ist, können Sie intValueExact
, longValueExact
, shortValueExact
und byteValueExact
der Klasse BigDecimal
verwenden. Ist die Konvertierung verlustbehaftet, wird eine ArithmeticException
geworfen.
BigDecimal bd = new BigDecimal("200"); System.out.println(bd.longValueExact()); // 200 System.out.println(bd.intValueExact()); // 200 System.out.println(bd.shortValueExact()); // 200 System.out.println(bd.byteValueExact()); // ArithmeticException
Selbes funktioniert bei einem BigDecimal
übrigens auch mit toBigInteger
bzw. toBigIntegerExact
für einen BigInteger
.
Konvertierung in einen String
Neben der gewöhnlichen toString()
-Darstellung unterstützt ein BigInteger
noch die Möglichkeit, den Zahlenwert in einem anderen Zahlensystem als dem Dezimalsystem darzustellen. Hierzu wird die Methode toString(int radix)
verwendet.
BigInteger bi = new BigInteger("42"); System.out.println(bi.toString()); // 42 System.out.println(bi.toString(16)); // 2a System.out.println(bi.toString(2)); // 101010
Bei einem BigDecimal
gibt es neben toString()
(interne Darstellung wird zurückgeliefert) noch die Methoden toPlainString()
(Rückgabe ohne Exponent) und toEngineeringString()
(Rückgabe mit Exponent – sofern sinnvoll).
BigDecimal bd = new BigDecimal("1.53254E10"); System.out.println(bd.toString()); // 1.53254E+10 System.out.println(bd.toPlainString()); // 15325400000 System.out.println(bd.toEngineeringString()); // 15.3254E+9
Hallo,
schön gemacht. Im Absatz „Basismathematik“ sollte ich der ersten Zeile des Quelltextes die Deklaration der Variablen i stehen, oder? Also ‚BigInteger i = BigInteger.ONE; // 1‘.
Weiter so mit eurem super Blog!
Markus
Hallo Markus,
vielen Dank für die Korrektur. Da haben Sie natürlich recht. Ich habe den Artikel ausgebessert.
Danke und Gruß
Stefan
Mir ist ein kleiner Fehler aufgefallen: Bei der „Ggleichbedeutung“ des OR-Vergleiches bei BigInteger fehlt das Ergebnis als Kommentar. Ausserdem wird beim XOR-Vergleich wieder die or-Methode aufgerufen ^^
Grüße
Cyrill
Danke, habe ich ausgebessert.
Grüße
Stefan