ohne - java zahlen runden




Wie man eine Zahl auf n Dezimalstellen in Java rundet (20)

@Milhous: Das Dezimalformat für das Runden ist ausgezeichnet:

Sie können auch die

DecimalFormat df = new DecimalFormat("#.00000");
df.format(0.912385);

um sicher zu stellen, dass Sie die abschließenden Nullen haben.

Ich würde hinzufügen, dass diese Methode sehr gut darin ist, einen tatsächlichen numerischen Rundungsmechanismus bereitzustellen - nicht nur visuell, sondern auch bei der Verarbeitung.

Hypothetisch: Sie müssen einen Rundungsmechanismus in ein GUI-Programm implementieren. Um die Genauigkeit / Genauigkeit einer Ergebnisausgabe zu ändern, ändern Sie einfach das Caret-Format (dh innerhalb der Klammern). Damit:

DecimalFormat df = new DecimalFormat("#0.######");
df.format(0.912385);

würde als Ausgabe zurückgeben: 0.912385

DecimalFormat df = new DecimalFormat("#0.#####");
df.format(0.912385);

würde als Ausgabe zurückgeben: 0.91239

DecimalFormat df = new DecimalFormat("#0.####");
df.format(0.912385);

würde als Ausgabe zurückgeben: 0.9124

[BEARBEITEN: Auch wenn das Caret-Format so ist ("# 0. ############") und Sie eine Dezimalzahl eingeben, zB 3.1415926, erzeugt DecimalFormat keinen Müll ( zB nachgestellte Nullen) und wird zurückgeben: 3.1415926 .. wenn du so geneigt bist. Zugegeben, es ist ein wenig wortreich für den Geschmack einiger Entwickler - aber hey, es hat einen geringen Speicherbedarf während der Verarbeitung und ist sehr einfach zu implementieren.]

Die Schönheit von DecimalFormat besteht also im Wesentlichen darin, dass es gleichzeitig das Aussehen der Saite und das Niveau der Rundungsgenauigkeit behandelt. Ergo: Sie erhalten zwei Vorteile für den Preis einer Code-Implementierung. ;)

Was ich möchte, ist eine Methode, um ein Double in einen String zu konvertieren, der mit der Half-Up-Methode gerundet wird - dh wenn die zu gerundete Dezimalzahl 5 ist, wird immer auf die vorherige Zahl aufgerundet. Dies ist die Standardmethode der Rundung, die die meisten Menschen in den meisten Situationen erwarten.

Ich möchte auch nur signifikante Ziffern anzeigen lassen - dh es sollte keine abschließenden Nullen geben.

Ich weiß, eine Methode, dies zu tun, ist die String.format Methode zu verwenden:

String.format("%.5g%n", 0.912385);

kehrt zurück:

0.91239

Das ist großartig, aber es zeigt immer Zahlen mit 5 Dezimalstellen an, auch wenn sie nicht signifikant sind:

String.format("%.5g%n", 0.912300);

kehrt zurück:

0.91230

Eine andere Methode ist die Verwendung von DecimalFormatter :

DecimalFormat df = new DecimalFormat("#.#####");
df.format(0.912385);

kehrt zurück:

0.91238

Wie Sie jedoch sehen können, wird hier eine halbrunde Rundung verwendet. Das heißt, es wird abgerundet, wenn die vorherige Ziffer gerade ist. Was ich möchte ist das:

0.912385 -> 0.91239
0.912300 -> 0.9123

Was ist der beste Weg, dies in Java zu erreichen?


Angenommen, du hast

double d = 9232.129394d;

Sie können BigDecimal

BigDecimal bd = new BigDecimal(d).setScale(2, RoundingMode.HALF_EVEN);
d = bd.doubleValue();

oder ohne BigDecimal

d = Math.round(d*100)/100.0d;

mit beiden Lösungen d == 9232.13


Da ich zu diesem Thema keine vollständige Antwort gefunden habe, habe ich eine Klasse zusammengestellt, die das richtig behandeln soll, mit Unterstützung für:

  • Formatierung : Einfaches Formatieren eines Double in String mit einer bestimmten Anzahl von Dezimalstellen
  • Parsing : Parsen Sie den formatierten Wert zurück zu double
  • Gebietsschema : Formatieren und analysieren Sie das Standardgebietsschema
  • Exponentielle Notation : Beginnen Sie mit der Exponentialnotation nach einer bestimmten Schwelle

Die Verwendung ist ziemlich einfach :

(Für dieses Beispiel verwende ich ein benutzerdefiniertes Gebietsschema)

public static final int DECIMAL_PLACES = 2;

NumberFormatter formatter = new NumberFormatter(DECIMAL_PLACES);

String value = formatter.format(9.319); // "9,32"
String value2 = formatter.format(0.0000005); // "5,00E-7"
String value3 = formatter.format(1324134123); // "1,32E9"

double parsedValue1 = formatter.parse("0,4E-2", 0); // 0.004
double parsedValue2 = formatter.parse("0,002", 0); // 0.002
double parsedValue3 = formatter.parse("3423,12345", 0); // 3423.12345

Hier ist die Klasse :

import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.ParseException;
import java.util.Locale;

public class NumberFormatter {

    private static final String SYMBOL_INFINITE           = "\u221e";
    private static final char   SYMBOL_MINUS              = '-';
    private static final char   SYMBOL_ZERO               = '0';
    private static final int    DECIMAL_LEADING_GROUPS    = 10;
    private static final int    EXPONENTIAL_INT_THRESHOLD = 1000000000; // After this value switch to exponential notation
    private static final double EXPONENTIAL_DEC_THRESHOLD = 0.0001; // Below this value switch to exponential notation

    private DecimalFormat decimalFormat;
    private DecimalFormat decimalFormatLong;
    private DecimalFormat exponentialFormat;

    private char groupSeparator;

    public NumberFormatter(int decimalPlaces) {
        configureDecimalPlaces(decimalPlaces);
    }

    public void configureDecimalPlaces(int decimalPlaces) {
        if (decimalPlaces <= 0) {
            throw new IllegalArgumentException("Invalid decimal places");
        }

        DecimalFormatSymbols separators = new DecimalFormatSymbols(Locale.getDefault());
        separators.setMinusSign(SYMBOL_MINUS);
        separators.setZeroDigit(SYMBOL_ZERO);

        groupSeparator = separators.getGroupingSeparator();

        StringBuilder decimal = new StringBuilder();
        StringBuilder exponential = new StringBuilder("0.");

        for (int i = 0; i < DECIMAL_LEADING_GROUPS; i++) {
            decimal.append("###").append(i == DECIMAL_LEADING_GROUPS - 1 ? "." : ",");
        }

        for (int i = 0; i < decimalPlaces; i++) {
            decimal.append("#");
            exponential.append("0");
        }

        exponential.append("E0");

        decimalFormat = new DecimalFormat(decimal.toString(), separators);
        decimalFormatLong = new DecimalFormat(decimal.append("####").toString(), separators);
        exponentialFormat = new DecimalFormat(exponential.toString(), separators);

        decimalFormat.setRoundingMode(RoundingMode.HALF_UP);
        decimalFormatLong.setRoundingMode(RoundingMode.HALF_UP);
        exponentialFormat.setRoundingMode(RoundingMode.HALF_UP);
    }

    public String format(double value) {
        String result;
        if (Double.isNaN(value)) {
            result = "";
        } else if (Double.isInfinite(value)) {
            result = String.valueOf(SYMBOL_INFINITE);
        } else {
            double absValue = Math.abs(value);
            if (absValue >= 1) {
                if (absValue >= EXPONENTIAL_INT_THRESHOLD) {
                    value = Math.floor(value);
                    result = exponentialFormat.format(value);
                } else {
                    result = decimalFormat.format(value);
                }
            } else if (absValue < 1 && absValue > 0) {
                if (absValue >= EXPONENTIAL_DEC_THRESHOLD) {
                    result = decimalFormat.format(value);
                    if (result.equalsIgnoreCase("0")) {
                        result = decimalFormatLong.format(value);
                    }
                } else {
                    result = exponentialFormat.format(value);
                }
            } else {
                result = "0";
            }
        }
        return result;
    }

    public String formatWithoutGroupSeparators(double value) {
        return removeGroupSeparators(format(value));
    }

    public double parse(String value, double defValue) {
        try {
            return decimalFormat.parse(value).doubleValue();
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return defValue;
    }

    private String removeGroupSeparators(String number) {
        return number.replace(String.valueOf(groupSeparator), "");
    }

}

Das Code-Snippet unten zeigt, wie n Ziffern angezeigt werden. Der Trick besteht darin, die Variable pp auf 1 zu setzen, gefolgt von n Nullen. Im Beispiel unten hat der Wert für variable pp 5 Nullen, daher werden 5 Ziffern angezeigt.

double pp = 10000;

double myVal = 22.268699999999967;
String needVal = "22.2687";

double i = (5.0/pp);

String format = "%10.4f";
String getVal = String.format(format,(Math.round((myVal +i)*pp)/pp)-i).trim();

Für den Fall, dass jemand noch Hilfe braucht. Diese Lösung funktioniert perfekt für mich.

private String withNoTrailingZeros(final double value, final int nrOfDecimals) {
return new BigDecimal(String.valueOf(value)).setScale(nrOfDecimals,  BigDecimal.ROUND_HALF_UP).stripTrailingZeros().toPlainString();

}

gibt einen String mit der gewünschten Ausgabe zurück.


Hier ist eine Grundlösung von jpdymond :

   public static double round(double value, int precision) {
      int scale = (int) Math.pow(10, precision);
      return (double) Math.round(value * scale) / scale;
  }

https://.com/a/22186845/212950


Ich kam hierher, wollte nur eine einfache Antwort darauf, wie man eine Nummer abrundet. Dies ist eine ergänzende Antwort darauf.

Wie man eine Nummer in Java rundet

Der häufigste Fall ist die Verwendung von Math.round() .

Math.round(3.7) // 4

Die Zahlen werden auf die nächste ganze Zahl gerundet. Ein Wert von .5 wird aufgerundet. Wenn Sie ein anderes Rundungsverhalten benötigen, können Sie eine der anderen Math Funktionen verwenden. Siehe den Vergleich unten.

round

Wie oben erwähnt, wird auf die nächste ganze Zahl gerundet. .5 Dezimalstellen runden auf. Diese Methode gibt ein int .

Math.round(3.0); // 3
Math.round(3.1); // 3
Math.round(3.5); // 4
Math.round(3.9); // 4

Math.round(-3.0); // -3
Math.round(-3.1); // -3
Math.round(-3.5); // -3 *** careful here ***
Math.round(-3.9); // -4

ceil

Jeder dezimale Wert wird auf die nächste ganze Zahl aufgerundet. Es geht bis zur Decke . Diese Methode gibt ein double .

Math.ceil(3.0); // 3.0
Math.ceil(3.1); // 4.0
Math.ceil(3.5); // 4.0
Math.ceil(3.9); // 4.0

Math.ceil(-3.0); // -3.0
Math.ceil(-3.1); // -3.0
Math.ceil(-3.5); // -3.0
Math.ceil(-3.9); // -3.0

floor

Jeder dezimale Wert wird auf die nächste ganze Zahl abgerundet. Diese Methode gibt ein double .

Math.floor(3.0); // 3.0
Math.floor(3.1); // 3.0
Math.floor(3.5); // 3.0
Math.floor(3.9); // 3.0

Math.floor(-3.0); // -3.0
Math.floor(-3.1); // -4.0
Math.floor(-3.5); // -4.0
Math.floor(-3.9); // -4.0

rint

Dies ist vergleichbar mit der Rundung der Dezimalwerte auf die nächste ganze Zahl. Im Gegensatz zu round , runden die Werte .5 auf die gerade ganze Zahl ab. Diese Methode gibt ein double .

Math.rint(3.0); // 3.0
Math.rint(3.1); // 3.0
Math.rint(3.5); // 4.0 ***
Math.rint(3.9); // 4.0
Math.rint(4.5); // 4.0 ***
Math.rint(5.5); // 6.0 ***

Math.rint(-3.0); // -3.0
Math.rint(-3.1); // -3.0
Math.rint(-3.5); // -4.0 ***
Math.rint(-3.9); // -4.0
Math.rint(-4.5); // -4.0 ***
Math.rint(-5.5); // -6.0 ***

Ich stimme der gewählten Antwort zu, um DecimalFormat --- oder BigDecimal .

Bitte lesen Sie zuerst das Update unten!

Wenn Sie jedoch den doppelten Wert runden möchten und ein double Ergebnis erhalten org.apache.commons.math3.util.Precision.round(..) , können Sie org.apache.commons.math3.util.Precision.round(..) wie oben erwähnt verwenden. Die Implementierung verwendet BigDecimal , ist langsam und erstellt Müll.

Eine ähnliche, aber schnelle und DoubleRounder Methode bietet das Dienstprogramm DoubleRounder in der Bibliothek dezimal4j:

 double a = DoubleRounder.round(2.0/3.0, 3);
 double b = DoubleRounder.round(2.0/3.0, 3, RoundingMode.DOWN);
 double c = DoubleRounder.round(1000.0d, 17);
 double d = DoubleRounder.round(90080070060.1d, 9);
 System.out.println(a);
 System.out.println(b);
 System.out.println(c);
 System.out.println(d);

Wird ausgegeben

 0.667
 0.666
 1000.0
 9.00800700601E10

Siehe https://github.com/tools4j/decimal4j/wiki/DoubleRounder-Utility

Haftungsausschluss: Ich bin am decimal4j-Projekt beteiligt.

Update: Wie @iaforek darauf hingewiesen hat, gibt DoubleRounder manchmal nicht eingängige Ergebnisse zurück. Der Grund ist, dass es mathematisch korrekte Rundungen durchführt. Zum Beispiel wird DoubleRounder.round(256.025d, 2) auf 256.02 abgerundet, weil der doppelte Wert, dargestellt als 256.025d, etwas kleiner ist als der rationale Wert 256.025 und daher abgerundet wird.

Anmerkungen:

  • Dieses Verhalten ist dem des BigDecimal(double) valueOf(double) sehr ähnlich (aber nicht zu valueOf(double) , das den Zeichenfolgenkonstruktor verwendet).
  • Das Problem kann mit einem doppelten Rundungsschritt zunächst auf eine höhere Präzision umgangen werden, ist aber kompliziert und ich gehe hier nicht auf die Details ein

Aus diesen Gründen und allem, was oben in diesem Beitrag erwähnt wurde, kann ich DoubleRounder nicht empfehlen .



Sie können auch die

DecimalFormat df = new DecimalFormat("#.00000");
df.format(0.912385);

um sicher zu stellen, dass Sie die abschließenden Nullen haben.


Sie könnten die folgende Hilfsmethode verwenden:

public static double round(double valueToRound, int numberOfDecimalPlaces)
{
    double multipicationFactor = Math.pow(10, numberOfDecimalPlaces);
    double interestedInZeroDPs = valueToRound * multipicationFactor;
    return Math.round(interestedInZeroDPs) / multipicationFactor;
}

Unter der Annahme, dass der value ein double , können Sie Folgendes tun:

(double)Math.round(value * 100000d) / 100000d

Das ist für 5-stellige Genauigkeit. Die Anzahl der Nullen gibt die Anzahl der Dezimalstellen an.


Verwenden Sie setRoundingMode , legen Sie den RoundingMode explizit fest, um das Problem mit der setRoundingMode Runde zu behandeln, und verwenden Sie dann das Formatmuster für die erforderliche Ausgabe.

Beispiel:

DecimalFormat df = new DecimalFormat("#.####");
df.setRoundingMode(RoundingMode.CEILING);
for (Number n : Arrays.asList(12, 123.12345, 0.23, 0.1, 2341234.212431324)) {
    Double d = n.doubleValue();
    System.out.println(df.format(d));
}

gibt die Ausgabe:

12
123.1235
0.23
0.1
2341234.2125

Wenn Sie DecimalFormat , um double in String zu konvertieren, ist das sehr einfach:

DecimalFormat formatter = new DecimalFormat("0.0##");
formatter.setRoundingMode(RoundingMode.HALF_UP);

double num = 1.234567;
return formatter.format(num);

Abhängig von dem von Ihnen benötigten Verhalten stehen mehrere Werte für den RoundingMode zur Verfügung.


Wie einige andere bemerkt haben, ist die richtige Antwort entweder DecimalFormat oder BigDecimal . Der Fließkommawert hat keine Nachkommastellen, daher können Sie nicht auf eine bestimmte Anzahl von ihnen abrunden / abschneiden. Sie müssen in einer Dezimalzahl arbeiten, und genau das tun diese beiden Klassen.

Ich poste den folgenden Code als ein Gegenbeispiel zu allen Antworten in diesem Thread und tatsächlich überall in (und anderswo), die Multiplikation gefolgt von Abschneiden gefolgt von Division empfehlen. Den Verfechtern dieser Technik obliegt es zu erklären, warum der folgende Code in über 92% der Fälle die falsche Ausgabe liefert.

public class RoundingCounterExample
{

    static float roundOff(float x, int position)
    {
        float a = x;
        double temp = Math.pow(10.0, position);
        a *= temp;
        a = Math.round(a);
        return (a / (float)temp);
    }

    public static void main(String[] args)
    {
        float a = roundOff(0.0009434f,3);
        System.out.println("a="+a+" (a % .001)="+(a % 0.001));
        int count = 0, errors = 0;
        for (double x = 0.0; x < 1; x += 0.0001)
        {
            count++;
            double d = x;
            int scale = 2;
            double factor = Math.pow(10, scale);
            d = Math.round(d * factor) / factor;
            if ((d % 0.01) != 0.0)
            {
                System.out.println(d + " " + (d % 0.01));
                errors++;
            }
        }
        System.out.println(count + " trials " + errors + " errors");
    }
}

Ausgabe dieses Programms:

10001 trials 9251 errors

EDIT: Um einige Kommentare unten zu adressieren, habe ich den Modulo-Teil der Testschleife mit BigDecimal und dem new MathContext(16) für die Modulo-Operation wie folgt new MathContext(16) :

public static void main(String[] args)
{
    int count = 0, errors = 0;
    int scale = 2;
    double factor = Math.pow(10, scale);
    MathContext mc = new MathContext(16, RoundingMode.DOWN);
    for (double x = 0.0; x < 1; x += 0.0001)
    {
        count++;
        double d = x;
        d = Math.round(d * factor) / factor;
        BigDecimal bd = new BigDecimal(d, mc);
        bd = bd.remainder(new BigDecimal("0.01"), mc);
        if (bd.multiply(BigDecimal.valueOf(100)).remainder(BigDecimal.ONE, mc).compareTo(BigDecimal.ZERO) != 0)
        {
            System.out.println(d + " " + bd);
            errors++;
        }
    }
    System.out.println(count + " trials " + errors + " errors");
}

Ergebnis:

10001 trials 4401 errors

Wo dp = Dezimalstelle Sie wollen, und Wert ist ein Doppel.

    double p = Math.pow(10d, dp);

    double result = Math.round(value * p)/p;

If you're using a technology that has a minimal JDK. Here's a way without any Java libs:

double scale = 100000;    
double myVal = 0.912385;
double rounded = (int)((myVal * scale) + 0.5d) / scale;

In general, rounding is done by scaling: round(num / p) * p

/**
 * MidpointRounding away from zero ('arithmetic' rounding)
 * Uses a half-epsilon for correction. (This offsets IEEE-754
 * half-to-even rounding that was applied at the edge cases).
 */
double RoundCorrect(double num, int precision) {
	double c = 0.5 * EPSILON * num;
//	double p = Math.pow(10, precision); //slow
	double p = 1; while (precision--> 0) p *= 10;
	if (num < 0)
		p *= -1;
	return Math.round((num + c) * p) / p;
}

// testing edge cases
RoundCorrect(1.005, 2);   // 1.01 correct
RoundCorrect(2.175, 2);   // 2.18 correct
RoundCorrect(5.015, 2);   // 5.02 correct

RoundCorrect(-1.005, 2);  // -1.01 correct
RoundCorrect(-2.175, 2);  // -2.18 correct
RoundCorrect(-5.015, 2);  // -5.02 correct


DecimalFormat myFormatter = new DecimalFormat("0.000");
String output = myFormatter.format(2.34d);

double myNum = .912385;
int precision = 10000; //keep 4 digits
myNum= Math.floor(myNum * precision +.5)/precision;






digits