Rechnen mit Java Ganze Zahlen Gebrochene Zahlen Eigenschaft der Fundamentalen Java baut auf 8 fundamentalen Datentypen (primitiven) auf. Java kann ausschliesslich mit diesen Primitiven rechnen! Für die Arithmetik wird in Java zwischen zwei Rechenarten unterschieden: Fazit Rechnen mit Java Ganze Zahlen Gebrochene Zahlen Eigenschaft der Fundamentalen Java baut auf 8 fundamentalen Datentypen (primitiven) auf. Java kann ausschliesslich mit diesen Primitiven rechnen! Für die Arithmetik wird in Java zwischen zwei Rechenarten unterschieden: Integer Rechnen mit ganzen Zahlen in vollständiger Präzision Fazit Rechnen mit Java Ganze Zahlen Gebrochene Zahlen Eigenschaft der Fundamentalen Java baut auf 8 fundamentalen Datentypen (primitiven) auf. Java kann ausschliesslich mit diesen Primitiven rechnen! Für die Arithmetik wird in Java zwischen zwei Rechenarten unterschieden: Integer Rechnen mit ganzen Zahlen in vollständiger Präzision Float Rechnen mit gebrochenen Zahlen in angenäherter Präzision Fazit Rechnen mit Java Ganze Zahlen Gebrochene Zahlen Rechnen im Intervall Dabei werden Datentypen verwendet, welche innerhalb eines beschränkten Zahlenraums sämtliche ganze Zahlen darstellen können: Fazit Rechnen mit Java Ganze Zahlen Gebrochene Zahlen Fazit Rechnen im Intervall Dabei werden Datentypen verwendet, welche innerhalb eines beschränkten Zahlenraums sämtliche ganze Zahlen darstellen können: Zahlenspiele Typ byte short int long Bit 8 16 32 64 Zahlenraum 256 65536 4294967296 18446744073709551616 Wertebereich −128 −32768 −2147483648 −9223372036854775808 ... ... ... ... +127 +32767 +2147483647 +9223372036854775807 char 16 65536 +0 ... +65535 Rechnen mit Java Ganze Zahlen Gebrochene Zahlen Fazit Rechnen im Intervall Dabei werden Datentypen verwendet, welche innerhalb eines beschränkten Zahlenraums sämtliche ganze Zahlen darstellen können: Zahlenspiele Typ byte short int long Bit 8 16 32 64 Zahlenraum 256 65536 4294967296 18446744073709551616 Wertebereich −128 −32768 −2147483648 −9223372036854775808 ... ... ... ... +127 +32767 +2147483647 +9223372036854775807 char 16 65536 +0 ... +65535 Es wird offensichtlich, dass der Default-Wert int z.B. in der heutigen Finanzwelt unzureichend ist, wie folgendes Beispiel erläutert: Rechnen mit Java Ganze Zahlen Gebrochene Zahlen Rechnen im Intervall 8 9 10 11 12 13 int val = Integer . MAX_VALUE ; System . out . print ( " Gewinn der UBS im Jahr 2007: " ) ; System . out . println ( val ) ; // -> 2147483647 val +=2; System . out . print ( " Gewinn der UBS im Jahr 2008: " ) ; System . out . println ( val ) ; // -> -2147483647 Fazit Rechnen mit Java Ganze Zahlen Gebrochene Zahlen Rechnen im Intervall 8 9 10 11 12 13 int val = Integer . MAX_VALUE ; System . out . print ( " Gewinn der UBS im Jahr 2007: " ) ; System . out . println ( val ) ; // -> 2147483647 val +=2; System . out . print ( " Gewinn der UBS im Jahr 2008: " ) ; System . out . println ( val ) ; // -> -2147483647 15 16 17 18 19 20 long val = Integer . MAX_VALUE ; System . out . print ( " Gewinn der UBS im Jahr 2007: " ) ; System . out . println ( val ) ; // -> 2147483647 val +=2; System . out . print ( " Gewinn der UBS im Jahr 2008: " ) ; System . out . println ( val ) ; // -> 2147483649 Fazit Rechnen mit Java Ganze Zahlen Gebrochene Zahlen Angenäherter Präzision Dabei werden Datentypen verwendet, welche innerhalb eines beschränkten Zahlenraums einige Reelle Zahlen darstellen können: Fazit Rechnen mit Java Ganze Zahlen Gebrochene Zahlen Angenäherter Präzision Dabei werden Datentypen verwendet, welche innerhalb eines beschränkten Zahlenraums einige Reelle Zahlen darstellen können: Zahlenspiele Typ float double Bit 32 64 Zahlenraum 4294967296 18446744073709551616 Wertebereich ±1, 4 × 10−45 ±4, 9 × 10−324 ... ... ±3, 4 × 1038 ±1, 7 × 10308 Fazit Rechnen mit Java Ganze Zahlen Gebrochene Zahlen Angenäherter Präzision Dabei werden Datentypen verwendet, welche innerhalb eines beschränkten Zahlenraums einige Reelle Zahlen darstellen können: Zahlenspiele Typ float double Bit 32 64 Zahlenraum 4294967296 18446744073709551616 Wertebereich ±1, 4 × 10−45 ±4, 9 × 10−324 ... ... ±3, 4 × 1038 ±1, 7 × 10308 Um den Wertebereich von float vollständig in einer linearen Granularität von 1, 4 × 10−45 darstellen zu können, bräuchte es einen Zahlenraum der Grösse 2 × 101710 . Es wird offensichtlich, dass mit einem Zahlenraum von 232 somit riesige Zahlenlöcher entstehen, welche die Genauigkeit bis zum Unbrauchbaren degradieren lassen, wie folgendes Beispiel erläutert: Fazit Rechnen mit Java Ganze Zahlen Gebrochene Zahlen Angenäherter Präzision 22 23 System . out . println ( " Next double value after 1000: " + Math . nextUp (1000 d ) ) ; // -> 1 00 0. 00 00000000001 Fazit Rechnen mit Java Ganze Zahlen Angenäherter Präzision 25 26 double val = 1.0 e16 ; double add =1000; Gebrochene Zahlen Fazit Rechnen mit Java Ganze Zahlen Gebrochene Zahlen Angenäherter Präzision 25 26 27 double val = 1.0 e16 ; double add =1000; System . out . println ( " " + val + " + " + add + " -" + val + " = " + ( val + add - val ) ) ; Fazit Rechnen mit Java Ganze Zahlen Gebrochene Zahlen Angenäherter Präzision 25 26 27 28 double val = 1.0 e16 ; double add =1000; System . out . println ( " " + val + " + " + add + " -" + val + " = " + ( val + add - val ) ) ; // -> 1000.0 Fazit Rechnen mit Java Ganze Zahlen Gebrochene Zahlen Angenäherter Präzision 25 26 27 28 29 30 31 32 double val = 1.0 e16 ; double add =1000; System . out . println ( " " + val + " + " + add + " -" + val + " = " + ( val + add - val ) ) ; // -> 1000.0 System . out . println ( " Next double value after " + val + " : " + Math . nextUp ( val ) ) ; // -> 1 .0 00 0000000000002 E16 System . out . println ( " Precise : " +( long ) ( Math . nextUp ( val ) ) ) ; // -> 100 00000000000002 Fazit Rechnen mit Java Ganze Zahlen Gebrochene Zahlen Angenäherter Präzision 34 35 36 37 38 39 40 41 double val = 1.0 e17 ; double add =1000; System . out . println ( " " + val + " + " + add + " -" + val + " = " +( val + add - val ) ) ; // -> 992.0 System . out . println ( " Next double value after " + val + " : " + Math . nextUp ( val ) ) ; // -> 1. 0 0 000000000000016 E17 System . out . println ( " Precise : " +( long ) ( Math . nextUp ( val ) ) ) ; // -> 1 00 000000000000016 Fazit Rechnen mit Java Ganze Zahlen Gebrochene Zahlen Angenäherter Präzision 43 44 45 46 47 48 49 50 double val = 1.0 e18 ; double add =1000; System . out . println ( " " + val + " + " + add + " -" + val + " = " +( val + add - val ) ) ; // -> 1024.0 System . out . println ( " Next double value after " + val + " : " + Math . nextUp ( val ) ) ; // -> 1. 0 0 000000000000013 E18 System . out . println ( " Precise : " +( long ) ( Math . nextUp ( val ) ) ) ; // -> 10 0 0 000000000000128 Fazit Rechnen mit Java Ganze Zahlen Gebrochene Zahlen Angenäherter Präzision 52 53 54 55 56 57 58 59 double val = 1.0 e19 ; double add =1000; System . out . println ( " " + val + " + " + add + " -" + val + " = " +( val + add - val ) ) ; // -> 0.0 System . out . println ( " Next double value after " + val + " : " + Math . nextUp ( val ) ) ; // -> 1 .0 00 0000000000002 E19 System . out . println ( " Precise : " +( long ) ( Math . nextUp ( val ) ) ) ; // -> 92 2 3 372036854775807 Fazit Rechnen mit Java Ganze Zahlen Gebrochene Zahlen Angenäherter Präzision Der Wertebereich im Bereich ±0 ist somit am feinsten aufgelöst und gibt somit das genauste Resultat. Man könnte annehmen, dass eine Rechnung so nahe an 0 wie möglich sein sollte. . . aber auch das stimmt leider nicht: Fazit Rechnen mit Java Ganze Zahlen Gebrochene Zahlen Angenäherter Präzision 73 74 75 76 77 double val =100.0; val /=3.0; val /=3.0; val *=9.0; System . out . println ( " ((100/3.0) /3.0) *9.0 = " + val ) ; Fazit Rechnen mit Java Ganze Zahlen Gebrochene Zahlen Angenäherter Präzision 73 74 75 76 77 78 double val =100.0; val /=3.0; val /=3.0; val *=9.0; System . out . println ( " ((100/3.0) /3.0) *9.0 = " + val ) ; // -> 1 00 .0 00 00000000001 Fazit Rechnen mit Java Ganze Zahlen Gebrochene Zahlen Angenäherter Präzision 80 81 82 83 84 double val =100; val *=9; val /=3.0; val /=3.0; System . out . println ( " ((100*9) /3.0) /3.0 = " + val ) ; Fazit Rechnen mit Java Ganze Zahlen Gebrochene Zahlen Angenäherter Präzision 80 81 82 83 84 85 double val =100; val *=9; val /=3.0; val /=3.0; System . out . println ( " ((100*9) /3.0) /3.0 = " + val ) ; // -> 100.0 Fazit Rechnen mit Java Angenäherter Präzision Ganze Zahlen Gebrochene Zahlen Fazit Rechnen mit Java Ganze Zahlen Gebrochene Zahlen Beliebiger Präzision Es gibt in Java eine Möglichkeit, ’Fliesskoma-Zahlen’ mit beliebiger Präzision zu verwalten. Diese Möglichkeit wird mit Hilfe der Klasse BigDecimal erreicht. Dabei wird die darzustellende Zahl ’häppchenweise’ gerechnet. Damit scheint das Problem gelöst zu sein: Fazit Rechnen mit Java Ganze Zahlen Gebrochene Zahlen Beliebiger Präzision 88 89 90 BigDecimal val = new BigDecimal ( " 1.0 e19 " ) ; BigDecimal add = new BigDecimal ( " 1000 " ) ; System . out . println ( " " + val + " + " + add + " -" + val + " = " + ( val . add ( add ) . subtract ( val ) ) ) ; Fazit Rechnen mit Java Ganze Zahlen Gebrochene Zahlen Beliebiger Präzision 88 89 90 91 BigDecimal val = new BigDecimal ( " 1.0 e19 " ) ; BigDecimal add = new BigDecimal ( " 1000 " ) ; System . out . println ( " " + val + " + " + add + " -" + val + " = " + ( val . add ( add ) . subtract ( val ) ) ) ; // -> 1000.0 Fazit Rechnen mit Java Ganze Zahlen Gebrochene Zahlen Beliebiger Präzision 93 94 95 BigDecimal val = new BigDecimal ( " 1.0 e19 " ) ; BigDecimal fraction = new BigDecimal ( " 1.0 e -19 " ) ; System . out . println ( " " + val + " + " + fraction + " = " + ( val . add ( fraction ) ) ) ; Fazit Rechnen mit Java Ganze Zahlen Gebrochene Zahlen Beliebiger Präzision 93 94 95 96 BigDecimal val = new BigDecimal ( " 1.0 e19 " ) ; BigDecimal fraction = new BigDecimal ( " 1.0 e -19 " ) ; System . out . println ( " " + val + " + " + fraction + " = " + ( val . add ( fraction ) ) ) ; // -> 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 Fazit Rechnen mit Java Beliebiger Präzision Ganze Zahlen Gebrochene Zahlen Fazit Rechnen mit Java Ganze Zahlen Gebrochene Zahlen Beliebiger Präzision Aber leider klappt auch in diesem Falle das Rechnen mit Rationalen Zahlen nicht: Fazit Rechnen mit Java Ganze Zahlen Gebrochene Zahlen Beliebiger Präzision 98 99 100 101 102 BigDecimal val = new BigDecimal ( " 100.0 " ) ; val = val . divide ( new BigDecimal ( " 3.0 " ) ) ; val = val . divide ( new BigDecimal ( " 3.0 " ) ) ; val = val . multiply ( new BigDecimal ( " 9.0 " ) ) ; System . out . println ( " ((100/3.0) /3.0) *9.0 = " + val ) ; Fazit Rechnen mit Java Ganze Zahlen Gebrochene Zahlen Beliebiger Präzision 98 99 100 101 102 103 BigDecimal val = new BigDecimal ( " 100.0 " ) ; val = val . divide ( new BigDecimal ( " 3.0 " ) ) ; val = val . divide ( new BigDecimal ( " 3.0 " ) ) ; val = val . multiply ( new BigDecimal ( " 9.0 " ) ) ; System . out . println ( " ((100/3.0) /3.0) *9.0 = " + val ) ; // -> java . lang . ArithmeticException : Non terminating decimal expansion ; no exact representable decimal result . Fazit Rechnen mit Java Ganze Zahlen Gebrochene Zahlen Beliebiger Präzision 105 106 107 108 109 BigDecimal val = new BigDecimal ( " 100.0 " ) ; val = val . divide ( new BigDecimal ( " 3.0 " ) ,20 , BigDecimal . ROUND_HALF_UP ) ; val = val . divide ( new BigDecimal ( " 3.0 " ) ,20 , BigDecimal . ROUND_HALF_UP ) ; val = val . multiply ( new BigDecimal ( " 9.0 " ) ) ; System . out . println ( " ((100/3.0) /3.0) *9.0 = " + val ) ; Fazit Rechnen mit Java Ganze Zahlen Gebrochene Zahlen Beliebiger Präzision 105 106 107 108 109 110 BigDecimal val = new BigDecimal ( " 100.0 " ) ; val = val . divide ( new BigDecimal ( " 3.0 " ) ,20 , BigDecimal . ROUND_HALF_UP ) ; val = val . divide ( new BigDecimal ( " 3.0 " ) ,20 , BigDecimal . ROUND_HALF_UP ) ; val = val . multiply ( new BigDecimal ( " 9.0 " ) ) ; System . out . println ( " ((100/3.0) /3.0) *9.0 = " + val ) ; // -> 9 9 . 9 9 9 9 9 9 9 9 99 9 9 9 99 9 9 9 99 0 Fazit Rechnen mit Java Beliebiger Präzision Ganze Zahlen Gebrochene Zahlen Fazit Rechnen mit Java Ganze Zahlen Gebrochene Zahlen Wir haben gesehen, dass das Rechnen mit den Primitiven Fazit Rechnen mit Java Ganze Zahlen Gebrochene Zahlen Wir haben gesehen, dass das Rechnen mit den Primitiven für ganze Zahlen innerhalb des Bereiches möglich ist Fazit Rechnen mit Java Ganze Zahlen Gebrochene Zahlen Wir haben gesehen, dass das Rechnen mit den Primitiven für ganze Zahlen innerhalb des Bereiches möglich ist für gebrochene Zahlen nicht den Erwartungen entspricht Fazit Rechnen mit Java Ganze Zahlen Gebrochene Zahlen Wir haben gesehen, dass das Rechnen mit den Primitiven für ganze Zahlen innerhalb des Bereiches möglich ist für gebrochene Zahlen nicht den Erwartungen entspricht Fazit Rechnen mit Java Ganze Zahlen Gebrochene Zahlen Wir haben gesehen, dass das Rechnen mit den Primitiven für ganze Zahlen innerhalb des Bereiches möglich ist für gebrochene Zahlen nicht den Erwartungen entspricht Es ist wichtig zu verstehen, dass dies kein Problem einer speziellen Programmiersprache ist, sondern das Problem der Computerarchitektur. Fazit Rechnen mit Java Ganze Zahlen Gebrochene Zahlen Wir haben gesehen, dass das Rechnen mit den Primitiven für ganze Zahlen innerhalb des Bereiches möglich ist für gebrochene Zahlen nicht den Erwartungen entspricht Es ist wichtig zu verstehen, dass dies kein Problem einer speziellen Programmiersprache ist, sondern das Problem der Computerarchitektur. Aber In Java gibt es die Möglichkeit Klassen zu schreiben, welche die rationale Mathematik exakt abbilden. Fazit