Taylor serise로 sin값을 구하는 코드가 cmath의 sin과 오차가 7%정도 됩니다.
글쓴이: awidesky / 작성시간: 수, 2020/12/16 - 1:06오후
Taylor serise로 sin값을 구하는 코드를 작성했는데, cmath에 있는 sin과 relative error(fabs( 1 - (getTaylorSin(i) / sin(i)) ))를 비교해 보니 7%정도의 차이가 나네요;;
floating point의 오차일 뿐일까요...?
double getTaylorSin(const double x) { if (x < 0) return -getTaylorSin(x); if (x > doubleTwoPI) //ask JVM to do modulus with double : no problem! :D return getTaylorSin(std::fmod(x, doubleTwoPI)); //ask C++ compiler to do modulus with double : F you I refuse to calculate &$^%&#$%@$& if (x > doublePI) return (-1) * getSin(x - doublePI); if (x > doubleHalfPI) return getTaylorSin(doublePI - x); int sign = -1; double divisor = 1; int i = 1; double num = 0; double dividend = x; double result = dividend; do { dividend = dividend * x * x * sign; i += 1; divisor *= i; i += 1; divisor *= i; num = dividend / divisor; result += num; } while (num >= epsillon); return result; }
https://stackoverflow.com/questions/42217069/approximating-sinex-with-a-taylor-series-in-c-and-having-a-lot-of-problems
여기에 나오는 식을 이용했습니다. 혹시 implement 과정에서 오류가 있을지 궁금합니다!
Forums:
전체 프로그램을 다 올려주세요. 테스트해 볼 수 있게
전체 프로그램을 다 올려주세요. 테스트해 볼 수 있게.
1) doublePI를 어떻게 설정했는지, epsilon 값은 어떻게 줬는지,
2) 어떤 입력(x)을 줬는지
3) 어떤 결과가 나왔고, std::sin 결과와 어떻게 차이가 나는지
등등. 테스트하는 데 필요한 정보를 모두 올려주셔야 문제를 재현해보고 원인을 파악할 수 있겠지요.
일단 (Taylor serise를 제대로 구현했다는 가정하에) 가장 먼저 의심되는 부분은 doublePI가 충분히 정밀하지 않고, x를 너무 큰 값을 넣어준 경우입니다.
이 경우 fmod에서 오차가 발생하여 최종 연산 결과에 거의 고스란히 나타나게 됩니다.
테일러 시리즈는 무한 급수입니다.
테일러 시리즈는 무한 급수입니다.
정확한 값을 계산하는게 아니고, 그 값에 수렴해 갈 뿐입니다.
아마 epsillon 값을 작게 주면 더 정밀한 값이 나오지 않을까 합니다.
모든 코드를 첨부합니다.
우선 다양한 가능성을 짚어 주신 분들 감사드립니다.
VS 2019 상에서의 테스트 결과도 주석으로 적어놓았습니다.
흠...
흠...
1. getTaylorSin 함수에서 반복문을 나오는 조건은 absolute error에 걸려 있는데,
정작 정확도를 확인하는 방법은 relative error로군요.
아시다시피 참값의 절대값이 작을수록 absolute error 대비 relative error가 훨씬 크게 증폭됩니다.
2. 게다가 반복문을 보면 sin(x)에 들어갈 x 값이 꽤 작은 값부터 시작하는군요.
아시다피시 x이 매우 적으면 sin(x)는 거의 x입니다.
x에 epsilon을 넣고 돌리면 getTaylorSin 함수의 루프가 몇 번이나 돌겠어요?
double-pricision이라도 오차가 발생할
double-pricision이라도 오차가 발생할 수밖에 없으니 relative error로 condition을 지정해 주었다가 num이 0이 되어도 조건을 만족하지 못해 무한루프를 돌지는 않을까 걱정되어서 absolute error를 썼었습니다.
아래와 같이 num변수(serise의 각 항)이 0이 될 때까지 돌리면 되지 않을까 생각합니다.(그 이후는 의미가 없으니...)
또 답변 주신 이후 테스트를 한번 더 해봤습니다.
(공교롭게도 taylor에서 에러가 최댓값일 때(1.5708) sin 값이 거의 1이네요)
1이면 결과값의 절댓값이 작은 편은 아닌데;; 하는 의문이 듭니다.(sin의 최댓값이니까요)
답변 마지막 문단처럼 x가 매우 작을 때 에러가 크게 날 것이라고 생각했는데 이상하네요;;
여러 항의 합으로 구한 값보다 개형이 비슷한 2,3차 함수로 근사하는 것이 훨씬 정확하게 나오는 걸 보면 여러 수를 곱하고 나누는 과정에서 double의 오차가 축적되었다는 설명이 제일 논리적이지 않을까 생각이 듭니다.
double-pricision이라도 오차가 발생할
double-pricision이라도 오차가 발생할 수밖에 없으니 relative error로 condition을 지정해 주었다가 num이 0이 되어도 조건을 만족하지 못해 무한루프를 돌지는 않을까 걱정되어서 absolute error를 썼었습니다.
아래와 같이 num변수(serise의 각 항)이 0이 될 때까지 돌리면 되지 않을까 생각합니다.(그 이후는 의미가 없으니...)
또 답변 주신 이후 테스트를 한번 더 해봤습니다.
(공교롭게도 taylor에서 에러가 최댓값일 때(1.5708) sin 값이 거의 1이네요)
1이면 결과값의 절댓값이 작은 편은 아닌데;; 하는 의문이 듭니다.(sin의 최댓값이니까요)
답변 마지막 문단처럼 x가 매우 작을 때 에러가 크게 날 것이라고 생각했는데 이상하네요;;
여러 항의 합으로 구한 값보다 개형이 비슷한 2,3차 함수로 근사하는 것이 훨씬 정확하게 나오는 걸 보면 여러 수를 곱하고 나누는 과정에서 double의 오차가 축적되었다는 설명이 제일 논리적이지 않을까 생각이 듭니다.
ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ
ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ
getTaylorSin 함수의 do-while문 조건에서
num >= epsillon
=>
abs(num) >= epsillon
으로 바꿔야지요.
익히 아시겠지만 sin 함수의 Taylor series는 양수와 음수가 교대로 나타납니다.
do-while문에 들어가면 num은 곧바로 음수가 되니 do-while문은 단 한 번만 반복되는 셈이고
결국 어떤 x에 대해서건 최대 3차항까지만 계산되겠네요.
조건을
num > 0.0
으로 바꿔도 똑같은 문제입니다.아오. 이걸 돌려 보고서야 알아채다니, 저도 실력 다 죽었네요.
공부 좀 하고 살아야겠습니다.
sin 함수 구현을 했다면...
sin 함수 구현을 했다면...
c 라이브러리의 sin 함수 결과와만 비교할게 아니라...
참값! 하고도 비교를 해야하지 않나 싶습니다.
수치해석용 수학 라이브러리의 존재의 이유가 있다면 분명 차이가 있을 겁니다.
바꿨습니다 ㅋㅋㅋㅋㅋㅋㅋ
감사드립니다. double에 대한 불신이 싹 날아갔네요, 대신 제 자신에 대한 불신이 무럭무럭 자라납니다 ㅋㅋㅋㅋㅋ
참값과 비교해 보라는 주언해주신 분 또한 역시 감사드립니다. cmath 의외에도 수치해석용 라이브러리가 따로 있나 보군요?(사실 c에 대해 제가 아직 익숙치 못합니다, 저 코드도 이전에 java로 짠 걸 포팅한 거라ㅎㅎ;;)
c에도 java의 BigDecimal과 비슷한 라이브러리가 있을 테니 해봐야겠네요, 감사합니다!
댓글 달기