Titel   Inhalt   Suchen   Index   DOC  Handbuch der Java-Programmierung, 7. Auflage
 <<    <     >    >>   API  Kapitel 18 - Utility-Klassen II

18.3 Die Klassen BigInteger und BigDecimal



Seit dem JDK 1.1 gibt es das Paket java.math mit den beiden Klassen BigInteger und BigDecimal. Beide implementieren beliebig große bzw. beliebig genaue Zahlen und stellen Methoden zur Durchführung arithmetischer Berechnungen zur Verfügung. Während BigInteger beliebig große Ganzzahlen implementiert, dient BigDecimal zur Darstellung sehr großer Fließkommazahlen. Objekte beider Klassen sind immutable, d.h., sie können nach der Instanzierung nicht mehr verändert werden.

18.3.1 Die Klasse BigInteger

Die einfachste Art, ein BigInteger-Objekt zu konstruieren, besteht darin, eine String-Repräsentation der darzustellenden Zahl an den Konstruktor zu übergeben. Das kann wahlweise mit oder ohne Angabe des Zahlensystems geschehen:

public BigInteger(String val)

public BigInteger(String val, int radix)
java.math.BigInteger

Wird das Zahlensystem nicht angegeben, erwartet der Konstruktor eine Zahl zur Basis 10. Der übergebene String darf eine beliebig lange Folge von Ziffern sein. An erster Stelle kann ein Minuszeichen stehen, um eine negative Zahl anzuzeigen.

Die Arithmetik auf BigInteger-Zahlen erfolgt durch Aufruf ihrer arithmetischen Methoden und Übergabe der erforderlichen Argumente, die meist ebenfalls vom Typ BigInteger sind. Der Methodenaufruf verändert dabei nicht den Wert des Objekts, sondern gibt das Ergebnis als neue BigInteger-Zahl an den Aufrufer zurück. Wichtige arithmetische Methoden sind:

public BigInteger add(BigInteger val)
public BigInteger subtract(BigInteger val)
public BigInteger multiply(BigInteger val)
public BigInteger divide(BigInteger val)
public BigInteger remainder(BigInteger val)
public BigInteger pow(int exponent)
java.math.BigInteger

Sie berechnen die Summe, Differenz, Produkt, Quotient, Restwert und Potenz zweier BigInteger-Zahlen. Neben den Grundrechenarten gibt es weitere Funktionen:

public BigInteger abs()
public BigInteger negate()
public int signum()
public BigInteger gcd(BigInteger val)

public BigInteger min(BigInteger val)
public BigInteger max(BigInteger val)
java.math.BigInteger

Sie stellen den absoluten Betrag zur Verfügung, multiplizieren mit -1, liefern das Vorzeichen und ermitteln den größten gemeinsamen Teiler zweier Zahlen. min und max liefern den kleineren bzw. größeren Wert aus aktueller und als Argument übergebener Zahl. Daneben gibt es logische und bitweise Operationen, die denen der primitiven ganzzahligen Typen entsprechen.

Zum Vergleich zweier BigInteger-Zahlen kann compareTo und equals verwendet werden, die Konvertierung in einen String wird mit toString erledigt:

public int compareTo(BigInteger val)
public boolean equals(Object x)

public String toString()
java.math.BigInteger

Die Arbeitsweise dieser Methoden entspricht der in der Klasse Object und dem Interface Comparable definierten und kann in Abschnitt 9.1.2 und Abschnitt 10.2 nachgelesen werden. Schließlich gibt es noch einige Methoden, um BigInteger-Objekte in primitive Typen zu verwandeln:

public int intValue()
public long longValue()
public float floatValue()
public double doubleValue()
java.math.BigInteger

Die Umwandlung folgt den in Abschnitt 5.6 beschriebenen Regeln für einschränkende Konvertierungen.

Als abschließendes Beispiel wollen wir uns ein kleines Programm ansehen, das die Fakultäten der Zahlen 30 bis 40 berechnet:

001 /* Listing1805.java */
002 
003 import java.math.*;
004 
005 public class Listing1805
006 {
007   public static void printFaculty(int n)
008   {
009     BigInteger bi = new BigInteger("1");
010     for (int i = 2; i <= n; ++i) {
011       bi = bi.multiply(new BigInteger("" + i));
012     }
013     System.out.println(n + "! is " + bi.toString());
014   }
015 
016   public static void main(String[] args)
017   {
018     for (int i = 30; i <= 40; ++i) {
019       printFaculty(i);
020     }
021   }
022 }
Listing1805.java
Listing 18.5: Anwendung der Klasse BigInteger

Die Ausgabe des Programms ist:

30! is 265252859812191058636308480000000
31! is 8222838654177922817725562880000000
32! is 263130836933693530167218012160000000
33! is 8683317618811886495518194401280000000
34! is 295232799039604140847618609643520000000
35! is 10333147966386144929666651337523200000000
36! is 371993326789901217467999448150835200000000
37! is 13763753091226345046315979581580902400000000
38! is 523022617466601111760007224100074291200000000
39! is 20397882081197443358640281739902897356800000000
40! is 815915283247897734345611269596115894272000000000

18.3.2 Die Klasse BigDecimal

Die Klasse BigDecimal kann beliebig genaue Fließkommazahlen darstellen. Sie bestehen aus einer Ziffernfolge (die als Objekt vom Typ BigInteger gespeichert ist) und einer Skalierung, die als nichtnegative Ganzzahl gespeichert wird. Die Skalierung bestimmt die Anzahl der Nachkommastellen. Der Wert der Zahl ergibt sich aus der Formel Unskalierter Wert / (10 Skalierung).

Die Instanzierung eines BigDecimal-Objekts kann analog zur Klasse BigInteger durch Übergabe eines Strings an den Konstruktor erfolgen. Dabei ist neben dem ganzzahligen Teil zusätzlich ein Dezimalpunkt und ein Gleitkommateil erlaubt. Die Anzahl der Nachkommastellen bestimmt die Skalierung. Alternativ kann das Objekt auch aus einem BigInteger oder einem double konstruiert werden:

public BigDecimal(BigInteger val)
public BigDecimal(double val)
public BigDecimal(String val)
java.math.BigDecimal

BigDecimal stellt elementare arithmetische Funktionen zur Verfügung:

public BigDecimal add(BigDecimal val)
public BigDecimal subtract(BigDecimal val)
public BigDecimal multiply(BigDecimal val)
public BigDecimal divide(BigDecimal val, int roundingMode)

public BigDecimal abs()
public BigDecimal negate()
public int signum()

public BigDecimal min(BigDecimal val)
public BigDecimal max(BigDecimal val)
java.math.BigDecimal

Ihr Verhalten entspricht weitgehend dem bei BigInteger beschriebenen. Eine Ausnahme bildet die Methode divide, denn sie benötigt zusätzlich eine Konstante, die die Art der Rundung (falls erforderlich) bestimmt:

Auch die Konvertierungs- und Vergleichsmethoden entsprechen denen der Klasse BigInteger:

public int compareTo(BigDecimal val)
public boolean equals(Object x)

public String toString()

public int intValue()
public long longValue()
public float floatValue()
public double doubleValue()
java.math.BigDecimal

Mit Hilfe der Methoden scale und setScale kann die Skalierung abgefragt bzw. ein neues Objekt mit geänderter Skalierung erzeugt werden:

public int scale()

public BigDecimal setScale(int scale)
public BigDecimal setScale(int scale, int roundingMode)
java.math.BigDecimal

Das Ändern der Skalierung lässt den numerischen Wert des Objekts intakt und verändert lediglich die Anzahl der darstellbaren Dezimalstellen. Soll diese verkleinert werden, muss die zweite Variante von setScale verwendet und eine passende Rundungskonstante übergeben werden. Soll die Anzahl der Dezimalstellen vergrößert werden, kann die erste Variante verwendet werden.

Zusätzlich gibt es zwei Methoden, mit denen der Dezimalpunkt um eine bestimmte Anzahl Stellen nach links oder rechts verschoben werden kann, also eine Multiplikation bzw. Division mit einer Zehnerpotenz ausgeführt wird:

public BigDecimal movePointLeft(int n)
public BigDecimal movePointRight(int n)
java.math.BigDecimal

Zum Abschluss wollen wir uns ein Beispielprogramm ansehen, das die Quadratwurzel der Zahl 2 in (theoretisch) beliebiger Genauigkeit errechnen kann:

001 /* Listing1806.java */
002 
003 import java.math.*;
004 
005 public class Listing1806
006 {
007   public static final BigDecimal ZERO = new BigDecimal("0");
008   public static final BigDecimal ONE  = new BigDecimal("1");
009   public static final BigDecimal TWO  = new BigDecimal("2");
010 
011   public static BigDecimal sqrt(BigDecimal x, int digits)
012   {
013     BigDecimal zero = ZERO.setScale(digits + 10);
014     BigDecimal one  = ONE.setScale(digits + 10);
015     BigDecimal two  = TWO.setScale(digits + 10);
016     BigDecimal maxerr = one.movePointLeft(digits);
017     BigDecimal lower = zero;
018     BigDecimal upper = x.compareTo(one) <= 0 ? one : x;
019     BigDecimal mid;
020     while (true) {
021       mid = lower.add(upper).divide(two, BigDecimal.ROUND_HALF_UP);
022       BigDecimal sqr = mid.multiply(mid);
023       BigDecimal error = x.subtract(sqr).abs();
024       if (error.compareTo(maxerr) <= 0) {
025         break;
026       }
027       if (sqr.compareTo(x) < 0) {
028         lower = mid;
029       } else {
030         upper = mid;
031       }
032     }
033     return mid;
034   }
035 
036   public static void main(String[] args)
037   {
038     BigDecimal sqrtTwo = sqrt(TWO, 100);
039     BigDecimal apxTwo  = sqrtTwo.multiply(sqrtTwo);
040     System.out.println("sqrt(2): " + sqrtTwo.toString());
041     System.out.println("check  : " + apxTwo.toString());
042   }
043 }
Listing1806.java
Listing 18.6: Anwendung der Klasse BigDecimal

Das Programm arbeitet mit einer Intervallschachtelung. Dazu werden zunächst passende Unter- und Obergrenzen gesucht, so dass das Quadrat der Untergrenze auf jeden Fall kleiner gleich und das Quadrat der Obergrenze größer oder gleich der gesuchten Zahl ist. Nun wird der Wert genau in der Mitte zwischen beiden Punkten quadriert und mit dem gesuchten Ergebnis verglichen. Ist er größer, wird die Mitte als neue Obergrenze verwendet, andernfalls als neue Untergrenze. Diese Iteration wird so lange fortgesetzt, bis der Fehler kleiner als der maximal erlaubte ist.

Die Ausgabe des Programms sieht wie folgt aus:

sqrt(2): 1.4142135623730950488016887242096980785696718753769480...
check  : 1.9999999999999999999999999999999999999999999999999999...

Das Beispielprogramm berechnet das Ergebnis auf etwa 100 Stellen nach dem Komma. Dieser Wert kann prinzipiell beliebig vergrößert werden. Zu bedenken ist allerdings, dass dadurch die Laufzeit überproportional ansteigt. Zwar bleibt die Intervallschachtelung an sich performant, aber durch die größere Anzahl an zu verarbeitenden Dezimalstellen benötigt jeder einzelne Aufruf einer arithmetischen Methode mehr Rechenzeit. Zudem erfordert die höhere Genauigkeit insgesamt mehr Iterationen, so dass insgesamt das Laufzeitverhalten wohl quadratisch mit der Genauigkeitsanforderung wachsen dürfte.

 Hinweis 


 Titel   Inhalt   Suchen   Index   DOC  Handbuch der Java-Programmierung, 7. Auflage, Addison Wesley, Version 7.0
 <<    <     >    >>   API  © 1998, 2011 Guido Krüger & Heiko Hansen, http://www.javabuch.de