# Java rechnent ungenau, problemlösung?



## regic (20. Feb 2012)

Hallo Java-Programmierer,

Ich habe mir einen Taschenrechner programmiert, und habe dabei leider feststellen müssen das Java an sich ungenau rechnet.  z.b 0.03 * 180  herauskommen müsste 5,4 aber java bekommt 5,3999999999999

woran liegt das genau, und wie kann ich das beheben? ohne einfach zu runden!!!

p.s. dieser rechenfehler liegt nicht an meinem Taschenrechner, es ist 100% eine Java sache!!

mfg


----------



## Gast2 (20. Feb 2012)

Das liegt daran:
Floating point - Wikipedia, the free encyclopedia
Kurz gesagt: Nicht alle Zahlen können intern genau dargestellt werden.

Lösung: Beispielsweise [JAPI]BigDecimal[/JAPI]


----------



## irgendjemand (20. Feb 2012)

@TO
FALSCH
das hat nicht wirklich was mit JAVA zu tun ... sondern damit wie DEIN RECHNER intern floating point zahlen darstellt ...
und ein rechner kann nun mal kein 0.03 darstellen sondern nur 3 x 10^-2 ... und je komplexer die zahlen sind desto ungenauer wird das auch ...

ergo : such dir einen datentyp der dieses problem nicht hat ...

und da du mit floating point zahlen arbeitest bietet sich da java.math.BigDecimal an ... der hat solche probleme nicht weil intern einiges anderst gerechnet wird als man es von einem normalen 386er erwarten würde ...


----------



## nocturne (20. Feb 2012)

öm


----------



## regic (20. Feb 2012)

ja, ist schon genauer aber geht es nicht noch genauer? ich meine das beispiel "0.03*180" rechnet er jetzt ja richtig, aber je weiter rechts die Kommazahlen sind desto ungenauer wird es wieder.
Also was ist die genauste Methode bzw. der genauste Datentyp.
Oder soll ich mir jetzt einen eigenen Datentyp schreiben?


----------



## Schandro (20. Feb 2012)

regic hat gesagt.:


> ja, ist schon genauer aber geht es nicht noch genauer? ich meine das beispiel "0.03*180" rechnet er jetzt ja richtig, aber je weiter rechts die Kommazahlen sind desto ungenauer wird es wieder.
> Also was ist die genauste Methode bzw. der genauste Datentyp.
> Oder soll ich mir jetzt einen eigenen Datentyp schreiben?


dafür kannst du doch ein MathContext dem Konstruktor übergeben...


----------



## Wildcard (20. Feb 2012)

> ich meine das beispiel "0.03*180" rechnet er jetzt ja richtig, aber je weiter rechts die Kommazahlen sind desto ungenauer wird es wieder.


Nein, das ist so nicht richtig. Gleitkommazahlen sind sehr genau auch für extreme kleine Zahlen, aber die Matisse ist begrenzt, daher lassen sich für große Zahlen nur wenige bis keine Nachkommastellen damit abbilden.
Zu wissen wie Gleitkommazahlen arbeiten gehört für Entwickler zum Handwerkzeug.



> Also was ist die genauste Methode bzw. der genauste Datentyp.


Der genauste Datentyp ist BigDecimal, allerdings ist BigDecimal sehr viel langsamer und braucht mehr Speicher. Man muss also wissen wann man BigDecimal einsetzen sollte und sich vorher überlegen mit welcher Genauigkeit die Rechnung erfolgen soll und welche Rundungsstrategie benötigt wird.


----------



## Marco13 (20. Feb 2012)

Bei einem Taschenrechner dürfte im Hinblick auf die Performance das Rennen zwischen dem Zahlen-anklickenden Benutzer und dem gelangweilt surrenden PC wohl mit einer Sehnenscheidenentzündug des ersteren enden  Aber natürlich sollte man das allgemein berücksichtigen - und auch, dass das Programmieren mit BigDecimal schrecklich "unbequem" ist. (In der "TODO TODO TODO.txt" auf meinem Desktop steht zwar "Create BCEL converter from double to DigDecimal" aber das war nur so ein Gedanke...  ). Es gibt nur wenige Stellen, an denen man wirklich mehr als [c]double[/c] _braucht_....


----------



## Wildcard (20. Feb 2012)

> Bei einem Taschenrechner dürfte im Hinblick auf die Performance das Rennen zwischen dem Zahlen-anklickenden Benutzer und dem gelangweilt surrenden PC wohl mit einer Sehnenscheidenentzündug des ersteren enden


Absolut. Ich wollte nur vermeiden das regic vorschnell zur Ansicht gelangt "genauer ist besser" und fortan nur noch BigDecimals im Code hat. Alles schon erlebt


----------



## Marco13 (20. Feb 2012)

Volle Zustimmung. BigDecimal wird IMHO auch oft "vorschnell" empfohlen (genau wie RegEx :autsch: ). Ein Taschenrechner könnte einer der wenigen Fälle sein, wo es angebracht ist, wenn man das erklärte Ziel hat, "beliebig genau" zu rechnen, aber der Aufwand im Vergleich zu double ist schon immens.


----------



## irgendjemand (20. Feb 2012)

@Marco
wobei RegEx einem das leben deutlich erleichtern können ...
klar sind es immer mindestens 2 zeilen mehr die man braucht ... aber ist deutlich bequemer als wenn ich mit mit substring und charAt sonstwas zusammen zimmere ...
von daher trifft da dein "vorschnell" nicht so zu ...

was BigDecimal angeht
ich denke ist in diesem fall berechtigt da es das system von TO nicht mal schafft 0.03 "richtig" darzustellen bzw es richtig mit 180.0 zu multiplizieen ...
ich habs jetzt nicht ausprobiert ... aber ich bin der meinung das mein system das schafft *warum und was der unterschied an den systemen ist weis ich nicht ... aber das er es packt dürfte fest stehen* ...

und da TO explizit nach einem "genauen" datentyp für diese rechnung gefragt hat wäre hier BigDecimal wohl die gesuchte antwort da normale float/double bei ihm nicht zum gewünschten ergebnis führen ...


@TO
warum BigDecimal bei dir immer noch "ungenaue" werte liefert ist schon seltsam ... da könnte wirklich was mit der FPU deiner CPU nicht ganz stimmen ...


----------



## Wildcard (20. Feb 2012)

> ich denke ist in diesem fall berechtigt da es das system von TO nicht mal schafft 0.03 "richtig" darzustellen bzw es richtig mit 180.0 zu multiplizieen .


Hängt doch wohl sehr davon was ausgerechnet werden soll und ob Rundung erlaubt und gewünscht ist.
BigDecimal braucht man *fast* nie. Primär wissenschaftliche Berechnungen, Finanzmathematik (mit festgelegter Präzision und Rundungssemantik) und der allseits beliebte Taschenrechner.



> ich habs jetzt nicht ausprobiert ... aber ich bin der meinung das mein system das schafft *warum und was der unterschied an den systemen ist weis ich nicht ... aber das er es packt dürfte fest stehen* ...


Gleitkommazahlen sind nach IEEE spezifiziert und immer gleich. CPUs implementieren aber nicht exakt IEEE, sonder haben oft ein paar zusätzliche bit. Aus performance Gründen delegiert Java direkt an die CPU und nimmt das Ergebnis so hin. Wenn erforderlich ist das eine Berechnung auf jedem System ein identisches Ergebnis liefert ist das langsamere (und ungenauere) strictfp Keyword zu verwenden.


----------



## irgendjemand (21. Feb 2012)

ich weis ... hat weder was mit dem thema zu tun noch mag es relevant sein ... aber es ist in meinem bekanten-kreis schon öfter vorgekommen das gerade bei floating point auf unterschiedlichen rechnern unterschiedliche ergebnisse zu stande kamen ... und das obwohl wie du ja sagt eigentlich das ergebnis fest definiert ist ...

FDIV-bug kann ich ausschließen das dieser *zumindest meines wissens nach* ja nur bei intel-cpus auftritt ... die test-systeme waren aber *damals zu mindest* fast ausschließlich amd-cpus ...

wirklich verwunderlich war nur das diese abweichungen sowohl in höherwertigen bits als auch in niederwertigen bits auftraten ... auf einem system gab es sogar bei der selben rechnung abweichungen auf grund des takt-zustandes ...


naja wie dem auch sei ...

zum thema selbst sollte klar sein das die von TO angemerkten abweichungen dadurch auftreten weil floating point zahlen nun mal in spezielle binär-darstellung haben ... welche meist bei vermeintlich "einfachen" dingen wie 0.1 + 0.1 != 0.2 auftreten ...


----------



## Landei (21. Feb 2012)

irgendjemand hat gesagt.:


> ich weis ... hat weder was mit dem thema zu tun noch mag es relevant sein ... aber es ist in meinem bekanten-kreis schon öfter vorgekommen das gerade bei floating point auf unterschiedlichen rechnern unterschiedliche ergebnisse zu stande kamen ... und das obwohl wie du ja sagt eigentlich das ergebnis fest definiert ist



"Überall das gleiche Ergebnis" kann man mit dem Schlüsselwort [c]strictfp[/c] erzwingen (obwohl mir das in 10 Jahren mehr oder weniger professioneller Programmierung noch nie in freier Wildbahn begegnet ist): strictfp - Wikipedia, the free encyclopedia


----------



## javapower (21. Feb 2012)

Kann jemand genauere Angaben machen, damit in Java bei 0.03 * 180.0 auch 5.4 als Ergebnis liefert?


----------



## EnHancEd[] (21. Feb 2012)

```
public class Nummer 
{
	public static void main(String[] args){
		
	
	float zahl1= (float) 0.03;
	double zahl2=  180.0;
	float ergebnis=  (float) (zahl1*zahl2);
	
	System.out.println(ergebnis);
	
	
}
```


Also bei mir kommt da 5,4 raus.


----------



## javapower (21. Feb 2012)

EnHancEd[];867417 hat gesagt.:
			
		

> Also bei mir kommt da 5,4 raus.



Im Buch Effective Java Programming von J. Bloch steht:

"Item 31: Avoid float and double if exact answers are required"

und weiter

"The right way to solve this problem is to use BigDecimal, int, or long for monetary calculations."

Was ist mit

System.out.println(BigDecimal.valueOf(3, 2).multiply(new BigDecimal("180")));

Liefert 5.40!


----------



## EnHancEd[] (21. Feb 2012)

So wie es da steht machen?
Die Variablen umdefinieren, ausprobieren, ausrechnen & Rückmeldung geben ob es geklappt hat.

p.s nächstes mal früher den Hinweis geben, ich habe das Buch nicht zur Hand.

Greetz


----------



## irgendjemand (21. Feb 2012)

EnHancEd[];867417 hat gesagt.:
			
		

> ```
> public class Nummer
> {
> public static void main(String[] args){
> ...



sorry ... aber was soll das rumgecaste denn da bitte ?

entweder du nutzt float ... oder double ... aber beides mischen und dann durch casts ? ... da hat wohl jemand beim studium gepennt


----------



## EnHancEd[] (21. Feb 2012)

Ich habe das absichtlich einmal mit float und einmal mit double gemacht um ihm zu zeigen, dass es ein "rumgecaste" gibt.

Zudem um den Unterschied von float (sehr kleine Kommazahlen) und double (Größeren) zu verdeutlichen.

Abgesehen davon kannst du es nicht mischen, wenn du nicht castest 

[EDIT]Edit: Selbst wenn du es nur mit float machen würdest, müsstest du casten:

```
public static void main(String[] args){
	        
	    
	    float zahl1= (float) 0.03;
	    float zahl2=  (float) 180.0;
	    float ergebnis=  (zahl1*zahl2);
	    
	    System.out.println(ergebnis);
	    
	    
	}
```
und wenn du es nur mit double machst, kommen sowieso keine 5,4 raus[/EDIT]


greetz


----------



## truesoul (21. Feb 2012)

EnHancEd[];867529 hat gesagt.:
			
		

> Ich habe das absichtlich einmal mit float und einmal mit double gemacht um ihm zu zeigen, dass es ein "rumgecaste" gibt.
> 
> Zudem um den Unterschied von float (sehr kleine Kommazahlen) und double (Größeren) zu verdeutlichen.
> 
> ...



Sorry, aber Bullsh*t. Dein Beispiel zeigt nichts *AUSSER *das dort _fast _alles falsch gemacht wird, was falsch gemacht werden kann. 
Dein "rumgecaste" ist tatsächlich nur rumgecaste, vorallem weil hier von garkein casten die rede war. 
Und schonmal was von float zahl1= 0.03f; gehört oder gelesen?


----------



## regic (21. Feb 2012)

Ja, das habe ich auch schon herausgefunden das bei float das richtige rauskommt aber bei double nicht.

und Danke Profis D, und auch danke das ihr das alles auch gut begründet habt warum das nun so rechnet, ist als Anfänger immer sehr gut, wenn man alles erklärt bekommt D

:toll:


----------



## xehpuk (21. Feb 2012)

EnHancEd[];867529 hat gesagt.:
			
		

> Edit: Selbst wenn du es nur mit float machen würdest, müsstest du casten:


Da du dort ja auch doubles hast. Schau dir mal die Basics an: Primitive Data Types (The Java™ Tutorials > Learning the Java Language > Language Basics)


----------



## Landei (21. Feb 2012)

EnHancEd[];867529 hat gesagt.:
			
		

> [EDIT]Edit: Selbst wenn du es nur mit float machen würdest, müsstest du casten:
> 
> ```
> public static void main(String[] args){
> ...



Das ist schlichtweg falsch, es sind keine Casts notwendig:


```
public static void main(String[] args){
	        
	    
	    float zahl1= 0.03f;
	    float zahl2=  180.0f;
	    float ergebnis=  (zahl1*zahl2);
	    
	    System.out.println(ergebnis);
	    
	    
	}
```

Neben dem Suffix 
	
	
	
	





```
f
```
 für 
	
	
	
	





```
float
```
 gibt es noch 
	
	
	
	





```
d
```
für 
	
	
	
	





```
double
```
 und 
	
	
	
	





```
L
```
 für 
	
	
	
	





```
long
```
 (Groß- oder Kleinschreibung spielt keine Rolle, aber bei 
	
	
	
	





```
long
```
 sollte man aus Lesbarkeitsgründen natürlich 
	
	
	
	





```
L
```
 nehmen)


----------



## EnHancEd[] (21. Feb 2012)

cast & f kommt wohl auf das gleiche raus.
(double) -> d
(float) -> f

wieso sollte ich was nachlesen? stimmt ja so.


----------



## EnHancEd[] (21. Feb 2012)

xehpuk hat gesagt.:


> Da du dort ja auch doubles hast. Schau dir mal die Basics an: Primitive Data Types (The Java™ Tutorials > Learning the Java Language > Language Basics)



Verstehe nicht, was du mir damit sagen willst ???:L

Das Beispiel war mir float, nicht doubles. Und jmd zu sagen, er solle sich die Grundlagen anschauen der das schon n weilchen macht.. hmm :lol:


----------



## EnHancEd[] (21. Feb 2012)

truesoul hat gesagt.:


> Sorry, aber Bullsh*t. Dein Beispiel zeigt nichts *AUSSER *das dort _fast _alles falsch gemacht wird, was falsch gemacht werden kann.
> Dein "rumgecaste" ist tatsächlich nur rumgecaste, vorallem weil hier von garkein casten die rede war.
> Und schonmal was von float zahl1= 0.03f; gehört oder gelesen?



wie gesagt, ob nun f oder (float) kommt so ziemlich auf dasselbe Ergebnis raus.

greetz


----------



## xehpuk (21. Feb 2012)

Nun ja, offensichtlich hast du die Basics nicht verstanden.

Was kommt wohl hier raus?

```
public static void main(final String[] args) {
	float a = 1E40F;
	float b = (float) 1E40;
	System.out.println(a == b);
}
```

Du schreibst doch nicht etwa auch immer sowas?

```
int i = (int) 0L;
int j = (int) 0.0;
```


----------



## truesoul (21. Feb 2012)

EnHancEd[];867657 hat gesagt.:
			
		

> wie gesagt, ob nun f oder (float) kommt so ziemlich auf dasselbe Ergebnis raus.
> 
> greetz



Siehe Beitrag über mir


----------



## irgendjemand (21. Feb 2012)

schön das ich wenigstens mal zur abwechslung nicht der einzige ist der anmerkt das "EnHancEd[]" die basics scheinbar nicht ganz verstanden hat ...

und da bereits alles gesagt wurde *sehr schön fand ich vor allem das zweite beispiel von "xehpuk"* habe ich dem auch nichts weiter hinzuzufügen außer :

@EnHancEd[]

vielleicht solltest du die ratschläge ernst nehmen und dich selbst mal damit befassen anstatt einem *vermutlich* noch schlechterem anfänger so einen totalen käse beibringen zu wollen ...
mal davon abgesehen das auch so um die 5 conventions verletzt hast ist es eben NICHT egal ob "(float) 1.0" oder "1.0f" ... aber was red ich da ... du weist es ja anscheint besser als wir alle zusammen

BTW : multi-posts sind hier unerwünscht ... versuche also nächste mal deine antwort in EINEN post zu packen an statt in 3 auf ein ander folgenden ...


----------



## EnHancEd[] (22. Feb 2012)

Ich denke es ist wichtiger, die Basics von OOP drauf zu haben, nicht irgendeine Typkonvertierung die man zur Not nachschauen kann, habe lange nichtsmehr mit formatieren gemacht, dennoch kam bei dem Beispiel bei mir das selbe raus. Deswegen sage ich, dass bei diesem Beispiel es nichts ausgemacht hat, ob man nun f oder (float) schreibt. [EDIT]Beides vermischen sollte man nicht.
[/EDIT]
@irgendjemand: 5 conventions?
Klar hätte man das auch mit f,d,L machen können, aber so funktioniert es auch.


----------



## x22 (22. Feb 2012)

man hätte das natürlich anders machen können, jedoch finde ich es nicht in Ordnung, wenn hier jeder so krass auf eine Person einhackt. Das grenzt ja schon an Cybermobbing :noe:
OOP Grundlagen finde ich auch wichtiger, Typkonvertierungen hmm habe ich vor Jahren mal angeschaut..

Das zeigt, dass manche doch nicht so reif sind, wie sie sich geben..

Back to Topic: Vermischen sollte man sie auch nicht..

Best regards, 
x22


----------



## Sonecc (22. Feb 2012)

x22 hat gesagt.:


> man hätte das natürlich anders machen können, jedoch finde ich es nicht in Ordnung, wenn hier jeder so krass auf eine Person einhackt. Das grenzt ja schon an Cybermobbing :noe:



Das Problem ist, dass Enhanced[] dauernd mit irgendwelchen obskuren, verwirrenden und teils sinnlosen oder gar falschen beispielen kommt. Wenn man ihm dann erklären will, was an seinen Beispielen falsch ist, kommt die gleiche reaktion wie hier. Nämlich ein patziges "Ist mir doch egal, es funktioniert doch"
Dass diese Einstellung nicht richtig ist, sollte jedem klar sein.

OOP zu verstehen mag wichtig sein.
Das Typensystem ist aber ebenfalls wichtig. Wenn ich jemandem im Bezug auf die Datentypen helfen will und ihm ein Beispiel liefere, sollte ich genug Ahnung davon haben.
Die Aussage: 



> Ich denke es ist wichtiger, die Basics von OOP drauf zu haben, nicht irgendeine Typkonvertierung die man zur Not nachschauen kann



Ist daher gerade in diesem Thema fehl am Platz, denn in der Problemstellung geht es nicht um OOP sondern um Datentypen.
Dem TO war am besten damit geholfen, dass man ihm erklärt, wie Gleitkommazahlen funktionieren und was für alternativen sich bieten. Diffuse Beispiele mit zig Casts (die nunmal unnötig sind und am Beispiel von Xehpuk gesehen sogar teilweise falsch bzw. gefährlich sind) helfen dabei kaum bis gar nicht.




> Und jmd zu sagen, er solle sich die Grundlagen anschauen der das schon n weilchen macht.. hmm



Scheinbar hat er nunmal nicht die Grundlagen, die er ja schon angeblich ein weilchen hat, wenn man obige Aussage beachtet. Ein weiterer Grund, warum diese Diskussion stattfindet.


----------



## x22 (22. Feb 2012)

Das mag alles sein, dann soll ein Moderator die Beiträge löschen, die fehl am Platz sind..
Aber wie gesagt, finde es nicht in Ordnung wenn man so auf einer Person rumhackt, jeder hat mal früher Fehler gemacht..

Will jetzt hier keine Diskussion anregen, bin nur auf den Thread gestoßen und musste meinen Senf dazu geben..

Best regards,
x22


----------

