2010-05-27 6 views
9

그래서 모듈러스 연산자가 왜 그렇게 큰 값을 반환하는지 알아 내려고합니다.부동 소수점 연산 - 더블 형의 모듈러 연산자

나는 코드가있는 경우 : 그것은 0.09999999999999995의 결과를 줄 것이다

double result = 1.0d % 0.1d;

합니다. 나머지 0을해야한다는 것을 의미 10.0의 결과를 줄 것이다 double result = 1.0d/0.1d;

-이 문제는 분할 연산자를 사용하여 존재하지 않는 0

참고의 가치를 기대하는 것이다.

오류가 존재한다는 사실에 놀랄 일이 아닙니다. 오류가 너무 깁니다. 과 비교해 놀랍습니다. 0.0999 ~ = 0.1 및 0.1은 0.1d과 동일한 크기 순서이고 1.0d에서 멀리 떨어진 크기의 한 자리수입니다. 그게 당신이 double.epsilon과 비교할 수 없거나 "< 0.00001 차이가 있다면 그 동등 함"이라고 말할 수 있습니다.

StackOverflow에서이 주제에 관해서는 onetwothree 등의 게시물을 읽었습니다.

누구나이 오류가 왜 그렇게 큰지 설명 할 수 있습니까? 미래에 문제가 생기는 것을 피할 수있는 제안 (10 진수를 대신 사용할 수는 있지만 그 성능에 대해서는 우려하고 있습니다).

편집 : 나는 0.1이라는 숫자가 infinitely repeating series of numbers in binary이라는 것을 구체적으로 지적해야한다.

답변

14

double은 정확히 0.1을 나타낼 수 없기 때문에 오류가 발생합니다. 가장 가까운 것은 0.1000000000000005551115123126과 같습니다. 이제 1.0으로 나눌 때 10보다 약간 작은 숫자를 얻을 수 있지만 다시 두 배로 정확하게 표현할 수는 없으므로 최대 10을 올림합니다.하지만 모드를 수행하면 약간 줄 수 있습니다 0.1 미만이다.

0 = 0.1 mod 0.1이므로 mod의 실제 오류는 0.1 - 0.09999999 ... - 매우 작습니다.

% 연산자의 결과를 9 * 0.1에 더하면 다시 1.0이됩니다. 반올림 물건에

편집

좀 더 세부 정보 -이 문제가 혼합 정밀도의 위험의 좋은 예이다 특히있다.

부동 소수점 숫자의 경우 a % b은 일반적으로 a - (b * floor(a/b))과 같이 계산됩니다. 문제는 이러한 연산을 통해 얻을 수있는 것보다 더 많은 내부 정밀도로 모든 것을 한꺼번에 수행 할 수 있으며 결과를 각 단계에서 fp 번호로 반올림하므로 결과가 달라질 수 있다는 것입니다. Intel x86/x87 하드웨어는 중간 계산에 80 비트 정밀도를 사용하고 메모리 값에는 64 비트 정밀도 만 사용하는 것으로 나타났습니다. 위의 방정식에서 b의 값은 메모리에서 나오므로 정확하게 0.1이 아닌 64 비트 fp 숫자이므로 정확한 값은 dan04입니다. 따라서 1.0/0.1을 계산할 때 9.999999999999944488848768742172978818416595458984375 (반올림하여 80 비트). 이제 64 비트로 반올림하면 10.0이되지만 내부 80 비트를 유지하고 바닥을 칠하면 9.0으로 자르기 때문에 최종 답은 .09999999999999500399638918679556809365749359130859375가됩니다.

이 경우 불연속적인 스텝 기능 (바닥)을 사용하기 때문에 큰 명백한 오류가 나타납니다. 이는 내부 값의 매우 작은 차이로 인해 단계를 수행 할 수 있음을 의미합니다. 그러나 mod 자체는 예상 할 수없는 비 연속적인 계단 함수이므로 실제 함수의 오차는 0.1-0.0999 ... 0.1은 mod 함수의 범위에서 불연속 점이므로.

+0

0.1000000000000000055511151231257827021181583404541015625입니다. – dan04

+0

나는 계산을 이해한다. 한 가지 설명 - "0 = 0.1 mod 0.1 이후 mod의 실제 오류는 0.1 - 0.09999999 ... - 매우 작습니다." 내 결과가 0.099999 이었기 때문에 혼란 스럽지만 실제 결과는 0입니다. 실제 오류는 어떻게 0.1-0.099999입니까? 이 부분에 대한 오류 계산을 좀 더 자세하게 설명해 주시겠습니까? – CrimsonX

+0

은 향후 독자를 위해 이전 댓글에 대한 답변을 제공합니다. 편집 섹션이 이에 응답합니다. (짧은 설명 : 모드의 불안정성 (톱니 모양의 기능)으로 인해 오류가 큽니다. 'a'가 약간 증가하면 (예 : 10.0001) 결과가 0에 가까워집니다) –

2

계산에서 "오류"가 아니라 정확히 0.1부터 시작해야한다는 사실입니다.

이진 부동 소수점에서는 1.0을 정확하게 표현할 수 있지만 음수 2부터는 정확하게 구성 할 수 없으므로 0.1을 사용할 수 없다는 문제가 있습니다. (1/16 + 1/32 + ...)

실제로 1.0 % 0.1이 나오지 않으면 컴퓨터가 1.0 % 0.1 + 0.00 ...을 계산하도록 남겨두고 정직하게보고합니다. 결과적으로 ...

큰 나머지를 가지기 위해서, %의 두 번째 피연산자가 0.1을 약간 넘었을 것임에 틀림없지 만, 최종 나눗셈을 방지하고 결과적으로 거의 전체 0.1이 결과라고 생각합니다. 수술.

+0

을 왜 것 1.0 %, 0.1 공연이 오류 1.0/0.1 오류를 표시하지 :

당신이 수를 필요에 따라, 쓰기 좋을 것? – CrimsonX

+0

또한 나는 당신이 "기계가 1.0 % 0.0999를 계산하도록 남아있다 ..."라고 생각합니다. – CrimsonX

+0

1.0/.0999 ...와 1.0/0.1가 거의 동일하기 때문에 1.0/0.1가 나오는 이유는 바로 기능이 연속적입니다. 계산은 약간의 추가 비트로 완료되며 최종 표현 가능한 결과는 두 경우 모두 동일합니다. 그런 다음 1.0/exactly_0.1에 대한 대답이 10이 될 것으로 예상하여 "올바르게"보인다. 그러나 기계에 표시된 문제는 1.0 % 0.1이 아니라 1.0 % 0.10000000001이거나이 계산이 실제로 둥근 수없는 큰 나머지. – DigitalRoss

2

0.1을 정확히 2 진수로 표현할 수 없다는 사실은이 문제와 관련이 있습니다.

0.1을으로 나타낼 수있는 경우 계산할 연산의 실제 결과에 가장 가까운 이중 가장 가까운 값 ("가장 가까운"반올림 모드라고 가정)을 얻게됩니다.

계산할 수 없으므로 계산하려는 것과 가장 다른 연산에 가장 가까운 표현 가능한 이중을 얻게됩니다.

또한 /는 대부분 연속 함수입니다 (인수에 대한 작은 차이는 일반적으로 결과에 작은 차이가 있음을 의미하며 파생 값은 0 근처에서 크지 만 동일한 측면에서는 0 이상에 대해 인수가 도움이된다). 반면에 %는 연속적이지 않습니다. 어떤 정밀도를 선택하든, 첫 번째 인수에서 임의로 작은 표현 오류가 결과에 큰 오류를 의미하는 인수가 항상있을 것입니다.

IEEE 754가 지정된 방식으로 부동 소수점 연산의 근사값에 대해서만 보장됩니다. 인수는 인수가 정확히 원하는 것으로 가정합니다. 인수가 원하는 것과 정확히 일치하지 않으면 인터벌 산술이나 프로그램의 적절한 조건 분석과 같은 다른 솔루션으로 전환해야합니다 (부동 소수점 숫자에 %를 사용하는 경우 잘되지 않을 수 있습니다 - 조건부).

0

표시되는 오류는 작습니다. 그것은 언뜻 보면 커 보인다. % 0.1 수술에서 0이 예상되면 귀하의 결과 (디스플레이 반올림 후)는 0.09999999999999995 == (0.1 - 5e-17)입니다. 그러나 이것은 거의 0.1이며, 0.1 % 0.1 == 0이라는 것을 기억하십시오.

실제 오류는 -5e-17입니다. 나는 이것을 작게 부른다.

double result = 1.0 % 0.1; result = result >= 0.1/2 ? result - 0.1 : result;