1395

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

また、有効数字のみを表示したいと思います。つまり、末尾のゼロがあってはなりません。

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

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でこれを達成する最良の方法は何ですか?

4

37 に答える 37

843

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

例:

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

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

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

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

于 2008-09-30T16:14:15.940 に答える
506

が であると仮定するvaluedouble、次のことができます。

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

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

于 2008-09-30T16:07:24.550 に答える
203
new BigDecimal(String.valueOf(double)).setScale(yourScale, BigDecimal.ROUND_HALF_UP);

を取得しますBigDecimal。文字列を取得するには、そのメソッドを呼び出すか、Java 5+ BigDecimalのメソッドを呼び出して、プレーンなフォーマット文字列を取得します。toStringtoPlainString

サンプルプログラム:

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));
    }
于 2008-09-30T18:33:56.337 に答える
125

また、

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

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

于 2008-09-30T16:58:12.433 に答える
92

他の人が指摘したように、正しい答えは、またはのいずれ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
于 2012-10-02T03:18:23.370 に答える
88

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

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

于 2011-01-28T09:52:24.187 に答える
64

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

double d = 3.76628729;

DecimalFormat newFormat = new DecimalFormat("#.##");
double twoDecimal =  Double.valueOf(newFormat.format(d));
于 2011-09-29T07:01:59.260 に答える
46

Real の Java How-toにこのソリューションが掲載されており、Java 1.6 より前のバージョンと互換性があります。

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

更新: BigDecimal.ROUND_HALF_UP は非推奨です - RoundingMode を使用してください

BigDecimal bd = new BigDecimal(Double.toString(number));
bd = bd.setScale(decimalPlaces, RoundingMode.HALF_UP);
return bd.doubleValue();
于 2009-09-01T11:14:32.770 に答える
32
double myNum = .912385;
int precision = 10000; //keep 4 digits
myNum= Math.floor(myNum * precision +.5)/precision;
于 2008-09-30T16:09:26.840 に答える
31

@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 などの 10 進数を入力した場合、DecimalFormat はガベージを生成しません (たとえば、末尾のゼロ) を返します: 3.1415926.. もしあなたがそのように傾いているなら。確かに、一部の開発者の好みでは少し冗長ですが、処理中のメモリ使用量が少なく、実装が非常に簡単です。]

したがって、本質的に、DecimalFormat の優れた点は、文字列の外観と、設定された丸め精度のレベルを同時に処理できることです。エルゴ: 1 つのコード実装の価格で 2 つの利点が得られます。;)

于 2011-07-03T15:50:51.653 に答える
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. Coltの機能

    double rounded = Functions.round(0.00001).apply(0.912385)
    
  3. Wekaのユーティリティ

    double rounded = Utils.roundDouble(0.912385, 5)
    
于 2015-08-15T15:09:34.647 に答える
19

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

public static double round(double valueToRound, int numberOfDecimalPlaces)
{
    double multipicationFactor = Math.pow(10, numberOfDecimalPlaces);
    double interestedInZeroDPs = valueToRound * multipicationFactor;
    return Math.round(interestedInZeroDPs) / multipicationFactor;
}
于 2011-04-27T15:56:25.130 に答える
9

BigDecimal を使用できます

BigDecimal value = new BigDecimal("2.3");
value = value.setScale(0, RoundingMode.UP);
BigDecimal value1 = new BigDecimal("-2.3");
value1 = value1.setScale(0, RoundingMode.UP);
System.out.println(value + "n" + value1);

参照: http://www.javabeat.net/precise-rounding-of-decimals-using-rounding-mode-enumeration/

于 2014-07-22T09:49:35.497 に答える
8

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

Use BigDecimal or any other decimal-based format.

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

実際、私は最近、解析からロングを使用して、##################と同じ大きさの数値のGUIで(16進数の結果ではなく)正確な表現を取得しました。 ###############文字(例として)。

于 2011-07-04T09:10:41.860 に答える
7

このテーマに関する完全な答えが見つからなかったので、これを適切に処理する必要があるクラスをまとめました。

  • フォーマット: double を特定の小数点以下桁数の文字列に簡単にフォーマットする
  • 解析: フォーマットされた値を解析して double に戻します
  • Locale : デフォルトのロケールを使用してフォーマットおよび解析します
  • 指数表記: 一定の閾値を超えると指数表記を使い始める

使い方はとても簡単です:

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

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

}
于 2016-11-10T07:50:39.900 に答える
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 を返します。

于 2016-03-03T11:43:00.023 に答える
4

1.005これは、エッジケースを正しく丸めるより良い関数です。

簡単に言えば、四捨五入する前に、最小の float 値 (= 1 ulp; 最後の桁の単位) を数値に追加します。これは、ゼロから離れた、数値の後の次の表現可能な値に移動します。

これは、それをテストするための小さなプログラムです: ideone.com

/**
 * Round half away from zero ('commercial' rounding)
 * Uses correction to offset floating-point inaccuracies.
 * Works symmetrically for positive and negative numbers.
 */
public static double round(double num, int digits) {

    // epsilon correction
    double n = Double.longBitsToDouble(Double.doubleToLongBits(num) + 1);
    double p = Math.pow(10, digits);
    return Math.round(n * p) / p;
}

// test rounding of half
System.out.println(round(0.5, 0));   // 1
System.out.println(round(-0.5, 0));  // -1

// testing edge cases
System.out.println(round(1.005, 2));   // 1.01
System.out.println(round(2.175, 2));   // 2.18
System.out.println(round(5.015, 2));   // 5.02

System.out.println(round(-1.005, 2));  // -1.01
System.out.println(round(-2.175, 2));  // -2.18
System.out.println(round(-5.015, 2));  // -5.02
于 2018-02-13T10:37:22.710 に答える
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();
于 2011-09-08T11:09:10.377 に答える
0

小数点以下の桁数が制限されているかどうかを比較する簡単な方法。DecimalFormat、Math、または BigDecimal の代わりに、Casting を使用できます。

これがサンプルです。

public static boolean threeDecimalPlaces(double value1, double value2){
    boolean isEqual = false;
    // value1 = 3.1756 
    // value2 = 3.17
    //(int) (value1 * 1000) = 3175
    //(int) (value2 * 1000) = 3170

    if ((int) (value1 * 1000) == (int) (value2 * 1000)){
        areEqual = true;
    }

    return isEqual;
}
于 2020-10-17T10:24:46.503 に答える