Math.round(0.4999999999994)가 1을 반환하는 이유는 무엇입니까?
다음 프로그램에서 각 값이 다음 값보다 약간 작은 것을 볼 수 있습니다..5
를 제외하고 반올림됩니다.0.5
.
for (int i = 10; i >= 0; i--) {
long l = Double.doubleToLongBits(i + 0.5);
double x;
do {
x = Double.longBitsToDouble(l);
System.out.println(x + " rounded is " + Math.round(x));
l--;
} while (Math.round(x) > i);
}
인쇄하다
10.5 rounded is 11
10.499999999999998 rounded is 10
9.5 rounded is 10
9.499999999999998 rounded is 9
8.5 rounded is 9
8.499999999999998 rounded is 8
7.5 rounded is 8
7.499999999999999 rounded is 7
6.5 rounded is 7
6.499999999999999 rounded is 6
5.5 rounded is 6
5.499999999999999 rounded is 5
4.5 rounded is 5
4.499999999999999 rounded is 4
3.5 rounded is 4
3.4999999999999996 rounded is 3
2.5 rounded is 3
2.4999999999999996 rounded is 2
1.5 rounded is 2
1.4999999999999998 rounded is 1
0.5 rounded is 1
0.49999999999999994 rounded is 1
0.4999999999999999 rounded is 0
자바6 업데이트 31을 사용하고 있습니다.
요약
Java 6에서는 (아마도 그 이전)round(x)
로서 실장되다floor(x+0.5)
이것은 정확히 이 하나의 2병적인 경우에 대한 사양상의 버그입니다.1Java 7은 더 이상 이 실패한 3구현을 요구하지 않습니다.
문제
0.5+0.499999999999999999994는 정확히 1의 배정도입니다.
static void print(double d) {
System.out.printf("%016x\n", Double.doubleToLongBits(d));
}
public static void main(String args[]) {
double a = 0.5;
double b = 0.49999999999999994;
print(a); // 3fe0000000000000
print(b); // 3fdfffffffffffff
print(a+b); // 3ff0000000000000
print(1.0); // 3ff0000000000000
}
이는 0.49999999999999994의 지수가 0.5보다 작기 때문에 이들을 더하면 그 가수가 이동되고 ULP가 커지기 때문입니다.
해결 방법
Java 7 이후 OpenJDK(예를 들어)는 다음과 4같이 Java 7을 구현합니다.
public static long round(double a) {
if (a != 0x1.fffffffffffffp-2) // greatest double value less than 0.5
return (long)floor(a + 0.5d);
else
return 0;
}
1. http://docs.oracle.com/javase/6/docs/api/java/lang/Math.html#round%28double%29
2. http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6430675 (이것에 대해서는, @SimonNickerson에게 문의해 주세요)
3. http://docs.oracle.com/javase/7/docs/api/java/lang/Math.html#round%28double%29
4. http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7u40-b43/java/lang/Math.java#Math.round%28double%29
이것은 기존의 버그로 보입니다(Java bug 6430675: Math.round는 Java 7에서 수정된 0x1.ffffffff-2)에 대해 놀라운 동작을 합니다.
JDK 6의 소스 코드:
public static long round(double a) {
return (long)Math.floor(a + 0.5d);
}
JDK 7의 소스 코드:
public static long round(double a) {
if (a != 0x1.fffffffffffffp-2) {
// a is not the greatest double value less than 0.5
return (long)Math.floor(a + 0.5d);
} else {
return 0;
}
}
값이 0.49999999999999994d인 경우 JDK6에서는 플로어를 호출하고1을 반환하지만 JDK7에서는if
조건은 숫자가 0.5보다 작은 최대 배수 값인지 여부를 확인하는 것입니다.이 경우에서와 같이 숫자는 0.5보다 작은 최대 배수가 아니기 때문에else
block은 0을 반환합니다.
0.4999999999999999999d는 0.5보다 작은 최대 배값이기 때문에 1을 반환하지만 0은 반환하지 않습니다.
JDK 1.6 32비트에서도 마찬가지지만 Java 7 64비트에서는 0.499999999999994가 0으로 반올림되고 마지막 줄은 인쇄되지 않습니다.VM의 문제인 것 같습니다만, 부동 소수점을 사용하는 경우는, 환경(CPU, 32비트, 또는 64비트 모드)에 따라서 결과가 약간 다를 것으로 예상할 수 있습니다.
그리고 사용할 때round
행렬을 반전시키는 등의 방법으로 큰 차이를 만들 수 있습니다.
x64 출력:
10.5 rounded is 11
10.499999999999998 rounded is 10
9.5 rounded is 10
9.499999999999998 rounded is 9
8.5 rounded is 9
8.499999999999998 rounded is 8
7.5 rounded is 8
7.499999999999999 rounded is 7
6.5 rounded is 7
6.499999999999999 rounded is 6
5.5 rounded is 6
5.499999999999999 rounded is 5
4.5 rounded is 5
4.499999999999999 rounded is 4
3.5 rounded is 4
3.4999999999999996 rounded is 3
2.5 rounded is 3
2.4999999999999996 rounded is 2
1.5 rounded is 2
1.4999999999999998 rounded is 1
0.5 rounded is 1
0.49999999999999994 rounded is 0
다음 답변은 Oracle 버그리포트 6430675의 발췌입니다.자세한 내용은 보고서를 참조하십시오.
{Math, StrictMath.round} 메서드는 다음과 같이 정의됩니다.
(long)Math.floor(a + 0.5d)
이중논쟁을 위해.이 정의는 보통 예상대로 기능하지만 0x1.ffffffffff-2(0.4999999999994)에 대해서는 0이 아닌 1이라는 놀라운 결과를 얻을 수 있습니다.
값 0.4999999999999999994는 0.5보다 작은 최대 부동소수점 값입니다.16진수 부동소수점 리터럴의 값은 0x1.ffffffff-2로, (2 - 2^52) * 2^-2. == (0.5 - 2^54)와 같습니다.따라서 합계의 정확한 값은
(0.5 - 2^54) + 0.5
오후 10:23:49
메서드가 정의된 대로 동작하고 있는 동안 이 입력의 동작은 매우 놀라우며, 사양을 "가장 가까운 긴 타이에 반올림"과 같이 수정하면 이 입력의 동작을 변경할 수 있습니다.
언급URL : https://stackoverflow.com/questions/9902968/why-does-math-round0-49999999999999994-return-1
'programing' 카테고리의 다른 글
기능의 시간 복잡도는 어느 정도입니까? (0) | 2022.06.13 |
---|---|
String Builder와 String Buffer의 차이점 (0) | 2022.06.13 |
C에서 프리프로세서 변수를 인쇄할 수 있습니까? (0) | 2022.06.13 |
Vue.js 컴포넌트 내의 모멘트 라이브러리를 사용하는 방법 (0) | 2022.06.13 |
리눅스에서 데몬 생성 (0) | 2022.06.13 |