# Double ungenau



## papabaer1707 (23. Mrz 2011)

Ich möchte ein Programm schreiben (Konsole), welches eine Wertetabelle für eine Funktion ausgibt (hier x²). Startwert, Endwert und Schrittweite sollen eingegeben werden. Alle Variablen wurden als double gekennzeichnet - als Ergebnis bekomme ich dies (siehe Bild).


```
public class wertetabelle {
  public static void main(String[] args) {
    double startwert;
    double endwert;
    double schrittweite;
    double funktionswert;

    System.out.print("Gib den Startwert ein: ");
    startwert = Input.readDouble();
    System.out.print("Gib den Endwert ein: ");
    endwert = Input.readDouble();
    System.out.print("Gib die Schrittweite ein: ");
    schrittweite = Input.readDouble();
    
    for (double i = startwert; i <= endwert; i += schrittweite) {
      funktionswert = i*i;
      System.out.println("\t " + i + "\t | " + funktionswert);
    }
  }
}
```

Weshalb gibt es diese Ungenauigkeiten? Wie kann ich das abstellen?


----------



## W9ND3R (23. Mrz 2011)

Allgemein:
Der Typ double ist eine Gleitkommazahl mit einer Größe von 8 Byte. Die Genauigkeit liegt bei 15 Nachkommastellen

Hier was aus der JavaInsel dazu.

Ansonsten kannst auch noch BigDecimal-Werte verwenden, wenn du die Genauigkeit sichern willst. Dazu hier die API.
Gruß.


----------



## papabaer1707 (23. Mrz 2011)

Hm, wenn ich aber von -5 bis 5 je 0.2 addiere dürfte das die Genauigkeit nicht überbeanspruchen ...

Mir würden aber z. B. 5 Nachkommastellen genügen ... mit ROUND kann ich das erledigen. Mich interessiert aber, weshalb er diese Abweichungen hat.


----------



## Marco13 (23. Mrz 2011)

In diesem Fall geht es vermutlich nicht um die Genauigkeit. Es gibt nur SEHR wenige Fälle, wo double nicht genau genug ist. Vermutlich geht es nur um die "häßliche" Ausgabe. Siehe String#format bzw. Formatter (Java 2 Platform SE 5.0)


----------



## Marco13 (23. Mrz 2011)

Gleichzeitig. 
Das Problem ist, dass bestimte Zahlen mit double einfach nicht genau dargestellt werden können, so wie z.B. 1/3 im dezimalsystem nicht genau dargestellt werden kann, sondern nur als 0.3333333333333333333333333333333333333333....  Siehe auch Gleitkommazahl ? Wikipedia und hunderte(!) Forenbeiträge hier.


----------



## Ark (23. Mrz 2011)

papabaer1707 hat gesagt.:


> Weshalb gibt es diese Ungenauigkeiten? Wie kann ich das abstellen?


Gleitkommazahlen sind prinzipiell ungenau, und eine der Ungenauigkeiten im Umgang mit Dezimalzahlen ist mit ihrer Basis begründet: Dezimalzahlen (das sagt schon das Wort) haben die Basis 10, Binärzahlen haben die Basis 2. Da double und float zu Letzterem gehören, können sie nur Zahlen exakt aufnehmen, deren Binärdarstellung endlich und auf eine bestimmte Anzahl von Binärziffern (bei double höchstens 53, bei float höchstens 24) begrenzt ist.

Die Zahl 0.2 zur Basis 10 hat aber zur Basis 2 eine unendliche periodische Ziffernfolge, deswegen können weder float noch double diese Zahl verlustfrei aufnehmen.

"Abstellen" in dem Sinne kann man das nicht. Man kann nur anders rechnen (z.B. mit anderer Skalierung, gemeinen Brüchen oder Implementierungen wie BigDecimal) oder, wenn das erlaubt und möglich ist, intern mit diesen Fehlern weiterrechnen und lediglich zur Anzeige auf passende Genauigkeiten runden.

Ark


----------



## papabaer1707 (23. Mrz 2011)

OK, schönen Dank, ich denke, ich bin im Bilde ...

Habs jetzt so gelöst ...


```
System.out.printf("%10s |%10s%n" , "x-Wert", "y-Wert");
    System.out.println("------------------------");
    for (double i = startwert; i <= endwert; i += schrittweite) {
      funktionswert = i*i;
      System.out.printf("%10.2f |%10.2f%n" , i, funktionswert);
    }
```


----------



## Andi_CH (24. Mrz 2011)

Lässt sich das Folgende auch irgendwie erklären?

10.0 "beliebig" genau
0.2 ist auch "beliebig" genau

und doch zeigt sich schon nach der zweiten Substraktion eine Abweichung die viel grösser als zu erwarten ist obwohl auch der Wert 9.6 "beliebig" genau dargestellt werden kann, wie unten gezeigt wird.

PS: bitte nicht über "beliebig" diskutieren  damit möchte ich nur sagen, dass es viel genauer als die Abweichung ist.


```
String format = "%5.100f";
double d1 = 10.0;
double d2 = 0.2;
System.out.println(String.format(format, d1));
System.out.println(String.format(format, d2));
for (int i=0; i<5; i++) {
	d1 -= d2;
	System.out.println(i + " : " + String.format(format, d1));
}
d1 = 9.6;
System.out.println(String.format(format, d1));
```

Output:

```
10.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0.2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0 : 9.8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
1 : 9.6000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000
2 : 9.4000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000
3 : 9.2000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000
4 : 9.0000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000
9.6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
```


----------



## tfa (24. Mrz 2011)

> 0.2 ist auch "beliebig" genau


Im Binärsystem ist 0.2 ein periodischer Dezimalbruch, und damit (intern) nicht beliebig genau. (Wenn ich mich nicht verrechnet habe ist 0,2=0,00101001010010100101...).
Den Fehler sieht man erst, wenn er durch Berechnungen angehäuft hat. Du kannst in deinem Test d2 ja mal den Wert 0,03125 geben. Diese Zahl kann man im Binärsystem exakt darstellen.


----------



## Andi_CH (24. Mrz 2011)

Die Frage ist eigentlich warum 0.2 mit so vielen 0 ausgegeben wird - bringt dann String.format nicht die Wahrheit zu Tage?


----------



## tfa (24. Mrz 2011)

Der Ausgabe-Algorithmus rundet ja auch bzw. benutzt eine gerundete Darstellung. Und am Anfang ist der Unterschied zum exakten Wert eben zu gering, um aufzufallen.

Die Wahrheit kannst Du so zu Tage fördern:


```
System.out.println(Long.toBinaryString((Double.doubleToLongBits(0.2))));
System.out.println(Long.toBinaryString((Double.doubleToLongBits(0.03125))));
```
liefert


> 11111111001001100110011001100110011001100110011001100110011010
> 11111110100000000000000000000000000000000000000000000000000000


(Hab mich oben wohl doch verrechnet)


----------



## Marco13 (24. Mrz 2011)

Die vielen Nullen liegen aber auch am verwendeten Format, "%5.100f" - 100 Nachkommastellen, auch wenn's nur Nullen sind.


----------



## tfa (24. Mrz 2011)

Mehr als 16 Stellen sind bei Double sowieso nicht möglich, auch wenn im Format 1000 steht...


----------



## Marco13 (24. Mrz 2011)

_Signifikante_ Stellen:
double d2 = 12345e-50;
->
0,0000000000000000000000000000000000000000000001234500000000000000000000000000000000000000000000000000


----------



## tfa (24. Mrz 2011)

Ja, Stellen != Nachkommastellen.


----------



## Andi_CH (24. Mrz 2011)

Hm es muss die 16. Stelle sein.


```
1.0/3.0   = 0.3333333333333333000000000000000
1.0/3.0   = 0.6666666666666666000000000000000
100.0/3.0 = 33.333333333333336000000000000000
```


----------

