Javaで数値を小数点以下n桁に丸める方法


1259

私が望むのは、doubleを、ハーフアップメソッドを使用して丸める文字列に変換するメソッドです。つまり、丸められる小数が5の場合、常に次の数値に切り上げられます。これは、ほとんどの人がほとんどの状況で期待する丸めの標準的な方法です。

また、有効数字のみが表示されるようにしたいと考えています。つまり、後続のゼロがあってはなりません。

これを行う1つの方法は、そのString.format方法を使用することです。

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

戻り値:

0.91239

これはすばらしいことですが、たとえ重要でなくても、常に小数点以下5桁の数値が表示されます。

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

戻り値:

0.91230

もう一つの方法は、使用することですDecimalFormatter

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

戻り値:

0.91238

ただし、ご覧のとおり、これは半偶数丸めを使用しています。つまり、前の桁が偶数の場合は切り捨てられます。私が欲しいのはこれです:

0.912385 -> 0.91239
0.912300 -> 0.9123

これをJavaで実現する最良の方法は何ですか?

回答:


751

を使用してsetRoundingModeRoundingModeし、半偶数ラウンドで問題を処理するように明示的にから、必要な出力にフォーマットパターンを使用します。

例:

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));
}

出力を与えます:

12
123.1235
0.23
0.1
2341234.2125

編集:元の答えは、double値の正確性に対処していません。切り上げても切り下げてもあまり気にしないのであれば問題ありません。ただし、正確な丸めが必要な場合は、予想される値の精度を考慮する必要があります。浮動小数点値は内部的にバイナリ表現を持っています。つまり、2.7735のような値は、実際にはその正確な値を内部的に持っていません。少し大きくすることも、少し小さくすることもできます。内部値がわずかに小さい場合、2.7740に切り上げられません。この状況を改善するには、使用している値の正確性に注意し、丸める前にその値を加算または減算する必要があります。たとえば、値が6桁まで正確であることがわかっている場合、中間値を切り上げるには、その精度を値に追加します。

Double d = n.doubleValue() + 1e-6;

端数を切り捨てるには、精度を引きます。


9
これはおそらくこれまでに提示された最良のソリューションです。DecimalFormatクラスを最初に見たときにこの機能を見つけなかったのは、この機能がJava 1.6でのみ導入されたためです。残念ながら、私は1.5の使用に制限されていますが、将来について知っておくと役に立ちます。
Alex Spurling

1
私はこれを試してみました:"#.##"、丸めHALF_UP256.335f-> "256.33"...(例は、コメントから@asteriteの答えまで)。
bigstones 2015

6
DecimalFormatは現在のローカル構成に依存するため、注意してください。区切り文字としてドットを取得できない場合があります。私は個人的に以下のアステライトの答えを好む
Gomino

1
また、DecimalFormatがスレッドセーフであると期待してはいけないことにも注意してください。あたりとしてのJavaドキュメント小数点形式は、一般的に同期化されません。スレッドごとに個別のフォーマットインスタンスを作成することをお勧めします。複数のスレッドが同時にフォーマットにアクセスする場合は、外部で同期する必要があります。
CGK 2016

1
適切な丸めを行って0.0004から0.001に丸めないようにするにはどうすればよいですか

471

をとするvaluedouble、次のことができます。

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

これは5桁の精度です。ゼロの数は小数点以下の桁数を示します。


71
更新:これを行うと、DecimalFormatを使用するよりも高速であることを確認しました。DecimalFormatを使用して200回ループし、このメソッドを実行しました。DecimalFormatは200ループを完了するのに14ミリ秒かかりました。このメソッドは1ミリ秒未満かかりました。私が疑ったように、これはより速いです。クロックサイクルで支払いを受ける場合は、これを実行する必要があります。クリス・カドモアが彼が正直だと言ったことを言うことにも驚いています。オブジェクトの割り当ては、プリミティブをキャストして静的メソッドを使用するよりも常にコストがかかります(decimalFormat.format()ではなく、Math.round())。
アンディジェイ

99
この手法は、90%以上のケースで失敗します。-1。
ローンの侯爵2012年

25
実際、これは失敗しますMath.round(0.1 * Math.pow(10,20))/Math.pow(10,20) == 0.09223372036854775
Robert Tupelo-Schneck

54
この方法(または浮動小数点の丸め)を使用する場合は、十分に注意してください。265.335のような単純なものでは失敗します。265.335 * 100(2桁の精度)の中間結果は26533.499999999996です。つまり、265.33に切り捨てられます。浮動小数点数から実際の10進数に変換する場合、単に固有の問題があります。EJPの回答はこちらのstackoverflow.com/a/12684082/144578
Sebastiaan van den Broek

6
@SebastiaanvandenBroek:うわー、間違った答えが簡単に得られるなんて知らなかったわ。ただし、正確でない数値を扱う場合は、値が正確ではないことを認識する必要があります。265.335実際にはは265.335 += tolerance、許容値が以前の操作と入力値の範囲に依存することを意味します。真の正確な値はわかりません。エッジ値では、どちらの答えも間違いなく正しいです。正確にする必要がある場合は、二重に作業することはできません。failここでは、二重に戻すの変換ではありません。そのOPで彼は265.335まさにそれがそうであるように彼が入ってくることに頼ることができると考えています。
ToolmakerSteve 2015

191
new BigDecimal(String.valueOf(double)).setScale(yourScale, BigDecimal.ROUND_HALF_UP);

あなたを取得しますBigDecimal。それから文字列を取得するには、ちょうどそれを呼び出すBigDecimaltoString方法、またはtoPlainStringプレーンなフォーマット文字列のJavaのための方法を5+。

サンプルプログラム:

package trials;
import java.math.BigDecimal;

public class Trials {

    public static void main(String[] args) {
        int yourScale = 10;
        System.out.println(BigDecimal.valueOf(0.42344534534553453453-0.42324534524553453453).setScale(yourScale, BigDecimal.ROUND_HALF_UP));
    }

38
それが私の推奨する解決策です。さらに短い:BigDecimal.valueOf(doubleVar).setScale(yourScaleHere、BigDecimal.ROUND_HALF_UP); BigDecimal.valueOf(double val)は実際には内部でDouble.toString()を呼び出します;)
Etienne Neveu

4
いいね。new BigDecimal(doubleVar)浮動小数点の丸めに関する問題が発生する可能性があるので、角を切り取って使用しないでください
Edd

8
@エッド、興味深いことに、丸めの問題は、SebastiaanvandenBroekがコメントでasteriteの回答に言及している場合に発生します。double val = 265.335;BigDecimal.valueOf(val).setScale(decimals, BigDecimal.ROUND_HALF_UP).toPlainString();=> 265.34、ただし(new BigDecimal(val)).setScale(decimals, BigDecimal.ROUND_HALF_UP).toPlainString();=> 265.33
ToolmakerSteve 2015

7
@ToolmakerSteveこれは、doubleで新しいBigDecimalを使用すると、double値を直接受け取り、それを使用してBigDecimalを作成しようとするためです。 。
MetroidFan2002 2015

116

あなたも使うことができます

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

末尾に0があることを確認します。


25
この質問の目標の1つは、「後続のゼロはあり得ない」ということでした
ランチボックス

7
この質問では、オペレーションはゼロを望んでいませんでしたが、これはまさに私が欲しかったものです。あなたは小数部3桁の番号のリストを持っている場合は、それらはすべて、それが0だ場合でも、同じ数字を持つようにしたい
トム・キンケード

指定するのを忘れたRoundingMode.
IgorGanapolsky 2015

1
@IgorGanapolskyのデフォルトのDecimal mode使用RoundingMode.HALF_EVEN.
EndermanAPM

87

他の人が指摘したように、正しい答えはまたはのどちらDecimalFormatかを使用することBigDecimalです。浮動小数点に小数点以下の桁数がないため、最初にそれらの特定の数に丸める/切り捨てることはできません。あなたは10進数で作業しなければなりません、そしてそれはそれらの2つのクラスがすることです。

私はこのスレッドのすべての回答、そして実際にはStackOverflow全体(および他の場所)に対する反例として次のコードを投稿しています。次のコードが92%以上のケースで誤った出力を生成する理由を説明するのは、この手法の擁護者の責任です。

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");
    }
}

このプログラムの出力:

10001 trials 9251 errors

編集:私は使用してテストループの弾性部分をやり直し下にいくつかのコメントに対処するために、BigDecimalそして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");
}

結果:

10001 trials 4401 errors

8
トリックは、すべての9251エラーで、印刷結果がまだ正しいということです。
Didier L

7
@DidierLそれは私を驚かせません。最初のコンピューティングコースとして「数値解法」を実行し、最初から浮動小数点で実行できることと実行できないことを紹介したのはとても幸運でした。ほとんどのプログラマーはそれについてかなり曖昧です。
ローン侯爵2013

15
あなたがしているのは、浮動小数点が多くの10進数値を正確に表していないという反論です。その丸めは問題を引き起こしません。認めるように、数値は期待どおりに印刷されます。
Peter Lawrey、2014年

8
テストが失敗し、round()を取り除き、テストが94%失敗します。ideone.com/1y62CYは印刷さ100 trials 94 errorsあなたが合格テストを開始し、導入テスト休憩を丸めることを示す必要があります。
Peter Lawrey

6
反駁、ここで反駁。doubleエラーなし
Peter Lawrey

83

あなたが持っていると仮定します

double d = 9232.129394d;

あなたは使うことができます BigDecimal

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

またはBigDecimalなし

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

両方のソリューションで d == 9232.13


2
これはJava 1.5ユーザー(およびそれ以下)に最適なソリューションだと思います。コメントが1つあります。HALF_EVEN丸めモードは、奇数と偶数の動作が異なるため(たとえば、2.5は2に丸められ、5.5は6に丸められるなど)、これが必要な場合を除き、使用しないでください。
IcedD​​ante 2012

4
最初の解決策は正しいです。2番目の解決策は機能しません。証明はこちらをご覧ください。
ローンの侯爵

1
@EJP:最初のソリューションでさえRoundingMode.HALF_UP間違っています。でお試しください1.505。正しい方法はを使用することBigDecimal.valueOf(d)です。
Matthias Braun 14年

Matthias Braun、解は細かいので31 ups .. 1.505を浮動小数点のdoubleに1.50499998として格納します。1.505を取得してdoubleからdecimalに変換する場合は、最初にDouble.toString(x)に変換する必要があります。次にそれをBigDecimal()に入れますが、これは非常に低速であり、最初に速度にdoubleを使用する目的に反しています。
2014

1
BigDecimal(225ミリ秒かかった)とMath.round(2ミリ秒)の方法で100kのループを実行し、ここにタイミングがあります...所要時間:225ミリ秒を使用して変換する:9232.13所要時間:2ミリ秒に変換する:9232.13 techiesinfo.com
07/31

59

DecimalFormatクラスを使用できます。

double d = 3.76628729;

DecimalFormat newFormat = new DecimalFormat("#.##");
double twoDecimal =  Double.valueOf(newFormat.format(d));

Double.valueOf()選ばれた理由は何Double.parseDouble()ですか?このvalueOf()メソッドはDoubleオブジェクトを返しますparseDouble()が、doubleプリミティブを返します。現在のコードの記述方法では、戻り値に自動ボックス化解除を適用して、twoDouble変数が期待するプリミティブに追加のバイトコード操作をキャストします。parseDouble()代わりに使用するように答えを変更します。
ecbrodie、2015

Double.parseDouble()String入力が必要です。
ライド

38

RealのJavaハウツーはこのソリューションを投稿します。これはJava 1.6より前のバージョンにも互換性があります。

BigDecimal bd = new BigDecimal(Double.toString(d));
bd = bd.setScale(decimalPlace, BigDecimal.ROUND_HALF_UP);
return bd.doubleValue();

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

2
はい、これは正の数に対してmath.roundが正確に行うことですが、負の数でこれを試しましたか?人々は他のソリューションでmath.roundを使用して、負の数のケースもカバーしています。
2014

注:Math.floor(x + 0.5)およびMath.round(x)
ピーターLawrey

30

@Milhous:丸めの10進形式は優れています。

あなたも使うことができます

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

末尾に0があることを確認します。

この方法は実際の数値の丸めメカニズムを提供するのに非常に優れていると私は付け加えます-視覚的にだけでなく、処理するときにも。

仮説:丸めメカニズムをGUIプログラムに実装する必要があります。結果出力の精度/精度を変更するには、キャレット形式を変更します(つまり、括弧内)。そのため:

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

出力として返されます: 0.912385

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

出力として返されます: 0.91239

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

出力として返されます: 0.9124

[編集:キャレット形式がそうである場合も( "#0。############")、引数として3.1415926などの小数を入力すると、DecimalFormatはガベージを生成しません(例:末尾のゼロ)が返されます: 3.1415926..傾斜している場合。確かに、一部の開発者の好みには少し冗長ですが、処理中のメモリフットプリントが低く、実装が非常に簡単です。]

したがって、基本的に、DecimalFormatの優れた点は、文字列の外観と、丸め精度セットのレベルを同時に処理できることです。Ergo:1つのコード実装の価格に対して2つの利点があります。;)


2
あなたは本当に(とだけでなく、出力用)は、計算のために小数たい場合は、バイナリベースの浮動小数点形式を使用しないようにdouble。BigDecimalまたはその他の10進数ベースの形式を使用します。
パウロEbermann

20

文字列として結果が必要な場合に使用できるものの概要を次に示します。

  1. DecimalFormat#setRoundingMode()

    DecimalFormat df = new DecimalFormat("#.#####");
    df.setRoundingMode(RoundingMode.HALF_UP);
    String str1 = df.format(0.912385)); // 0.91239
  2. BigDecimal#setScale()

    String str2 = new BigDecimal(0.912385)
        .setScale(5, BigDecimal.ROUND_HALF_UP)
        .toString();

double結果として必要な場合に使用できるライブラリの提案を以下に示します。ただし、doubleは必要なものを正確に表すことができない場合があるため、文字列変換にはお勧めしません(たとえば、こちらを参照)。

  1. Apache Commons Mathの精度

    double rounded = Precision.round(0.912385, 5, BigDecimal.ROUND_HALF_UP);
  2. コルトの機能

    double rounded = Functions.round(0.00001).apply(0.912385)
  3. Utilsのウェカから

    double rounded = Utils.roundDouble(0.912385, 5)

18

次のユーティリティメソッドを使用できます。

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

@mariolpantunes:失敗します。これを試してください:round(1.005,2);またはround(0.50594724957626620092, 20);
Matthias Braun

できます。しかし、情報量の少ない浮動小数点数と倍精度浮動小数点数は概算です。最初の例を考えてみましょう。Math.roundの前にInterestInZeroDPsの出力を印刷すると、100.49999999999999が印刷されます。あなたは、自然や山車に100原因としてラウンドな恐らくMath.roundとしての精度を失い、それは(ここではより多くの情報を適切に動作しない境界線の例がある倍増en.wikipedia.org/wiki/Floating_point#Accuracy_problemsは
mariolpantunes

ダブルは速いです!小数は遅いです。コンピュータは10進数表記で考えを処理する必要はありません。浮動小数点を2倍に高速に保つには、小数精度をあきらめる必要があります。
2014

@hamish問題は速度ではなく、精度についてです。
ローンの侯爵



7

これを試してください:org.apache.commons.math3.util.Precision.round(double x、int scale)

参照:http : //commons.apache.org/proper/commons-math/apidocs/org/apache/commons/math3/util/Precision.html

Apache Commons Mathematics Libraryホームページは次のとおりです。http//commons.apache.org/proper/commons-math/index.html

このメソッドの内部実装は次のとおりです。

public static double round(double x, int scale) {
    return round(x, scale, BigDecimal.ROUND_HALF_UP);
}

public static double round(double x, int scale, int roundingMethod) {
    try {
        return (new BigDecimal
               (Double.toString(x))
               .setScale(scale, roundingMethod))
               .doubleValue();
    } catch (NumberFormatException ex) {
        if (Double.isInfinite(x)) {
            return x;
        } else {
            return Double.NaN;
        }
    }
}

7

私はこのテーマについて完全な答えを見つけられなかったので、これを適切に処理する必要があるクラスをまとめてサポートしました:

  • 書式設定:doubleを特定の小数点以下の桁数の文字列に簡単に書式設定します
  • 解析:フォーマットされた値を解析して倍精度に戻します
  • ロケール:デフォルトのロケールを使用してフォーマットおよび解析します
  • 指数表記:特定のしきい値の後で指数表記の使用を開始します

使い方はかなり簡単です:

(この例のために、私はカスタムロケールを使用しています)

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

ここにクラスがあります:

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), "");
    }

}

7

(出力だけでなく)計算に10進数が本当に必要な場合は、doubleなどのバイナリベースの浮動小数点形式を使用しないでください。

Use BigDecimal or any other decimal-based format.

計算にはBigDecimalを使用しますが、処理する数値のサイズに依存することを覚えておいてください。ほとんどの実装では、doubleまたはintegerからLongへの解析は、非常に大きな数の計算には十分であることがわかりました。

実際、最近、parsed-to-Longを使用して、##################と同じ大きさの数値のGUIで(16進の結果ではなく)正確な表現を取得しました###############文字(例)。


6

これを実現するために、このフォーマッターを使用できます。

 DecimalFormat df = new DecimalFormat("#.00");
 String resultado = df.format(valor)

または:

DecimalFormat df = new DecimalFormat("0.00"); :

このメソッドを使用して、常に小数点以下2桁を取得します。

   private static String getTwoDecimals(double value){
      DecimalFormat df = new DecimalFormat("0.00"); 
      return df.format(value);
    }

この値の定義:

91.32
5.22
11.5
1.2
2.6

メソッドを使用して、次の結果を得ることができます。

91.32
5.22
11.50
1.20
2.60

オンラインデモ。


5

誰かがまだこれで助けが必要な場合に備えて。この解決策は私にとって完璧に機能します。

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

}

String 目的の出力でa を返します。


コメントに反対投票の理由を含めてください。そうでない場合は、脅迫と呼ばれます。
アッシャー。M. O

4

私は使用するために選択した答えに同意しますDecimalFormat---または代わりにBigDecimal

まず下の更新を読んでください!

あなたはしかし double値を四捨五入して取得したいdouble値の結果を、あなたは使用することができorg.apache.commons.math3.util.Precision.round(..)、上述したよう。実装はを使用しBigDecimal、時間がかかり、ガベージを作成します。

同様であるが高速でガベージフリーのメソッドはDoubleRounder、decimal4jライブラリのユーティリティによって提供されます。

 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);

出力されます

 0.667
 0.666
 1000.0
 9.00800700601E10

https://github.com/tools4j/decimal4j/wiki/DoubleRounder-Utilityを参照して ください

免責事項:私はdecimal4jプロジェクトに関与しています。

更新: @iaforekが指摘したように、DoubleRounderは時として直観に反する結果を返します。これは、数学的に正しい丸めを実行するためです。たとえばDoubleRounder.round(256.025d, 2)、256.025dとして表されるdouble値は、有理値256.025よりもいくらか小さいため、256.02に切り捨てられ、切り捨てられます。

ノート:

  • この動作は、BigDecimal(double)コンストラクターの動作と非常に似ています(ただしvalueOf(double)、文字列コンストラクターを使用する動作とは異なります)。
  • この問題は、最初に二重丸めのステップで精度を上げることで回避できますが、複雑なのでここでは詳しく説明しません。

これらの理由と、この投稿で前述したすべての理由から、私はDoubleRounderの使用を推奨できません


他のソリューションと比較してソリューションの効率を示す指標はありますか?
iaforek

他のソリューションと比較していませんが、ソースコードで利用可能なjmhベンチマークがあります:github.com/tools4j/decimal4j/blob/master/src/jmh/java/org/…VMで ベンチマークを実行しました、結果はcsvファイルとしてこちらから入手できます:github.com/tools4j/decimal4j/wiki/Performance
marco

1
DoubleRounderは次の場合に失敗します:DoubleRounder.round(256.025d、2)-予想:256.03、実際:256.02またはDoubleRounder.round(260.775d、2)の場合:予想:260.78、実際:260.77。
iaforek 2017年

@iaforek:DoubleRounderは数学的に正しい丸めを実行するため、これは正しいです。しかし、これはやや直観に反するものであり、それに応じて私の答えを更新することを認めます。
marco

3

以下のコードスニペットは、n桁を表示する方法を示しています。トリックは、変数ppを1に設定し、その後にゼロをn個続けることです。次の例では、変数pp値に5つのゼロがあるため、5桁が表示されます。

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();

3

DecimalFormatへの変換doubleに使用している場合はString、非常に簡単です。

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

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

RoundingMode必要な動作に応じて、いくつかの列挙値から選択できます。


3

数値を丸める方法について簡単な答えが欲しいだけでここに来ました。これはそれを提供するための補足的な回答です。

Javaで数値を丸める方法

最も一般的なケースはを使用することMath.round()です。

Math.round(3.7) // 4

数値は最も近い整数に丸められます。.5値が切り上げられます。それとは異なる丸め動作が必要な場合は、他の数学関数のいずれかを使用できます。以下の比較をご覧ください。

円形

上記のように、これは最も近い整数に丸めます。.5小数点以下は切り上げます。このメソッドはを返します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

天井

10進数の値は、次の整数に切り上げられます。それは天井に行きます。このメソッドはを返します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

10進数の値は、次の整数に切り捨てられます。このメソッドはを返します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

これは、10進値が最も近い整数に丸められるという点で、丸めに似ています。ただし、とは異なりround.5値は偶数の整数に丸められます。このメソッドはを返します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 ***

1
小数0に丸める特定のケースのみを解決します。元の質問はより一般的です。
lukas84

3

最小限のJDKを持つテクノロジーを使用している場合。Javaライブラリを使わない方法は次のとおりです。

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

これは、myValが1未満ではなく、小数点以下の桁数がゼロを超える場合に失敗します。myVal = 9.00000000912385であるとします。上記は9.0を返します。myValのすべてのケースで機能するソリューションを提供する必要があると思います。特にあなたが述べた価値のためではありません。
tavalendo

@ user102859この例では、9.0が正しい結果です。これがどのように失敗するかわかりません。
クレイゴ

2

DecimalFormatが出力の最良の方法ですが、私はそれを好みません。double値を返すので、私は常にこれを常に行います。したがって、出力だけでなくそれを使用することもできます。

Math.round(selfEvaluate*100000d.0)/100000d.0;

または

Math.round(selfEvaluate*100000d.0)*0.00000d1;

小数点以下の大きな値が必要な場合は、代わりにBigDecimalを使用できます。とにかく.0重要です。それなしでは、0.33333d5の丸めは0.33333を返し、9桁しか許可されません。なしの2番目の関数に.0は、0.30000が0.30000000000000004を返すという問題があります。


2

これが私の答えです:

double num = 4.898979485566356;
DecimalFormat df = new DecimalFormat("#.##");      
time = Double.valueOf(df.format(num));

System.out.println(num); // 4.89

1

ですから、ほとんどの回答を読んだ後、それらのほとんどが正確ではないことに気付きました。実際、使用BigDecimalするのが最善の選択のようですが、RoundingMode動作が理解できないと、必然的に精度が失われます。私はプロジェクトで大きな数を扱うときにこれを理解し、それが他の人が数を丸める際の問題を解決するのに役立つと考えました。例えば。

BigDecimal bd = new BigDecimal("1363.2749");
bd = bd.setScale(2, RoundingMode.HALF_UP);
System.out.println(bd.doubleValue());

1363.28出力として取得することを期待しますが、が1363.27何をしているのかがわからない場合は、予期しないになってしまいRoundingModeます。したがって、Oracle Docsを見ると、の次の説明が見つかりますRoundingMode.HALF_UP

両方の隣人が等距離でない限り、「最も近い隣人」に向かって丸める丸めモード。

これを知って、最も近い隣に向かって丸めたいのでない限り、正確な丸めができないことに気づきました。したがって、適切な丸めを達成するには、n-1小数から目的の小数の桁にループする必要があります。例えば。

private double round(double value, int places) throws IllegalArgumentException {

    if (places < 0) throw new IllegalArgumentException();

    // Cast the number to a String and then separate the decimals.
    String stringValue = Double.toString(value);
    String decimals = stringValue.split("\\.")[1];

    // Round all the way to the desired number.
    BigDecimal bd = new BigDecimal(stringValue);
    for (int i = decimals.length()-1; i >= places; i--) {
        bd = bd.setScale(i, RoundingMode.HALF_UP);
    }

    return bd.doubleValue();
}

これにより、予想される出力が得られます1363.28


0

ここで、dp =希望する小数点以下の桁数で、はdoubleです。

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

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

1
生産1.0のためにvalue = 1.005dp = 2。代わりにこれを使用してください。
Matthias Braun

それは大丈夫ですマット、あなたの例は無効です。とにかく、1.005は浮動小数点doubleで表すことができないためです。1.005より上または下に保存する必要があります。つまり、コンパイル時にdoubleとして保存されます。 double、あなたのようなフリンジケースはとにかく取るに足らないものです。もしそうなら、あなたは3dpを使用し、それを10進に変換し、あなたが投稿したリンクと同じように、10進の丸め関数を実行します。
2014

1
@hamishどこにMatthiasが「読者に信じさせる」か、値が10進数としてコンパイルされるようなものはわかりません。他人の口に言葉を入れないでください。
ローンの侯爵

0

String.format()とDecimalFormatは、デフォルトのロケールを使用して文字列を生成することに注意してください。そのため、整数と小数部の間の区切り文字としてドットまたはコンマを使用して、フォーマットされた数値を書き込むことができます。丸められた文字列が希望する形式であることを確認するには、次のようにjava.text.NumberFormatを使用します。

  Locale locale = Locale.ENGLISH;
  NumberFormat nf = NumberFormat.getNumberInstance(locale);
  // for trailing zeros:
  nf.setMinimumFractionDigits(2);
  // round to 2 digits:
  nf.setMaximumFractionDigits(2);

  System.out.println(nf.format(.99));
  System.out.println(nf.format(123.567));
  System.out.println(nf.format(123.0));

英語ロケールで印刷します(ロケールが何であっても):0.99 123.57 123.00

この例は、Farendaから引用したもので、doubleをStringに正しく変換する方法を示しています。


0

一般的に、丸めはスケーリングによって行われます。 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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.