Program Tip

자바에서 더블 라운드

programtip 2020. 12. 14. 20:45
반응형

자바에서 더블 라운드


반올림에 대한이 훌륭한 솔루션을 찾았습니다.

static Double round(Double d, int precise) {
    BigDecimal bigDecimal = new BigDecimal(d);
    bigDecimal = bigDecimal.setScale(precise, RoundingMode.HALF_UP);
    return bigDecimal.doubleValue();
}

그러나 결과는 혼란 스럽습니다.

System.out.println(round(2.655d,2)); // -> 2.65
System.out.println(round(1.655d,2)); // -> 1.66

이 출력을 제공하는 이유는 무엇입니까? jre 1.7.0_45를 사용하고 있습니다.


당신은 교체해야

BigDecimal bigDecimal = new BigDecimal(d);

BigDecimal bigDecimal = BigDecimal.valueOf(d);

예상되는 결과를 얻을 수 있습니다.

2.66
1.66

Java API의 설명 :

BigDecimal.valueOf (double val) -Double.toString () 메서드에서 제공하는 double의 정규 문자열 표현을 사용합니다. 이것은 double (또는 float)을 BigDecimal로 변환하는 데 선호되는 방법입니다.

new BigDecimal (double val ) -double 의 이진 부동 소수점 값의 정확한 십진수 표현을 사용 하므로이 생성자의 결과는 다소 예측할 수 없습니다.


다음과 같이 프로그램을 변경하려고 할 수 있습니다.

static Double round(Double d, int precise) 
{
BigDecimal bigDecimal = BigDecimal.valueOf(d);
bigDecimal = bigDecimal.setScale(precise, RoundingMode.HALF_UP);
return bigDecimal.doubleValue();
}

Ideone 샘플

Success  time: 0.07 memory: 381184 signal:0
Rounded: 2.66
Rounded: 1.66

Success  time: 0.07 memory: 381248 signal:0
Rounded: 2.66
Rounded: 1.66

BigDecimal.valueOfnew BigDecimalJoachim Sauer 의 말에 따르면으로 예상되는 결과를 얻는 이유는 다음 같습니다.

BigDecimal.valueOf(double)전달 된 double 값 표준 문자열 표현사용하여 BigDecimal 객체를 인스턴스화합니다. 다른 말로하면, BigDecimal물체 의 가치는 당신이 할 때 보게 될 것 System.out.println(d)입니다.

new BigDecimal(d)그러나 사용하는 경우 BigDecimal은 가능한 한 정확하게 double 값을 나타내려고 시도합니다. 이로 인해 일반적으로 원하는 것보다 훨씬 많은 숫자가 저장됩니다.

따라서 프로그램에서보고있는 혼란발생 합니다.

Java Doc에서 :

BigDecimal.valueOf (double val) -Double.toString (double) 메서드에서 제공하는 double의 정규 문자열 표현을 사용하여 double을 BigDecimal로 변환합니다.

new BigDecimal (double val) -

double을 double의 2 진 부동 소수점 값의 정확한 10 진수 표현 인 BigDecimal로 변환합니다. 반환 된 BigDecimal의 스케일은 (10scale × val)이 정수가되는 가장 작은 값입니다. 메모:

  • 이 생성자의 결과는 다소 예측할 수 없습니다. Java에서 new BigDecimal (0.1)을 작성하면
    정확히 0.1 (스케일되지 않은 값 1,
    스케일 1) 인 BigDecimal이 생성 되지만 실제로는 0.1000000000000000055511151231257827021181583404541015625와 같습니다. 0.1은 정확히 double로 표현할 수 없기 때문입니다 (또는
    유한 길이의 이진 분수 로 표현할 수 없습니다 ). 따라서
    생성자에 전달되는 은 외관에도 불구하고 정확히 0.1과 동일하지 않습니다.
  • 반면에 String 생성자는 완벽하게 예측 가능합니다. new BigDecimal ( "0.1")을 작성하면 예상대로 정확히 0.1 인 BigDecimal이 생성됩니다. 따라서 일반적으로이 생성자보다 우선적으로 String 생성자를 사용하는 것이 좋습니다.
  • Double을 BigDecimal의 소스로 사용해야하는 경우이 생성자가 정확한 변환을 제공합니다. Double.toString (double) 메서드를
    사용하여 double을 String으로 변환
    한 다음 BigDecimal (String)
    생성자 를 사용하는 것과 동일한 결과를 제공하지 않습니다 . 그 결과를 얻으려면 정적 valueOf (double)
    메서드를 사용하십시오.

이 테스트 케이스 는 매우 자명합니다.

public static void main (String[] args) throws java.lang.Exception
{
    System.out.println("Rounded: " + round(2.655d,2)); // -> 2.65
    System.out.println("Rounded: " + round(1.655d,2)); // -> 1.66
}

public static Double round(Double d, int precise)
{       
    BigDecimal bigDecimal = new BigDecimal(d);
    System.out.println("Before round: " + bigDecimal.toPlainString());
    bigDecimal = bigDecimal.setScale(precise, RoundingMode.HALF_UP);
    System.out.println("After round: " + bigDecimal.toPlainString());
    return bigDecimal.doubleValue();
}

산출:

Before round: 2.654999999999999804600747665972448885440826416015625
After round: 2.65
Rounded: 2.65

Before round: 1.6550000000000000266453525910037569701671600341796875
After round: 1.66
Rounded: 1.66

이를 고치기위한 더러운 해킹 은 두 단계반올림 하는 것입니다 .

static Double round(Double d, int precise)
{
    BigDecimal bigDecimal = new BigDecimal(d);
    System.out.println("Before round: " + bigDecimal.toPlainString());
    bigDecimal = bigDecimal.setScale(15, RoundingMode.HALF_UP);
    System.out.println("Hack round: " + bigDecimal.toPlainString());
    bigDecimal = bigDecimal.setScale(precise, RoundingMode.HALF_UP);
    System.out.println("After round: " + bigDecimal.toPlainString());
    return bigDecimal.doubleValue();
}

Here, 15 is a bit under the maximum number of digits a double can represent in base 10. Output:

Before round: 2.654999999999999804600747665972448885440826416015625
Hack round: 2.655000000000000
After round: 2.66
Rounded: 2.66

Before round: 1.6550000000000000266453525910037569701671600341796875
Hack round: 1.655000000000000
After round: 1.66
Rounded: 1.66

As said in API

  1. The results of this constructor can be somewhat unpredictable. One might assume that writing new BigDecimal(0.1) in Java creates a BigDecimal which is exactly equal to 0.1 (an unscaled value of 1, with a scale of 1), but it is actually equal to 0.1000000000000000055511151231257827021181583404541015625. This is because 0.1 cannot be represented exactly as a double (or, for that matter, as a binary fraction of any finite length). Thus, the value that is being passed in to the constructor is not exactly equal to 0.1, appearances notwithstanding.

  2. The String constructor, on the other hand, is perfectly predictable: writing new BigDecimal("0.1") creates a BigDecimal which is exactly equal to 0.1, as one would expect. Therefore, it is generally recommended that the String constructor be used in preference to this one.

  3. When a double must be used as a source for a BigDecimal, note that this constructor provides an exact conversion; it does not give the same result as converting the double to a String using the Double.toString(double) method and then using the BigDecimal(String) constructor. To get that result, use the static valueOf(double) method.

It's because of cannot represent double value exactly. So you have to use BigDecimal bigDecimal = BigDecimal.valueOf(d); instead of BigDecimal bigDecimal = new BigDecimal(d);


Rounding a double resp Double in itself does not make much sense, as a double datatype cannot be rounded (easily, or at all?).

What you are doing is:

  1. Take a Double d as input and a int precise number of digits behind the seperator.
  2. Create a BigDecimal from that d.
  3. Round the BigDecimal correctly.
  4. Return the double value of that BigDecimal, which has no rounding applied to it anymore.

You can go two ways:

  1. You can return a BigDecimal that represents the rounded double, and later decide what you do with it.
  2. You can return a String representing the rounded BigDecimal.

Either of those ways will make sense.


Decimal numbers can't be represented exactly in double.

So 2.655 ends up being this: 2.65499999999999980460074766597

whereas 1.655 ends up being this: 1.655000000000000026645352591

참고URL : https://stackoverflow.com/questions/22036885/round-a-double-in-java

반응형