PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Rechnen in Java, Verständnisprobleme: wann casten, wie casten?


melo
2009-10-24, 18:53:14
Hallo!

Ich beschäftige mich gerade mit Java und blicke bis jetzt ganz gut durch, aber simple Rechenoperationen krieg ich immer nur durch Testen zum laufen.

Beispiel:

int x = 6000;
long y = 80000L;
float anteil = 100/y*i;

Erbringt eine Ganzzahl als Ergebnis, obwohl das natürlich keine ist. Durch testn kam ich dann auf

float anteil = (float)100/y*i;

Das funktioniert. Das nächste Problem ließ jedoch nicht lange auf sich warten:

int n = 5;
float sum = 0;
for(int i = 1; i <= n; i++) {
sum += 1/i*i;
}

Die Ausgabe liefert wieder nur Ganzzahlen. Logisch dachte ich, bei der 1 muss wie oben wieder ein float hin:

sum += (float)1/i*i;

Denkste, es funktioniert jetzt und sieht so aus:

sum += (float)1/float(i*i);



Ihr seht schon mein Problem. Kann mir jemand eine Regel formulieren, mit der ich OHNE Ausprobieren schon den richtigen Term hinschreiben kann? Von was hängt das jetzt denn ab?
Es liegt wohl daran, dass der gesamte Ausdruck als Integer betrachtet wird, das denk ich mir. Doch warum reicht im ersten Term ein (float)-Cast, im zweiten braucht man jedoch 2 davon? In beiden Termen gibt es 2 Ints...

Das ist momentan mein Haupproblem.

Noch was:

Bei double = 1/y*i reicht es glaube ich, statt die 1 mit (double) zu casten auch einfach 1.0 zu schreiben. Bei float funktioniert das aber wohl nicht. Wird bei Punkschreibweise automatisch immer double angenommen?


Danke!

Monger
2009-10-24, 19:01:46
Wie du schon richtig erkannt hast, interpretiert er den Ausdruck rechts vom "=" als Integer. Denn i ist ein Integer, und Zahlen wie 1 oder 100 genauso.

Iirc hat Java genauso einen Bezeichner, um zwischen Integer und Gleitkomma unterscheiden zu können. Der sollte "f" heißen, also:


float anteil = 100f/y*i;
sum += 1f/i*i;

Die Kommaschreibweise lässt sich ja nur als Float darstellen, deshalb nimmt er dort schon den richtigen Typus an. Entweder gewöhnst du dir also bei Gleitkommazahlen an, immer gleich noch die Nachkommastelle zu setzen (was technisch ohnehin korrekter ist. Nicht alle Integer Zahlen lassen sich im Gleitkommabereich abbilden), oder du schreibst halt einen passenden Suffix dazu.

Tiamat
2009-10-24, 19:02:51
Es reicht in beiden Fällen einfach zahl.0F anzugeben.

Coda
2009-10-24, 19:07:20
sum += (float)1/(i*i); muss da stehen - was äquivalent ist zu deinem sum += (float)1/float(i*i)

Bei sum += (float)1/i * i; kommt natürlich immer ein Ganzzahlwert raus, weil mathematisch einfach 1 dasteht.

Eine Multiplikation/Division/Addition/Subtraktion von float mit int ergibt immer float. Das steht so in der Java-Spezifikation. Und zumindest in C/C++ ist 1f als Float-Literal nicht zulässig, sondern nur 1.0f.

Alles in allem würde ich der Deutlichkeit zu liebe sum += 1.0f/float(i*i); schreiben.

Berni
2009-10-25, 16:37:29
Innerhalb einer Grundoperationen (a+b, a-b, a/b, a*b) wird immer der größte vorkommende Datentyp zur Berechnung verwendet. Die Ordnung lautet dabei so: short < int < long < float < double
Hast du also ein float und ein double, so kommt auch ein double raus.

Nun zu deinen Casts: Es ist hier dann auch noch wichtig, die Ausführungsreihenfolge zu beachten. Zunächst wird der Inhalt von Klammern berechnet, dann Punkt-vor-Strich angewendet und anschließend einfach von links nach rechts berechnet.
Deine Operation lautet:
sum += 1/i*i;
bzw. sum = sum + (1/i*i);
Java macht nun Folgendes:
1/i = int/int => int (nennen wir es x)
x*i = int/int => int (nennen wir es y)
sum + y = float + int => float
Da zur Berechnung der y-Variable nur Integer-Berechnungen durchgeführt werden ist eben auch die Genauigkeit nur Integer.

creave
2009-10-25, 16:44:18
Innerhalb einer Grundoperationen (a+b, a-b, a/b, a*b) wird immer der größte vorkommende Datentyp zur Berechnung verwendet. Die Ordnung lautet dabei so: short < int < long < float < double
Hast du also ein float und ein double, so kommt auch ein double raus.


Fast richtig:

byte + byte = int
short + short = int

melo
2009-11-07, 11:46:24
Es scheint so einfach zu sein, aber ich habs immernoch nicht ganz begriffen.


Eine Multiplikation/Division/Addition/Subtraktion von float mit int ergibt immer float.


Innerhalb einer Grundoperationen (a+b, a-b, a/b, a*b) wird immer der größte vorkommende Datentyp zur Berechnung verwendet.


Also das erste, was ich wohl falsch verstanden habe: wenn ich einen ewig langen Term habe, jetzt mal ohne explizite casts und Klammern, sondern einfach nur mit vorher erstellten floats, ints, doubles und den Grundoperatoren - dann wäre es falsch zu sagen, es kommt double als Ergebnis raus, nur weil irgendwo im Term mal double vorkam?

Immernoch das Beispiel von oben:

int i = 4;
float sum;
sum += 1.0f/(i*i);

Das funktioniert prima - links ein float-literal, rechts zwei ints dank Klammer zu einem "verschmolzen". Und wie ihr schon sagtet, float mit int ergibt immer float.

Wenn die Berechnung jetzt aber von links nach rechts ausgeführt wird, warum klappt dann

sum += 1.0f/i*i;

nicht? Mal und Geteilt haben die gleiche Priorität 12, es geht von links nach rechts. Er müsste demnach doch erst 1.0f/i rechnen -> float/int, es kommt float raus - und dieses Zwischenergebnis dann nochmal mit i multiplizieren, also float * int.

Berni hat was dazu geschrieben, er nimmt aber die 1 als integer an, ich habe aber ja 1.0f angegeben.


Hm?

Gast
2009-11-09, 13:48:01
Immer alles kreuz und quer zu casten ist der falsche Ansatz. Du solltest gleich die Variablen im richtigen Datentyp deklarieren.
Wenn du Dinge hast, die von Natur aus ganzzahlig sind z.B. eine Byte Angabe oder die Anzahl an Objekten, die Länge einer Zeichenkette oder überhaupt nur einen Schleifenzähler, dann nimm einen Integer.
Wenn du eine Variable hast, die von Natur aus eine Gleitkommazahl ist z.B. das Gewicht einer Person, die Geschwindigkeit von einem Objekt, ein Geldbetrag etc, dann verwende hierfür einen Double, auch wenn du momentan nur Ganzzahlen einfüllst. Damit lösen sich die meisten Probleme schon in Luft auf.
Einen float würde ich wegen der hohen Ungenauigkeit schon überhaupt nicht verwenden. Da kommt nur in den wenigsten Fällen was Gutes dabei raus. Der Datentyp ist mehr aus historischen Zeiten, wo Doubles sehr lange gebraucht haben um berechnet zu werden und die Rechenleistung des Systems noch sehr knapp war.

AlSvartr
2009-11-09, 14:31:50
Trifft zwar hier nicht zu, aber es gibt auch jetzt noch entsprechende Architekturen, wo man mit float deutlich besser fährt (z.B. auf der SPE-Seite des Cell oder bei vielen GPU-Geschichten).

Pinoccio
2009-11-09, 14:52:48
Fast richtig:

byte + byte = int
short + short = intJa (http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.2.2), aber immernoch unvollständig. Es kommt immer mindestens int raus, außer es war ein long beteiligt, dann kommt long raus. Also auch char + char = int und gemischt (byte + short + char = int) ebenfalls.
Zur Ausgangsfrage: am besten garnicht, eben weils doof ist. Denn dummerweise verliert man mit etwas Pech Information*. Besser ist es, im Programm genau zu überdenken, welche Datentypen man nutzt.

mfg