Java에서 소수점 이하 자릿수를 반올림하는 방법


1259

내가 원하는 것은 double-up 방법을 사용하여 반올림하는 문자열로 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

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

답변:


751

을 사용하고 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

편집 : 원래 답변은 이중 값의 정확성을 다루지 않습니다. 반올림 여부에 상관없이 괜찮습니다. 그러나 정확한 반올림을 원하면 예상되는 값의 정확도를 고려해야합니다. 부동 소수점 값은 내부적으로 이진 표현을 갖습니다. 즉, 2.7735와 같은 값은 실제로 정확한 값을 갖지 않습니다. 약간 크거나 작을 수 있습니다. 내부 값이 약간 작은 경우 2.7740으로 올림되지 않습니다. 이러한 상황을 해결하려면 작업중인 값의 정확성을 알고 반올림하기 전에 해당 값을 더하거나 빼야합니다. 예를 들어, 값이 최대 6 자리까지 정확하다는 것을 알고 반값을 반올림하려면 해당 정확도를 값에 추가하십시오.

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

반올림하려면 정확도를 뺍니다.


9
이것은 아마도 지금까지 제시된 최고의 솔루션 일 것입니다. DecimalFormat 클래스를 처음 보았을 때이 기능을 발견하지 못한 이유는 Java 1.6에서만 도입 되었기 때문입니다. 불행히도 나는 1.5를 사용하도록 제한되었지만 미래를 아는 것이 유용 할 것입니다.
Alex Spurling

1
나는 이것을 시도했다 : "#.##", rounding HALF_UP. 256.335f-> "256.33"... (예는 주석에서 @asterite의 답변으로 제공됩니다).
bigstones

6
DecimalFormat은 현재 로컬 구성에 따라 다르므로 점이 구분 기호로 표시되지 않을 수 있습니다. 나는 아래에 Asterite의 답변을 개인적으로 선호합니다
Gomino

1
또한 DecimalFormat이 스레드로부터 안전하지 않아야합니다. 당으로 자바 문서 : 진수 포맷은 동기화되지 않습니다. 각 스레드마다 별도의 형식 인스턴스를 작성하는 것이 좋습니다. 여러 스레드가 동시에 형식에 액세스하는 경우 외부에서 동기화해야합니다.
CGK

1
어떻게하면 적절한 라운딩을하지 있도록해야합니까 그것을하지 않습니다 라운드 0.0004 0.001 너무

471

가정 valueA는 double, 당신은 할 수 있습니다 :

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

5 자리 정밀도입니다. 0의 수는 소수의 수를 나타냅니다.


71
업데이트 : 방금 DecimalFormat을 사용하는 것보다이 방법이 더 빠르다는 것을 확인했습니다. DecimalFormat을 200 번 사용 하여이 방법을 반복했습니다. DecimalFormat은 200 개의 루프를 완료하는 데 14ms가 걸렸으며,이 방법은 1ms 미만이었습니다. 내가 의심했듯이 이것은 더 빠릅니다. 당신이 시계주기에 의해 지불받을 경우, 이것은 당신이해야 할 일입니다. Chris Cudmore가 자신이 정직하다고 말한 것을 말한 것에 놀랐습니다. 객체를 할당하는 것은 항상 프리미티브를 캐스팅하고 정적 메소드를 사용하는 것보다 항상 비싸다 (10 진수 형식과는 대조적으로 Math.round ()).
Andi Jay

99
이 기술은 90 % 이상의 사례에서 실패합니다. -1.
user207421

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으로 반올림됨을 의미합니다. 부동 소수점 숫자에서 실수로 변환 할 때 문제가 발생합니다. 여기에 stackoverflow.com/a/12684082/144578
Sebastiaan van den Broek

6
@SebastiaanvandenBroek : 와우 ​​나는 그것이 틀린 대답을 얻는 것이 쉬운 지 알지 못했습니다. 그러나 정확하지 않은 숫자로 작업하는 경우 값 이 정확하지 않다는 것을 인식해야합니다 . 265.335실제로 265.335 += tolerance허용 오차는 이전 작업 및 입력 값 범위에 따라 달라집니다. 우리는 진실하고 정확한 가치를 모릅니다. 가장자리 값에서 대답 모두 틀림없이 맞습니다. 우리가 정확해야한다면, 우리는 이중으로 일해서는 안됩니다. 는 fail여기를 두 번 다시 변환에 있지 않습니다. OP의 생각에 따르면 그는 들어오는 265.335것에 정확하게 의존 할 수 있습니다 .
ToolmakerSteve

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

당신을 얻을 것입니다 BigDecimal. 그것에서 문자열을 얻으려면, 그냥 전화 BigDecimaltoString방법, 또는 toPlainString일반 형식 문자열에 대한 자바에 대한 방법을 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.toString ()를 호출)
에티엔 느 느뵈

4
좋은. new BigDecimal(doubleVar)부동 소수점 반올림 문제가 발생할 수 있으므로 모서리를 자르지 말고 사용하십시오
Edd

8
@Edd, 흥미롭게도, 반올림 문제는 SebastiaanvandenBroek이 애 스터 라이트의 답변에 대한 언급에서 언급 한 경우에 발생합니다. 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

7
@ToolmakerSteve double과 함께 새로운 BigDecimal을 사용하면 double 값을 직접 가져 와서 BigDecimal을 만드는 데 사용하려고 시도하지만 BigDecimal.valueOf 또는 tostring 형식을 사용하면 변환 전에 문자열로 먼저 구문 분석합니다 (보다 정확한 표현) .
MetroidFan2002 5

116

당신은 또한 사용할 수 있습니다

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

후행 0이 있는지 확인하십시오.


25
나는 질문의 목표 중 하나가 "가되어야했다 생각 하지 후행 제로가 될".
도시락

7
이 질문에서 op는 0을 원하지 않았지만 이것이 바로 내가 원하는 것입니다. 소수 자릿수가 3 인 숫자 목록이있는 경우 0이더라도 모두 같은 자릿수를 갖기를 원합니다.
Tom Kincaid

당신이 지정하는 것을 잊었다RoundingMode.
IgorGanapolsky

1
기본으로 @IgorGanapolsky Decimal mode사용RoundingMode.HALF_EVEN.
EndermanAPM

87

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

이 스레드의 모든 답변과 실제로 곱셈과 잘림, 나눗셈을 권장하는 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

편집 : 내가 사용 테스트 루프의 계수 부분을 redid 아래 몇 가지 의견을 해결하기 위해, 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 놀랍지 않습니다. 나는 첫 번째 컴퓨팅 과정으로 '수학적 방법'을 수행하고 부동 소수점이 할 수있는 것과 할 수없는 것에 대해 처음에 소개하는 행운을 얻었습니다. 대부분의 프로그래머는 그것에 대해 모호합니다.
user207421

15
당신이하는 모든 것은 부동이 많은 십진수 값을 정확하게 나타내지 않는다는 것을 반박하는 것입니다. 우리 모두가 이해하기를 바랍니다. 반올림으로 인해 문제가 발생하지 않습니다. 인정한대로 숫자는 여전히 예상대로 인쇄됩니다.
Peter Lawrey

8
테스트가 실패하고 round ()를 꺼내면 테스트가 94 % 실패합니다. ideone.com/1y62CY 인쇄 100 trials 94 errors통과하는 테스트로 시작하고 반올림을 도입하면 테스트가 중단된다는 것을 보여 주어야합니다.
Peter Lawrey

6
반박, 여기에 반박. 이 범위에서 double오류없이 Math.round 사용 ideone.com/BVCHh3
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 사용자 (및 이하)에게 가장 좋은 솔루션이라고 생각합니다. 한 가지 의견은 HALF_EVEN 반올림 모드를 사용하지 않는 것입니다. HALF_EVEN 반올림 모드는 홀수 및 짝수 (예를 들어 2.5 반올림, 5.5 반올림, 6 반올림)에 대한 동작이 다르기 때문에 사용하지 마십시오.
IcedDante

4
첫 번째 해결책은 맞습니다. 두 번째 해결책은 효과가 없습니다. 증거는 여기 를 참조 하십시오 .
user207421

1
@ EJP : 첫 번째 해결책조차 RoundingMode.HALF_UP잘못되었습니다. 로 사용해보십시오 1.505. 올바른 방법은을 사용하는 것 BigDecimal.valueOf(d)입니다.
Matthias Braun

Matthias Braun, 솔루션은 훌륭합니다. 따라서 31을 올립니다. 1.505를 가져 와서 double에서 10 진수로 변환하려면 먼저 1.505 소수를 부동 소수점 double에 1.50499998로 저장합니다. 그러면 Double.toString (x)로 먼저 변환해야합니다. 그런 다음 BigDecimal ()에 넣으십시오. 그러나 그것은 매우 느리고 처음에는 속도에 double을 사용하는 목적을 무효화합니다.
hamish

1
BigDecimal (225ms) 및 Math.round (2ms) 방식으로 100k의 루프를 실행했으며 여기에 타이밍이 있습니다 ... 소요 시간 : 225 밀리 초를 사용하여 변환 : 9232.13 소요 시간 : 2 밀리 초로 변환 : 9232.13 techiesinfo.com
user1114134

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

Double.parseDouble()String입력이 필요 합니다.
Ryde

38

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

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

2
네 이것은 math.round가 양수에 대해 정확히하는 일이지만 음수로 시도해 보셨습니까? 사람들은 다른 솔루션에서 math.round를 사용하여 음수의 경우도 다룹니다.
hamish

참고 : 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에서 가비지를 생성하지 않습니다 ( 예를 들어 후행 0)을 반환합니다. 3.1415926.. 그렇게 기울어 진 경우. 물론 일부 개발자의 취향에 따라 조금 장황하지만 처리 중에 메모리 사용량이 적고 구현하기가 쉽습니다.]

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


2
당신이 정말로 (그리고 출력 전용)은, 계산에 십진수합니다 바이너리 기반의 부동 소수점 형식을 사용하지 않는double. BigDecimal 또는 기타 10 진수 형식을 사용하십시오.
Paŭlo 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은 원하는 것을 정확하게 표현하지 못할 수도 있으므로 문자열 변환에는 권장하지 않습니다 (예 : here 참조 ).

  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. Weka의 유틸리티

    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

효과가있다. 그러나 무의식적으로 float와 double은 근사치입니다. 첫 번째 예를 살펴 보겠습니다. Math.round 전에 interestInZeroDPs의 출력을 인쇄하면 100.49999999999999가 인쇄됩니다. 당신은 Math.round와 같은 정밀도를 100으로 반올림했습니다. 본질이나 수레와 두 배로 인해 제대로 작동하지 않을 경우 경계선이
생깁니다

더블은 빠릅니다! 십진수가 느립니다. 컴퓨터는 십진수 표기법으로 생각을 처리하지 않습니다. 부동 소수점을 두 배 빠르게 유지하려면 소수 자릿수를 포기해야합니다.
hamish

@hamish 문제는 속도가 아니라 정밀도에 관한 것입니다.
user207421



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 to string을 쉽게 서식 지정
  • 구문 분석 : 서식이 지정된 값을 다시 두 번 구문 분석
  • 로케일 : 기본 로케일을 사용하여 형식화 및 구문 분석
  • 지수 표기법 : 특정 임계 값 후에 지수 표기법을 사용하여 시작

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

(이 예제를 위해 사용자 지정 로캘을 사용하고 있습니다)

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

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

Use BigDecimal or any other decimal-based format.

BigDecimal을 계산에 사용하지만 처리하는 숫자의 크기에 따라 다릅니다. 대부분의 구현에서 double 또는 integer에서 Long으로 구문 분석하는 것이 매우 많은 수의 계산에 충분하다는 것을 알았습니다.

사실, 최근에는 구문 분석에서 긴 구문 분석을 사용하여 GUI에서 ################# ############### 문자 (예를 들어).


6

이를 위해이 포맷터를 사용할 수 있습니다.

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

또는:

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

항상이 소수점 이하 자릿수를 얻으려면이 방법을 사용하십시오.

   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값의 결과를, 당신은 사용할 수 있습니다 org.apache.commons.math3.util.Precision.round(..)위에서 언급 한 바와 같이. 구현은을 사용 BigDecimal하고 느리고 가비지를 생성합니다.

DoubleRounderdecimal4j 라이브러리 의 유틸리티 는 비슷하지만 빠르고 가비지없는 방법을 제공 합니다.

 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

@iaforek : DoubleRounder가 수학적으로 올바른 반올림을 수행하기 때문에 이것은 정확합니다. 그러나 나는 이것이 다소 반 직관적이라는 것을 인정하고 이에 따라 내 대답을 업데이트 할 것입니다.
marco

3

아래 코드 스 니펫은 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();

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

찢다

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보다 작지 않고 소수 자릿수를 초과 한 후 0이있는 경우에는 실패합니다. myVal = 9.00000000912385; 위의 9.0을 반환합니다. myVal의 모든 경우에 작동하는 솔루션을 제공해야한다고 생각합니다. 당신이 언급 한 가치를 위해 특별히 아닙니다.
tavalendo

@ user102859 예제에서 9.0이 올바른 결과입니다. 이것이 어떻게 실패하는지 이해하지 못합니다.
Craigo 2016 년

2

DecimalFormat이 출력하는 가장 좋은 방법이지만 선호하지는 않습니다. 항상 double 값을 반환하기 때문에 항상이 작업을 수행합니다. 출력보다 더 많은 것을 사용할 수 있습니다.

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

또는

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

소수점 이하 자릿수가 큰 경우 대신 BigDecimal을 사용할 수 있습니다. 어쨌든 .0중요합니다. 그것 없이는 0.33333d5의 반올림은 0.33333을 반환하고 9 자리 만 허용됩니다. 두 번째 함수가없는 경우 .00.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 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보다 위 또는 아래에 저장해야합니다. 즉, 컴파일 할 때 이중으로 저장됩니다. 어쨌든 당신과 같은 프린지 케이스가 중요하지 않은 두 배. 그렇다면 3dp를 사용하고 10 진수로 변환 한 다음 게시 한 링크와 같이 10 진수 기능을 수행합니다.
hamish

1
@hamish 나는 Matthias가 값을 십진수로 컴파일하는 것과 같은 것을 '독자에게 믿어야 할 곳'을 보지 못합니다. 다른 사람의 입에 단어를 넣지 마십시오.
user207421

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.