java - 자리수 - 자바 소수점 반올림




자바에서 소수점 이하 n 자리로 반올림하는 법 (20)

내가 원하는 것은 double을 위로 반올림하는 문자열로 변환하는 방법입니다. 즉, 반올림 할 소수점이 5 인 경우, 항상 이전 숫자로 반올림됩니다. 이것은 대부분의 상황에서 대부분의 사람들이 예상하는 반올림의 표준 방법입니다.

나는 또한 유효 숫자 만 표시하고 싶습니다. 즉, 뒤 따르는 0이 없어야합니다.

이 작업을 수행하는 한 가지 방법은 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

자바에서 이것을 달성하는 가장 좋은 방법은 무엇입니까?


@Milhous : 반올림하기위한 십진수 형식이 우수합니다.

또한

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 쓰레기를 생성하지 않는 경우 ( 예를 들어 후행 0) 반환합니다 : 3.1415926 .. 만약 당신이 그런 식으로 경사. 허락하신다면, 일부 개발자의 취향에 약간은 장황 할 것입니다.하지만, 처리 과정에서 메모리 사용량이 적어 구현하기가 쉽습니다.]

따라서 기본적으로 DecimalFormat의 장점은 문자열 모양뿐만 아니라 반올림 정밀도 수준을 동시에 처리한다는 점입니다. Ergo : 하나의 코드 구현 가격으로 두 가지 이점을 얻습니다. ;)



DecimalFormat이 출력하는 가장 좋은 방법이지만 선호하지는 않습니다. 항상 double 값을 반환하기 때문에 항상이 작업을 수행합니다. 그래서 나는 단지 그것을 생산하는 것 이상의 것을 사용할 수 있습니다.

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

또는

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

큰 소수 자리 값이 필요하면 대신 BigDecimal을 사용할 수 있습니다. 어쨌든 .0 이 중요합니다. 그것없이 0.33333d5의 반올림은 0.33333을 반환하고 9 자리 만 허용됩니다. .0 이없는 두 번째 함수는 0.30000 반환 0.30000000000000004에 문제가 있습니다.


Real의 Java How-to는이 솔루션을 posts 하며 Java 1.6 이전 버전과도 호환됩니다.

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

간결한 해결책 :

   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 jpdymond 에게 감사드립니다.


나는 DecimalFormat --- 또는 양자 택일로 BigDecimal 을 사용하기 위해 선택한 대답에 동의한다.

먼저 아래 업데이트를 읽어주십시오!

그러나 double 값을 반올림하고 double 값 결과를 얻으려면 위에서 언급 한 것처럼 org.apache.commons.math3.util.Precision.round(..) 를 사용할 수 있습니다. 구현은 BigDecimal 사용하고, 느리고 가비지를 생성합니다.

Decimal4j 라이브러리의 DoubleRounder 유틸리티는 유사하지만 빠르며 가비지가없는 메소드를 제공합니다.

 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 참조 https://github.com/tools4j/decimal4j/wiki/DoubleRounder-Utility

면책 조항 : 저는 decimal4j 프로젝트에 관여하고 있습니다.

업데이트 : @iaforek가 지적했듯이 DoubleRounder는 때로는 반 직관적 인 결과를 반환합니다. 그 이유는 수학적으로 정확한 반올림을 수행하기 때문입니다. DoubleRounder.round(256.025d, 2) 는 256.02로 표시되는 double 값이 합리적인 값인 256.025보다 다소 작기 때문에 256.02로 반올림되어 반올림됩니다.

노트:

  • 이 동작은 BigDecimal(double) 생성자의 동작과 매우 유사하지만 문자열 생성자를 사용하는 valueOf(double) 에는 해당하지 않습니다.
  • 문제는 처음부터 더 높은 정밀도로 이중 반올림 단계를 거쳐 회피 될 수 있지만 복잡하며 여기서 자세히 다루지는 않습니다.

이 포스트에서 언급 한 이러한 이유로 인해 DoubleRounder를 사용하는 것은 권장 할 수 없습니다 .


네가 가지고 있다고 가정 해보자.

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


다른 사람들이 지적했듯이 정답은 DecimalFormat 또는 BigDecimal 을 사용하는 것입니다. 부동 소수점 에는 소수 자릿수가 없으므로 처음부터 특정 수만큼 반올림 /자를 수 없습니다. 십진수로 작업해야하는데, 그 두 클래스가하는 일입니다.

다음 코드를이 스레드의 모든 해답과 실제로 (및 다른 곳)에 곱셈을 추천하고 잘린 다음 나눗셈을 추천하는 카운터 예제로 게시하고 있습니다. 이 기법의 주창자는 다음 코드가 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

편집 : 아래의 몇 가지 설명을 해결하기 위해 다음과 같이 모듈러스 연산을 위해 BigDecimalnew MathContext(16) 를 사용하여 테스트 루프의 모듈러스 부분을 다시 redid합니다.

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

다음은 결과를 String으로 사용하려는 경우 사용할 수있는 사항에 대한 요약입니다.

  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 을 원한다면 어떤 라이브러리를 사용할 수 있는지 제안합니다. 그래도 문자열 변환에는 권장하지 않습니다. 이중에서는 정확히 원하는 것을 나타낼 수 없기 때문입니다 (예 : here 참조).

  1. Apache Commons Math의 Precision

    double rounded = Precision.round(0.912385, 5, BigDecimal.ROUND_HALF_UP);
    
  2. 콜트의 Functions

    double rounded = Functions.round(0.00001).apply(0.912385)
    
  3. Weka의 Utils

    double rounded = Utils.roundDouble(0.912385, 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 을 반환합니다.


실제로 계산을위한 십진수 (출력뿐만 아니라)를 원하면 double과 같은 바이너리 기반 부동 소수점 형식을 사용하지 마십시오.

Use BigDecimal or any other decimal-based format.

나는 계산을 위해 BigDecimal을 사용하지만, 당신이 다루는 숫자의 크기에 의존한다는 것을 명심해야한다. 대부분의 구현에서 double 또는 정수에서 Long으로 구문 분석하면 매우 큰 수의 계산을 수행하기에 충분합니다.

사실, 최근에 구문 분석을 사용하여 ############만큼 큰 숫자의 GUI에서 정확한 표현 (16 진 결과와 반대)을 얻었습니다. ######### 문자 (예로서).


아래의 코드 스 니펫은 n 자리를 표시하는 방법을 보여줍니다. 트릭은 변수 pp를 1로 설정하고 n을 0으로 설정하는 것입니다. 아래의 예에서 변수 pp 값은 5가 0이므로 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();

이 테마에 대한 완전한 대답을 찾지 못했기 때문에 다음을위한 지원을 통해 적절하게 처리해야하는 클래스를 작성했습니다.

  • 서식 지정 : 소수점 이하 자릿수로 문자열을 두 번 쉽게 서식을 지정합니다 .
  • 구문 분석 : 서식이 지정된 값을 다시 구문 분석하여 두 배로합니다.
  • 로케일 : 기본 로케일을 사용하여 형식화 및 구문 분석
  • 지수 표기 : 특정 임계 값 이후에 지수 표기법 사용을 시작합니다.

사용법은 매우 간단합니다 .

(이 예제에서는 사용자 정의 로켈을 사용하고 있습니다)

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

}

이것을 시도하십시오 : 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;
        }
    }
}

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

DecimalFormat 을 사용하여 doubleString 으로 변환하는 경우 매우 간단합니다.

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

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

필요한 동작에 따라 선택할 여러 RoundingMode 열거 형 값이 있습니다.


일반적으로 반올림은 크기 조정을 통해 수행됩니다. 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

만약 당신이 5 또는 n 소수점을 고려하십시오. 이 답이 귀하의 문제를 해결할 수 있습니다.

    double a = 123.00449;
    double roundOff1 = Math.round(a*10000)/10000.00;
    double roundOff2 = Math.round(roundOff1*1000)/1000.00;
    double roundOff = Math.round(roundOff2*100)/100.00;

    System.out.println("result:"+roundOff);

출력은 다음과 같습니다 : 123.0 1
이것은 루프 및 재귀 함수로 해결할 수 있습니다.


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

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

BigDecimal 을 얻을 것이다. 문자열을 빼내려면 BigDecimaltoString 메소드를 호출하거나 Java 5+의 toPlainString 메소드를 일반 형식 문자열로 호출하십시오.

샘플 프로그램 :

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






digits