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