0.1을 여러 번 더하는 것은 왜 무손실인 채로 남습니까?
나는 알고 있다0.1
10진수는 유한한 이진수(환산)로 정확하게 나타낼 수 없습니다.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비트 제한에 맞추려면 비트는 1을 아래로 이동해야 하며 오류는 폐기됩니다.에러가 다시 슬금슬금 다시 발생하다0.6
그리고.0.7
이 없다면0.8
로.1.0
에러는 파기됩니다.
5회 더하면 취소가 아니라 오류가 누적됩니다.
에러가 발생하는 이유는 53비트 등 정밀도가 한정되어 있기 때문입니다.즉, 숫자가 커질수록 더 많은 비트를 사용하므로 비트를 끝에서 삭제해야 합니다.이로 인해 반올림이 발생하며, 이 경우 귀하에게 유리합니다.
예를 들어, 작은 숫자를 얻으면 반대의 효과를 얻을 수 있습니다. 0.1-0.0999
=>1.0000000000000286E-4
이전보다 에러가 많아졌습니다.
Java 6에서 Math.round(0.499999999999994)가 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
따라서 한 번의 반올림만 이루어진 결과입니다.
두 번째 결과는 더 어렵습니다.여기서 그 데모를 설명하겠습니다(Stephen Canon은 마지막 3자리 숫자에 대한 사례 분석을 통해 또 다른 증거를 제시합니다).x
정리하면, 3* 중 하나입니다.x
2*와 같은 binade에 있습니다.x
또는 4*와 같은 binade에 있습니다.x
두 가지 경우 모두 세 번째 추가 시 오류가 두 번째 추가 시 오류를 취소하는 것으로 추론할 수 있습니다(첫 번째 추가는 이미 설명한 바와 같이 정확합니다).
세 번째 결과, "x + x + x + x + x
정확하게 반올림됨"은 첫 번째가 정확성으로부터 파생되는 것과 같은 방법으로 두 번째에서 파생된다.x + x
.
두 번째 결과는 그 이유를 설명한다.0.1 + 0.1 + 0.1 + 0.1
정확히 부동소수점 숫자입니다.0.4
: 유리수 1/10과 4/10은 부동소수로 변환될 때 같은 상대 오차로 근사됩니다.이들 부동소수점 숫자의 비율은 정확히 4입니다.첫 번째와 세 번째 결과에 따르면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 * 0.1
n/10에서 더 멀리 떨어져 있습니다.n의 함수로 "0.1 added to self n times"의 값을 플롯하면, 2진법별로 일정한 기울기의 선을 관찰할 수 있다(n번째 덧셈의 결과가 특정 2진법에 들어가는 즉시, 그 덧셈의 속성은 같은 2진법으로 산출된 이전의 덧셈과 유사할 것으로 예상할 수 있다).e) 같은 binade 내에서 에러는 증가하거나 축소됩니다.바이네이드에서 바이네이드까지의 슬로프의 순서를 보면, 다음의 반복 자릿수를 알 수 있습니다.0.1
잠시동안 바이너리로.그 후 흡수가 시작되고 곡선이 평평해집니다.
부동소수점 시스템은 반올림을 위한 약간의 추가 정밀도를 갖는 등 다양한 마법을 부립니다.따라서 0.1의 부정확한 표현으로 인한 아주 작은 오차는 결국 0.5로 반올림됩니다.
부동 소수점은 숫자를 표현하는 훌륭한 방법이지만 INCRACT라고 생각하십시오.모든 가능한 숫자가 컴퓨터에 쉽게 표현되는 것은 아니다.PI 같은 무리수.또는 SQRT(2)처럼요. (기호 수학 시스템은 그것들을 나타낼 수 있지만, 저는 "간단하게"라고 말했습니다.)
The floating point value may be extremely close, but not exact. It may be so close that you could navigate to Pluto and be off by millimeters. But still not exact in a mathematical sense.
Don't use floating point when you need to be exact rather than approximate. For example, accounting applications want to keep exact track of a certain number of pennies in an account. Integers are good for that because they are exact. The primary issue you need to watch for with integers is overflow.
Using BigDecimal for currency works well because the underlying representation is an integer, albeit a big one.
Recognizing that floating point numbers are inexact, they still have a great many uses. Coordinate systems for navigation or coordinates in graphics systems. Astronomical values. Scientific values. (You probably cannot know the exact mass of a baseball to within a mass of an electron anyway, so inexactness doesn't really matter.)
For counting applications (including accounting) use integer. For counting the number of people that pass through a gate, use int or long.
ReferenceURL : https://stackoverflow.com/questions/26120311/why-does-adding-0-1-multiple-times-remain-lossless
'programing' 카테고리의 다른 글
C의 보이드 포인터에 대한 포인터 산술 (0) | 2022.08.28 |
---|---|
정적 필드와 함께 @Autowired를 사용할 수 있습니까? (0) | 2022.08.28 |
오른쪽 정렬 버튼 그룹 Vuetify (0) | 2022.08.28 |
Vue 어레이에서 테이블로의 문제 (0) | 2022.08.28 |
vuex 지속 상태가 전체적으로 변환으로 업데이트되지 않음 (0) | 2022.08.28 |