자바에서 더블 라운드
반올림에 대한이 훌륭한 솔루션을 찾았습니다.
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();
}
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.valueOf
new BigDecimal
Joachim 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로 변환합니다.
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
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.
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.
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:
- Take a
Double d
as input and aint precise
number of digits behind the seperator. - Create a
BigDecimal
from thatd
. - Round the
BigDecimal
correctly. - Return the
double
value of thatBigDecimal
, which has no rounding applied to it anymore.
You can go two ways:
- You can return a
BigDecimal
that represents the rounded double, and later decide what you do with it. - You can return a
String
representing the roundedBigDecimal
.
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
'Program Tip' 카테고리의 다른 글
Rails에서 오류없이 파일을 삭제하는 방법 (0) | 2020.12.14 |
---|---|
IIS 용 PHP 관리자가 설치되지 않음 (0) | 2020.12.14 |
주석으로 IntelliJ IDEA에서 중복 경고를 억제합니다. (0) | 2020.12.14 |
SASL 인증 단계에서 서버가 오류를 반환했습니다. 인증 실패 (0) | 2020.12.14 |
JavaScript에서 OS 경로 구분 기호를 결정하는 방법은 무엇입니까? (0) | 2020.12.14 |