나는 0.1
십진수가 유한 이진수 ( 설명 )로 정확하게 표현 될 수 없다는 것을 알고 있으므로 double n = 0.1
약간의 정밀도를 잃고 정확하게되지 않습니다 0.1
. 반면에는 0.5
이므로 정확하게 표현할 수 있습니다 0.5 = 1/2 = 0.1b
.
0.1
세 번 추가 하면 정확히 0.3
다음 코드가 인쇄 되지는 않는다고 이해할 수 있습니다 false
.
double sum = 0, d = 0.1;
for (int i = 0; i < 3; i++)
sum += d;
System.out.println(sum == 0.3); // Prints false, OK
그러나 0.1
5 번 을 추가 하면 정확히 0.5
어떻게됩니까? 다음 코드가 인쇄됩니다 true
.
double sum = 0, d = 0.1;
for (int i = 0; i < 5; i++)
sum += d;
System.out.println(sum == 0.5); // Prints true, WHY?
0.1
정확하게 표현할 수 없다면 어떻게 5 번 더하면 정확하게 0.5
표현할 수 있습니까?
답변
반올림 오류는 무작위가 아니며 구현 방식은 오류를 최소화하려고 시도합니다. 이는 때때로 오류가 표시되지 않거나 오류가 없음을 의미합니다.
예를 들어 0.1
정확히되지 0.1
즉 new BigDecimal("0.1") < new BigDecimal(0.1)
하지만 0.5
정확히1.0/2
이 프로그램은 실제 가치를 보여줍니다.
BigDecimal _0_1 = new BigDecimal(0.1);
BigDecimal x = _0_1;
for(int i = 1; i <= 10; i ++) {
System.out.println(i+" x 0.1 is "+x+", as double "+x.doubleValue());
x = x.add(_0_1);
}
인쇄물
0.1000000000000000055511151231257827021181583404541015625, as double 0.1
0.2000000000000000111022302462515654042363166809082031250, as double 0.2
0.3000000000000000166533453693773481063544750213623046875, as double 0.30000000000000004
0.4000000000000000222044604925031308084726333618164062500, as double 0.4
0.5000000000000000277555756156289135105907917022705078125, as double 0.5
0.6000000000000000333066907387546962127089500427246093750, as double 0.6000000000000001
0.7000000000000000388578058618804789148271083831787109375, as double 0.7000000000000001
0.8000000000000000444089209850062616169452667236328125000, as double 0.8
0.9000000000000000499600361081320443190634250640869140625, as double 0.9
1.0000000000000000555111512312578270211815834045410156250, as double 1.0
참고 : 그것은 0.3
약간 꺼져 있지만 0.4
비트에 도달하면 53 비트 제한에 맞도록 비트를 아래로 이동해야하며 오류는 무시됩니다. 또, 에러에 대한 크립 위로 0.6
하고 0.7
있지만 위해 0.8
에 1.0
오류가 폐기된다.
5 번 추가하면 오류가 누적되고 취소되지 않습니다.
오류가 발생하는 이유는 정밀도가 제한되어 있기 때문입니다. 즉, 53 비트. 즉, 숫자가 커질수록 더 많은 비트를 사용하므로 비트를 끝까지 버려야합니다. 이 경우 반올림이 발생하여 선호합니다.
더 작은 숫자 (예 : 0.1-0.0999
=>)를 얻을 때 반대 효과를 얻을 수 있으며 1.0000000000000286E-4
이전보다 더 많은 오류가 표시됩니다.
Java 6에서 Math.round (0.49999999999999994)가 1을 반환하는 이유는 이것의 예입니다 .이 경우 계산에서 비트 손실이 답변과 큰 차이를 가져옵니다.
답변
부동 소수점에서의 오버플로 금지 x + x + x
는 실제 3 *로 정확하게 올림 된 (즉 가장 가까운) 부동 소수점 숫자 x
이며, x + x + x + x
정확히 4 * x
이며, x + x + x + x + x
5 *의 경우 올바르게 반올림 된 부동 소수점 근사값입니다 x
.
에 대한 첫 번째 결과 는 정확한 x + x + x
사실에서 비롯됩니다 x + x
. x + x + x
따라서 단 하나의 반올림 결과입니다.
두 번째 결과는 더 어렵습니다. 여기 에서 한 가지 시연이 설명 됩니다 (스티븐 캐논은의 마지막 세 자리에 대한 사례 분석을 통해 다른 증거를 제시합니다 x
). 요약하면, 3 * x
는 2 * 와 동일한 2 진법에x
있거나 4 *와 같은 2 진법에 있으며 x
, 각각의 경우 세 번째 덧셈의 오류가 두 번째 덧셈의 오류를 취소한다고 추론 할 수 있습니다 ( 우리가 이미 말했듯이 첫 번째 추가는 정확합니다.)
세 번째 결과 인 ” x + x + x + x + x
올바르게 반올림 됨 ” 은 첫 번째 결과 의 정확도와 동일한 방식으로 두 번째 결과에서 파생됩니다 x + x
.
두 번째 결과는 왜 0.1 + 0.1 + 0.1 + 0.1
정확히 부동 소수점 숫자인지를 설명합니다 0.4
. 유리수 1/10과 4/10은 부동 소수점으로 변환 될 때 동일한 상대 오차로 동일한 방식으로 근사됩니다. 이 부동 소수점 숫자의 비율은 정확히 4입니다. 상기 제 1 및 제 결과 표시 0.1 + 0.1 + 0.1
와는 0.1 + 0.1 + 0.1 + 0.1 + 0.1
그 자체로, 그들은 단지 각각에 결과를 관련, 순 에러 분석에 의해 추정되는 것보다 더 적은 에러를 갖는 것으로 예상 될 수 있지만, 3 * 0.1
및 5 * 0.1
근접하지만 반드시 일치하는 것으로 예상 될 수있는, 0.3
그리고 0.5
.
0.1
네 번째 덧셈 후에 계속 덧셈 을하면, 마지막으로“ 0.1
n 번에 자신을 더한 값”이 n에서 n * 0.1
분기되고 n / 10에서 더 많이 갈라지는 반올림 오류가 나타납니다 . n의 함수로 “0.1 n을 자신에 추가 한 n”의 값을 플로팅하는 경우, n의 가산 결과가 특정 이진법으로 떨어지 자마자 이진법으로 일정한 기울기 선을 관찰 할 수 있습니다. 첨가물의 특성은 동일한 이진법으로 결과를 생성 한 이전 첨가와 유사 할 것으로 예상된다). 동일한 바이너리 내에서 오류가 커지거나 줄어 듭니다. 이진법에서 이진법으로 기울기 순서를 살펴보면 반복되는 자릿수를 인식 할 수 있습니다.0.1
잠시 동안 바이너리로. 그 후 흡수가 시작되고 곡선이 평평 해집니다.
답변
부동 소수점 시스템은 반올림을위한 몇 가지 추가 정밀도를 포함하여 다양한 마법을 수행합니다. 따라서 0.1의 부정확 한 표현으로 인한 매우 작은 오류는 0.5로 반올림됩니다.
부동 소수점은 숫자를 나타내는 훌륭하지만 부정확 한 방법이라고 생각하십시오. 가능한 모든 숫자가 컴퓨터에서 쉽게 표현되는 것은 아닙니다. PI와 같은 비이성적 인 숫자. 또는 SQRT (2)처럼. (심볼 수학 시스템은 그것들을 나타낼 수 있지만 “쉽게”라고 말했습니다.)
부동 소수점 값은 매우 가깝지만 정확하지 않을 수 있습니다. 명왕성으로 이동하여 밀리미터 단위로 벗어날 수있을 정도로 가까이있을 수 있습니다. 그러나 여전히 수학적 의미에서는 정확하지 않습니다.
대략적인 것이 아니라 정확해야하는 경우 부동 소수점을 사용하지 마십시오. 예를 들어, 회계 응용 프로그램은 계정에서 특정 수의 동전을 정확하게 추적하려고합니다. 정수는 정확하기 때문에 좋습니다. 정수로 감시해야 할 주요 문제는 오버플로입니다.
기본 표현은 정수이지만 큰 표현이지만 통화에 BigDecimal을 사용하면 효과적입니다.
부동 소수점 숫자가 정확하지 않음을 인식하면 여전히 많은 용도로 사용됩니다. 그래픽 시스템의 내비게이션 또는 좌표를위한 좌표 시스템. 천문학적 가치. 과학적 가치. (어쨌든 전자 덩어리 내에서 야구 덩어리의 정확한 질량을 알 수 없으므로 부정확성은 실제로 중요하지 않습니다.)
계산 응용 프로그램 (회계 포함)의 경우 정수를 사용하십시오. 게이트를 통과하는 사람들의 수를 세려면 int 또는 long을 사용하십시오.