Rundungsfehler

Hey.

Ich helfe gerade einer Freundin bei einer Aufgabe: Eine Zahl auf bestimmte Stellen Runden. Und der Code ansich funktioniert jetz auch, nur das Problem ist das mir Java dauernd Rundungsfehler gibt…

        double gerundet;
        double hilfe = zahl;
        for (int i = 0; i <= stellen; i++) {
            hilfe *= 10.0;
        }
        gerundet = (int) hilfe;
        System.out.println("Hilfe: " + hilfe);
        while (hilfe > 10) {
            hilfe %= 10.0;
        }
        hilfe = (int) hilfe;
        gerundet /= 10;
        System.out.println(gerundet);
        gerundet = (int) gerundet;
        System.out.println(gerundet);
        if (hilfe >= 5) {
            gerundet++;
        }
        System.out.println(gerundet);
        System.out.println();
        for (int i = 0; i < stellen; i++) {
            gerundet /= 10.0;
            System.out.println(gerundet);
        }
        System.out.println();
        System.out.println(hilfe);
//        System.out.println(gerundet);

        return gerundet;
    }```
```run:
6
19

Hilfe: 1865664.3432999996
186566.4
186566.0
186566.0

18656.6
1865.6599999999999
186.56599999999997
18.656599999999997

4.0
18.656599999999997
BUILD SUCCESSFUL (total time: 0 seconds)```

Wie löse ich das?

Das Prblm. ist der Datentyp double. * 10, / 10, % 10 usw. mit ganzen Zahlen mag der gar nicht. Schreibe stattdessen:

        long l = 1;
        while (stellen-- > 0) {
            l *= 10;
        }
        return (double) (long) (Math.round(zahl * l)) / l;
    }```

DIe AUfgabe soll gelöst werden ohne dem benutzen der Math-Bibliothek, also im Prinzio durch eigenlösung- Daher habe ich Math-round auch nicht verwendet

Naja, dann nimm Math.round() einfach raus, alles >= xx.xx5 wird dann eben nicht aufgerundet. :slight_smile:

Nicht verzagen, CyborgBeta fragen. :slight_smile:

Und adventliche Grüße. :wink:

Edit: Ein Link: java - How to round without Math#round? - Stack Overflow

Ich hab hier eine mit 9 anstatt 7 Zeilen … Mit nachdenken kann man das selber.

( Math.round() wird vermieden, aber das ganze ist nicht mehr so „schön“ ).

Moin, kann mich nachher noch mal damit beschäftigen (oder jemand anderes macht des). Vermutlich liegt es an dem Modulo auf double.

Die Frage bei SO wurde downgevotet, weil man eben üblicherweise Math.round() dafür hernimmt, wenn nicht gar eine Formatierungsfunktion.

Also möglich ist das auf jeden, mit dem .5 Trick.

Nach der Aufgabenstellung könnte das Gewünschte so:

        System.out.println(rundenB(1234.56789, 0));
        System.out.println(rundenB(1234.56789, 1));
        System.out.println(rundenB(1234.56789, 2));
        System.out.println(rundenB(1234.56789, 3));
    }

    static double rundenB(double zahl, int stellen) {
        for (int i = 0; i <= stellen; i++) {
            zahl *= 10;
        }
        if ((long) zahl % 10 >= 5) {
            zahl /= 10;
            zahl++;
        } else {
            zahl /= 10;
        }
        zahl = (long) zahl;
        for (int i = 0; i < stellen; i++) {
            zahl /= 10;
        }
        return zahl;
    }```


1235.0
1234.6
1234.5700000000002
1234.568



FEHLER! Liegt vermutlich an ++.

Schreibe:

```        for (int i = 0; i <= stellen; i++) {
            zahl *= 10;
        }
        long l = (long) zahl;
        if (l % 10 >= 5) {
            l /= 10;
            l++;
        } else {
            l /= 10;
        }
        zahl = l;
        for (int i = 0; i < stellen; i++) {
            zahl /= 10;
        }
        return zahl;```

macht es eindeutig nicht besser, also:

```    static double rundenB(double zahl, int stellen) {
        long tmp = 1;
        for (int i = 0; i <= stellen; i++) {
            tmp *= 10;
        }
        long l = (long) (zahl * tmp);
        if (l % 10 >= 5) {
            l /= 10;
            l++;
        } else {
            l /= 10;
        }
        zahl = l * 10.0 / tmp;
        return zahl;
    }```

und schon ist deine Freundin befriedigt. :)

Wie wäre es mit :

	static double rundenB(double zahl, int stellen) {
		String fmt = "%."+stellen+"f";
		String sz = String.format(fmt, zahl).replace(",",".");
		return Double.parseDouble(sz);
	}

:slight_smile:

Also jedenfalls wird Math nicht direkt verwendet.

*** Edit ***

auch das sieht erst mal nicht so schlecht aus, denk ich:

	static double rundenB(double zahl, int stellen) {
        double s=10;
        for(int i=0;i<=stellen;i++){
        	s /= 10;
        }
        double nk = zahl % 1;
        double rnk;
        if (nk < 0) {       
          rnk = s * (long)(nk / s - 0.5);
        } else {
          rnk = s * (long)(nk / s + 0.5);        	
        }        	      
		return zahl - nk + rnk;
	}

@pappawinni : Etwas Ähnliches, formelhaftes/-lastiges hatte ich gestern spät auch! Ich kann im Moment nicht sagen, welches da besser geeignet wäre.

Allerdings ist mir aufgefallen, dass *= 10 (auf double angewandt) die problematische Operation darstellte.

Aber dieser Fehler ist auch schon länger bekannt.^^

[ot]
Ich finde, man so11te eine Variab1e nicht '1' nennen
[/ot]
Ansonsten wird das praktisch immer “irgendwann” an Grenzen stoßen, bzgl. der Rechengenauigkeit. Wurden weitere bzw. genauere Anforderungen in der Aufgabenstellung angegeben?

[QUOTE=Marco13][ot]
Ich finde, man so11te eine Variab1e nicht '1' nennen
[/ot][/QUOTE]

[OT]Da bin ich etwas eigen. Bei mir heißen die ersten long s l. :smiley: Ich hab in der Schule noch gelernt, dass man die 1 immer mit einem deutlich erkennbaren Aufstrich ("[3] Typografie: eine Linie in die obere Richtung beim Schreiben") schreibt, damit deutlich vom L/l unterschieden werden können. AAAABER: Damit könnte die 1 auch mit einem Und-Junktor (formale Logik / Liste mathematischer Symbole – Wikipedia ) verwechselt werden. Wie man es jetzt auch dreht, es könnte falsch sein. :([/OT]

Hättest du auch noch eine Variante mit Beibehaltung „größtmöglicher Genauigkeit“?

Bis dann (müde usw.)

Die Aufgabe war die hier:

Also quasi wirklich komplett selber machen. SInd ja schon paar Vorschläge hier, ich schau mir die mal an

wenn die Rundung auch in die Vorkommastellen gehen soll, also gewissermassen negativer Stellenanzahl,
könnte es vielleicht so gehen:

static double rundenB(double zahl, int stellen) {
		 double s = 10.0;
		 if (stellen < 0) {
			 for(int i=stellen+1;i<0;i++){
				  s *= 10.0;
			 }} else {
		     for(int i=0;i<=stellen;i++){
		          s /= 10.0;
		     }}
		 double nk = (zahl / s) % 1;
		 double result = s * (long)(zahl / s);
		 if (nk < 0){
		    result += s * (long)(nk - 0.5 );
		 } else {
			result += s * (long)(nk + 0.5 );
		 }
		 return result ;
	}
}

aber wozu das Rad neu erfinden…

[ot][quote=pappawinni]aber wozu das Rad neu erfinden…[/quote]

Da es zur Aufgabe gehört und es zu Übungszwecken, bzw. um die Sprache zu lernen, sinnvoll sein kann, bestimmte Methoden einmal selbst nachzuprogrammieren.[/ot]

Jetzt schon die 5000. Variante:

        long tmp = 10;
        for (int i = 0; i < stellen; i++) {
            tmp *= 10;
        }
        zahl *= tmp;
        if (zahl % 10 >= 5) {
            zahl /= 10;
            zahl++;
        } else {
            zahl /= 10;
        }
        zahl = (long) zahl * 10.0 / tmp;
        return zahl;
    }```

(Das lästige `l` nicht dabei.) Beruhen, wenn ich das richtig sehe, alle auf (long)-Cast.

Es gibt, mMn., 3 Qualitäts-Attribute:
- mögliche Größe double, für welche es genau ist (noch),
- Genauigkeit,
- und Korrektheit (schließt 1. und 2. ein).

Extrem große double wird man damit nicht runden können?

Zur Aufgabenstellung, jetzt wäre noch interessant, wo / in welch Rahmen diese Aufgabe gestellt wurde.

Ist schon ein bisschen computeralgebra- und zahlentheorie-angehaucht.
Demgegenüber aber (aber Spekulation, vielleicht Prüfungsfrage), 1 Punkt für die Aufgabe.
D. h., wahrscheinlich gewisse Toleranz gegeben.

(Berichtigt mich einfach, wenn ich falsch liege.)

[QUOTE=Darse]Die Aufgabe war die hier:

Also quasi wirklich komplett selber machen. SInd ja schon paar Vorschläge hier, ich schau mir die mal an[/QUOTE]

Ja, OK, da steht jetzt nichts spezifischeres dabei, davon kann man ausgehen, dass z.B. sowas wie aus pappawinnis erster Antwort gemeint ist (das zweite Codestück darin). Hab’s nicht getestet, aber vom Grundgedanken her: Den Faktor ausrechnen, mit dem man das Komma um die gewünschten Stellen (-1) verschieben kann, und den dann verwenden, um den Wert auszurechnen, der „normal“ gerundet werden muss - vielleicht gleich mit der Methode, die man für Aufgabenteil A geschrieben hat :wink:

Mein Test
[spoiler]

Wir erledigen ja normalerweise keine Hausaufgaben, aber … auch auf die Gefahr hin, dass das anmaßend oder arrogant klingt: Vielleicht zieht der eine oder andere ja eine Art „Inspiration“ aus den hier verwendeten Variablennamen…

package bytewelt;

import java.util.Locale;

public class Runden
{
    public static void main(String[] args)
    {
        testA(3.2);
        testA(3.5);
        testA(3.8);
        
        testA(-3.2);
        testA(-3.5);
        testA(-3.8);
        
        testB(1.234567, 1);
        testB(1.234567, 2);
        testB(1.234567, 3);
        testB(1.234567, 4);
        testB(1.234567, 5);

        testB(-1.234567, 1);
        testB(-1.234567, 2);
        testB(-1.234567, 3);
        testB(-1.234567, 4);
        testB(-1.234567, 5);
    }
    
    private static void testA(double value)
    {
        System.out.printf(Locale.ENGLISH, 
            "%16.8f becomes %16.8f
", value, roundA(value));
    }

    private static void testB(double value, int places)
    {
        System.out.printf(Locale.ENGLISH, 
            "%16.8f with %d places becomes %16.8f
", 
            value, places, roundB(value, places));
    }
    
    private static double roundA(double value)
    {
        long integralPart = (long)value;
        double fractionalPart = value - integralPart;
        if (fractionalPart >= 0.5)
        {
            return integralPart + 1;
        }
        if (fractionalPart <= -0.5)
        {
            return integralPart - 1;
        }
        return integralPart;
    }
    
    
    private static double roundB(double value, int places)
    {
        double factor = 1;
        for (int i=0; i<places; i++)
        {
            factor *= 10;
        }
        return roundA(value * factor) / factor;
    }
    
}

[/spoiler]

Trotzdem gibt es da natürlich auch offene Fragen und Details, und es wird an seine Grenzen stoßen, wenn man versucht, damit eine Zahl wie „1.23e100“ auf 99 Nachkommastellen zu runden oder so…