Arithmetik Probleme

Hallo,

Ich werde aus diesen Rechenbeispielen nicht schlau

Nach meinen Verständnis müsste bei allen vier Beispielen das selbe herauskommen. Was macht es für einen Unterschied, ob ich 1 oder 1.0 hernehme? Ok, das eine ist ein Integer, und das andere Double, aber es müsste trotzdem jeweils das gleiche rauskommen. Die 1/1.0 im vierten Beispiel (das einzige richtige) erscheint nochmal besonders sinnlos.

Tipp: Die Verwendung von 1.0 erzwingt eine Konvertierung der beteiligten Variablen in doubles…sonst berechnet Java nämlich 8/3=2

Ups, verstehe. Aber warum ist dann das 3 Beispiel mit 1.0/(i*i) falsch?

Weil im dritten Beispiel nicht 1.0 im Nenner steht, sondern 1. Dadurch wird es zu einer Ganzzahldivision, deren Resultat immer 0 ist, wenn i > 1 (Beispiel: 1/6 ist bei einer Ganzzahldivision 0)

AAAH sorry, hab die Beispiele falsch gezählt. Kleinen moment noch für die richtige Antwort bitte!

Pirmin war zwar schneller siehe nächster Beitrag, aber der Vollständigkeit halber:
Sehr gemein. Bis zu einer gewissen Größe von i funktioniert das 3. Beispiel sogar. Dann aber plötzlich nicht mehr. Das liegt daran, dass das Ganzahlquadrat i*i den Wertebereich von Integer hat. Dieser wird bei großen Zahlen verlassen. Durch voranstellen von 1.0 ist wird der Wertebereich von Double verwendet, der sehr viel höhere Zahlen erlaubt.

Das dritte beispiel ist richtig, wenn i² nicht zu groß wird. Weil für zu große i, hast du mit i² einen überlauf. Das Ergebnis von i² ist dann nicht korrekt und die division wird dann mit einem falschen Nenner durchgeführt. Als double hast du natürlich in dieser Größenordnung noch keinen überlauf

Im vierten Beispiel werden die i’s wegen der 1.0 zu einem double gecastet damit die Multiplikation sinnvoll durchgeführt werden.

Im dritten beispiel wird erst i*i berechnet und dann erst bei der division zu double gecastet.

Achso, verstehe. Java scheint bei einem Überlauf automatisch Infinity als Wert zuzuweisen. Auch gut zu wissen!
Hab’s jetzt glaube ich begriffen. Danke für alle Antworten!

Java scheint bei einem Überlauf automatisch Infinity

Beim Überlauf des int (der ursächlich für den Fehler ist) nicht. Operationen, die ein int zum Überlauf bringe,n führen schlicht zu falschen Ergebnissen (manchmal negativ, manchmal positiv, je nachdem, wie die resultierende Bitfolge in dem jeweiligen int gerade aussieht).

Achso, ok. Warum steht dann aber unter dem dritten Beispiel Infinity?

Also ab einem bestimmten i bekommst du zunächst Abweichungen zum richtigen Ergebnis raus.
Das ist der fall solange n<65536 ist.
Für n=65536 ist aufgrund des überlaufs i*i=0.
Und was ist 1/0? mathematisch gesehen nicht definiert, aber lim(1/x) konvergiert gegen unendlich, wenn man x vom positiven gegen null streben lässt.
Und wenn ein summand unendlich ist, spielt es keine rolle mehr welchen wert die anderen Summanden einnehmen. Es bleibt unendlich.

Mei, stimmt, das hab ich komplett übersehen, Danke!
Das Skript ist da nicht besonders gut gemacht. Ich mein, einfach “falsch” hinzuschreiben… klar, wenn ich zu große Werte nehme, dann kommt nichts gescheites raus, aber das gleiche gilt auch für double, nur dass man halt grössere Werte nehmen kann bevor es zum Überlauf kommt.
Bei n=65536, müsste i*i aber normal erst die kleinste negative Zahl annehmen, also eine 1 mit 31 nullen (Zweierkomplement). Das heißt wenn die recheninterne Multiplikation so funktioniert wie ich mir das vorstelle.

[QUOTE=koaplannet]
BTW, müsste es normal nicht zu einer Endlosschleife kommen, also garkein Ergebnis?[/QUOTE]
Ich sehe keinen Grund wieso es zu einer Endlosschleife kommen sollte. Sie bricht doch immer ab, sobald i>n ist.

Und bei n=65536, müsste i*i normal erst die kleinste negative Zahl annehmen, also eine 1 mit 31 nullen (Zweierkomplement). Das heißt wenn die recheninterne Multiplikation so funktioniert wie ich mir das vorstelle.

Wie sieht denn deine Rechnung dazu aus?

Wieso sollte man hier überhaupt quadrieren?

    public static double calcPi(int n) {
        double sum = 0;
        long s = 0;
        for(int i = 1, k = 1; i < n; i++) {
           s += k;
           k += 2;
           sum += 1.0/s;
        }
        return Math.sqrt(sum*6);
    }

@Pirmin : Sorry, hab deine Antwort nicht gesehen.

Stimmt, sorry da hab ich grad i und n vertauscht…

Wieder sorry, ich habe vergessen das die Zählung der bits bei 0 beginnt. Also i*i = 0 für i=2^16 stimmt wohl schon. Der Überlauf wird ja aber schon bei 2^31 beginnen, weil das mit 32 bit im Zweierkomplement auch nicht richtig darstellbar ist. Es wäre eine 1 mit 31 nullen, das letzte bit ist aber für’s Vorzeichen reserviert, somit ist die grösste Zahl die darstellbar ist 2^31-1 also 31 einsen, nicht? Ich erm, übernehme natürlich keine Garantie für Richtigkeit.

@Landei :
Wird wohl extra für Demonstrationszwecke so gemacht worden sein. Aber der code funktioniert wohl auch.