Main Content

반올림 오차를 인식하고 방지하기

값을 수치적으로 근사할 때는 부동소수점 결과가 사용된 정밀도의 영향을 받을 수 있다는 점에 유의하십시오. 또한, 부동소수점 결과에는 반올림 오차가 발생하기 쉽습니다. 다음 접근 방식은 잘못된 결과를 인식하고 방지하는 데 도움이 될 수 있습니다.

가능한 경우 기호 계산 사용하기

정확한 기호 계산은 반올림 오차가 발생하지 않기 때문에 기호적으로 계산을 수행하는 것이 좋습니다. 예를 들어, Symbolic Math Toolbox™에서 표준 수학 상수는 고유한 기호 표현을 갖습니다.

pi
sym(pi)
ans =
    3.1416

ans =
pi

수치 근삿값을 불필요하게 사용하지 마십시오. 부동소수점 숫자는 상수를 근사하지만 그 자체가 상수이지는 않습니다. 이 근삿값을 사용하면 잘못된 결과를 얻을 수 있습니다. 예를 들어, heaviside 특수 함수는 sym(pi)의 사인과 pi의 수치 근삿값에 대한 사인에 대해 각기 다른 결과를 반환합니다.

heaviside(sin(sym(pi)))
heaviside(sin(pi))
ans =
1/2

ans =
     1

정밀도를 높여 계산 수행하기

리만 가설은 리만 제타 함수 ζ(z)의 모든 자명하지 않은 영점이 동일한 실수부 ℜ(z) = 1/2을 갖는다고 가정합니다. 제타 함수의 가능한 영점을 찾기 위해 절댓값 |ζ(1/2 + iy)|를 플로팅합니다. 다음 플롯은 제타 함수 |ζ(1/2 + iy)|의 처음 3개의 자명하지 않은 근을 보여줍니다.

syms y
fplot(abs(zeta(1/2 + i*y)), [0 30])

Figure contains an axes object. The axes object contains an object of type functionline.

수치 솔버 vpasolve를 사용하여 제타 함수의 처음 3개의 영점을 근사합니다.

vpasolve(zeta(1/2 + i*y), y, 15)
vpasolve(zeta(1/2 + i*y), y, 20)
vpasolve(zeta(1/2 + i*y), y, 25)
ans =
14.134725141734693790457251983562
 
ans =
21.022039638771554992628479593897
 
ans =
25.010857580145688763213790992563

이제, 동일한 함수를 사용하되, 실수부를 약간 늘려 보겠습니다. ζ(10000000012000000000+iy) 리만 가설에 따르면 이 함수는 모든 실수 값 y에 대해 영점을 갖지 않습니다. 유효 소수점 자릿수를 10으로 하여 vpasolve를 사용하면 솔버가 제타 함수에 대해 다음과 같은 (존재하지 않는) 영점을 구합니다.

old = digits;
digits(10)
vpasolve(zeta(1000000001/2000000000 + i*y), y, 15)
ans =
14.13472514

자릿수를 늘리면 결과가 올바르지 않다는 것을 알 수 있습니다. 제타 함수 ζ(10000000012000000000+iy)14 < y < 15에 해당하는 모든 실수 값에 대해 영점을 갖지 않습니다.

digits(15)
vpasolve(zeta(1000000001/2000000000 + i*y), y, 15)
digits(old)
ans =
14.1347251417347 + 0.000000000499989207306345i

추후 계산을 위해 디폴트 자릿수를 복원합니다.

digits(old)

기호 결과와 수치 결과 비교하기

반정수 인덱스를 갖는 베셀 함수는 정확한 기호 표현식을 반환합니다. 이러한 표현식을 부동소수점 숫자로 근사하면 매우 불안정한 결과가 생성될 수 있습니다. 예를 들어, 다음 베셀 함수에 대한 정확한 기호 표현식은 아래와 같습니다.

B = besselj(53/2, sym(pi))
B =
(351*2^(1/2)*(119409675/pi^4 - 20300/pi^2 - 315241542000/pi^6...
 + 445475704038750/pi^8 - 366812794263762000/pi^10 +...
 182947881139051297500/pi^12 - 55720697512636766610000/pi^14...
 + 10174148683695239020903125/pi^16 - 1060253389142977540073062500/pi^18...
 + 57306695683177936040949028125/pi^20 - 1331871030107060331702688875000/pi^22...
 + 8490677816932509614604641578125/pi^24 + 1))/pi^2

vpa를 사용하여 이 표현식을 10자리 정확도로 근사합니다.

vpa(B, 10)
ans =
-2854.225191

이제 부동소수점 파라미터로 베셀 함수를 호출합니다. 이러한 두 근삿값 간의 유의미한 차이는 하나 또는 두 결과 모두가 올바르지 않음을 나타냅니다.

besselj(53/2, pi)
ans =
   6.9001e-23

B에 대한 더 정확한 근삿값을 구하려면 수치 작업 정밀도를 높이십시오.

vpa(B, 50)
ans =
0.000000000000000000000069001456069172842068862232841396473796597233761161

함수 또는 표현식 플로팅하기

결과를 플로팅하면 잘못된 근삿값을 인식하는 데 도움이 될 수 있습니다. 예를 들어, 다음 베셀 함수의 수치 근삿값을 구하면 아래와 같은 결과가 반환됩니다.

B = besselj(53/2, sym(pi));
vpa(B, 10)
ans =
-2854.225191

53/2 근방의 x의 값에 대해 이 베셀 함수를 플로팅합니다. 함수 플롯은 근삿값이 올바르지 않다는 것을 보여줍니다.

syms x
fplot(besselj(x, sym(pi)), [26 27])

Figure contains an axes object. The axes object contains an object of type functionline.