I've been working on a program that calculates sin(x), cos(x), and exp(x) without using math.h and compares them to the library values of their functions. I've been forbidden from actually using the basic power(x, n) and fact(n) functions. The only hint is that I have to do division before doing multiplication when combining the functions into one.
double power(double x, int n)
{
int i;
double prod=1.;
for(i=0;i<n;i++){
prod = prod*x;
}
return prod;
}
double fact(int n)
{
int i;
double prod=1.;
for(i=1;i<=n;i++) {
prod = prod*i;
}
return prod;
}
My idea is to somehow nest the for-loops together, and piecemeal the Taylor Expansion formula for each iteration of the loop, but I haven't had luck actually combining the two.
Any help or hint would be appreciated on how to combine these.
The other aspect of the program that confuses me is that there can only be a single input of X per iteration of the program, and therefore no dynamically defined 'n' for the loops.
Use Taylor series for the exponential:
e^x = 1 + x/1! + x^2/2! + x^3/3!...
and using Euler after that you can calculate sinx and cosx.
The trick is to look at the changes between each successive term in the Taylor series expansion. Let's start with ex:
e^x = 1 + x + x^2/2! + x^3/3! + x^4/4! + x^5/5! ...
Notice that each term is x / n times the prior term, where n is the term number. So start with a term of 1, then multiply by the above expression to get the next term.
That gives you the following implementation:
double etox(double x)
{
long double sum = 0;
// term starts at 1
long double term = 1;
// term number
int i = 1;
// continue until the term is below the precision of the current sum
while (sum + term != sum) {
sum += term;
// new term is x/i times the prior term, where i is the term number
term *= (long double)x / i;
i++;
}
return sum;
}
Note that with this implementation, you'll get some degree of error in the least significant digits. If you start adding from a higher term number and work your way back, this can be avoided.
Similarly for sin(x) and cos(x):
sin(x) = x - x^3/3! + x^5/5! - x^7/7! + x^9/9! ...
cos(x) = 1 - x^2/2! + x^4/4! - x^6/6! + x^8/8! ...
Each term is - (x*x) / ((2*n)*((2*n)-1)) times the prior term, where n is the term number.
I'll leave the the implementation of these two as an exercise for the reader.
Part of this comes from my answer for doing this in MIPS assembly: Taylor Series in MIPS assembly
You can do Taylor series on the fly without having to call sub-functions. In the series, each term can be calculated from the previous term in a loop. (i.e. no need to call fact and/or pow repeatedly, where each starts from the beginning). See https://en.wikipedia.org/wiki/Taylor_series
Anyway, here's code for sin and cos:
// mipstaylor/mipstaylor -- fast sine/cosine calculation
#include <stdio.h>
#include <math.h>
#define ITERMAX 10
// qcos -- calculate cosine
double
qcos(double x)
{
int iteridx;
double x2;
double cur;
int neg;
double xpow;
double n2m1;
double nfac;
double sum;
// square of x
x2 = x * x;
// values for initial terms where n==0:
xpow = 1.0;
n2m1 = 0.0;
nfac = 1.0;
neg = 1;
sum = 0.0;
iteridx = 0;
// NOTES:
// (1) with the setup above, we can just use the loop without any special
// casing
while (1) {
// calculate current value
cur = xpow / nfac;
// apply it to sum
if (neg < 0)
sum -= cur;
else
sum += cur;
// bug out when done
if (++iteridx >= ITERMAX)
break;
// now calculate intermediate values for _next_ sum term
// get _next_ power term
xpow *= x2;
// go from factorial(2n) to factorial(2n+1)
n2m1 += 1.0;
nfac *= n2m1;
// now get factorial(2n+1+1)
n2m1 += 1.0;
nfac *= n2m1;
// flip sign
neg = -neg;
}
return sum;
}
// qsin -- calculate sine
double
qsin(double x)
{
int iteridx;
double x2;
double cur;
int neg;
double xpow;
double n2m1;
double nfac;
double sum;
// square of x
x2 = x * x;
// values for initial terms where n==0:
xpow = x;
n2m1 = 1.0;
nfac = 1.0;
neg = 1;
sum = 0.0;
iteridx = 0;
// NOTES:
// (1) with the setup above, we can just use the loop without any special
// casing
while (1) {
// calculate current value
cur = xpow / nfac;
// apply it to sum
if (neg < 0)
sum -= cur;
else
sum += cur;
// bug out when done
if (++iteridx >= ITERMAX)
break;
// now calculate intermediate values for _next_ sum term
// get _next_ power term
xpow *= x2;
// go from factorial(2n+1) to factorial(2n+1+1)
n2m1 += 1.0;
nfac *= n2m1;
// now get factorial(2n+1+1+1)
n2m1 += 1.0;
nfac *= n2m1;
// flip sign
neg = -neg;
}
return sum;
}
// testfnc -- test function
void
testfnc(int typ,const char *sym)
{
double (*efnc)(double);
double (*qfnc)(double);
double vale;
double valq;
double x;
double dif;
int iter;
switch (typ) {
case 0:
efnc = cos;
qfnc = qcos;
break;
case 1:
efnc = sin;
qfnc = qsin;
break;
default:
efnc = NULL;
qfnc = NULL;
break;
}
iter = 0;
for (x = 0.0; x <= M_PI_2; x += 0.001, ++iter) {
vale = efnc(x);
valq = qfnc(x);
dif = vale - valq;
dif = fabs(dif);
printf("%s: %d x=%.15f e=%.15f q=%.15f dif=%.15f %s\n",
sym,iter,x,vale,valq,dif,(dif < 1e-14) ? "PASS" : "FAIL");
}
}
// main -- main program
int
main(int argc,char **argv)
{
testfnc(0,"cos");
testfnc(1,"sin");
return 0;
}
Related
I am making simple calculator and it is e^x function part.
it works for positive number, but it doesn't for negative x.
How can I make it works for negative x too?`
double calculateEx(double x) {
double beforeResult = 1, afterResult = 1, term = 1, error = 1, i = 1, j;
while (error > 0.001) {
afterResult = beforeResult;
for (j = 1; j <= i; j++) {
term *= x;
}
term /= fact(i);
afterResult += term;
error = (afterResult - beforeResult) / afterResult;
if (error < 0) error * -1;
error *= 100;
beforeResult = afterResult;
term = 1;
i++;
}
return beforeResult;
}
double fact (double num) {
int i, j;
double total = 1;
for (i = 2; i <= num; i++) {
total = total * i;
}
return total;
}
When computing exponent via Taylor serie
exp(x) = 1 + x / 1 + x**2/2! + ... + x**n/n!
you don't want any factorials, please, notice that if n-1th term is
t(n-1) = x**(n-1)/(n-1)!
then
t(n) = x**n/n! = t(n-1) * x / n;
That's why all you have to implement is:
double calculateEx(double x) {
double term = 1.0;
double result = term;
/*
the only trick is that term can be positive as well as negative;
we should either use abs in any implementation or putr two conditions
*/
for (int n = 1; term > 0.001 || term < -0.001; ++n) {
term = term * x / n;
result += term;
}
return result;
}
OK, as I wrote in a comment above, I'd use <math.h> if at all possible, but since you asked the question:
To make it work with negative numbers, if x is negative, consider what happens if you negate it.
You can get rid of the factorial function by storing a table of factorials. You won't need that many elements.
I've been following the guide my prof gave us, but I just can't find where I went wrong. I've also been going through some other questions about implementing the Taylor Series in C.
Just assume that RaiseTo(raise a number to the power of x) is there.
double factorial (int n)
{
int fact = 1,
flag;
for (flag = 1; flag <= n; flag++)
{
fact *= flag;
}
return flag;
}
double sine (double rad)
{
int flag_2,
plusOrMinus2 = 0; //1 for plus, 0 for minus
double sin,
val2 = rad,
radRaisedToX2,
terms;
terms = NUMBER_OF_TERMS; //10 terms
for (flag_2 = 1; flag_2 <= 2 * terms; flag_2 += 2)
{
radRaisedToX2 = RaiseTo(rad, flag_2);
if (plusOrMinus2 == 0)
{
val2 -= radRaisedToX2/factorial(flag_2);
plusOrMinus2++; //Add the next number
}
else
{
val2 += radRaisedToX2/factorial(flag_2);
plusOrMinus2--; //Subtract the next number
}
}
sin = val2;
return sin;
}
int main()
{
int degree;
scanf("%d", °ree);
double rad, cosx, sinx;
rad = degree * PI / 180.00;
//cosx = cosine (rad);
sinx = sine (rad);
printf("%lf \n%lf", rad, sinx);
}
So during the loop, I get the rad^x, divide it by the factorial of the odd number series starting from 1, then add or subtract it depending on what's needed, but when I run the program, I get outputs way above one, and we all know that the limits of sin(x) are 1 and -1, I'd really like to know where I went wrong so I could improve, sorry if it's a pretty bad question.
Anything over 12! is larger than can fit into a 32-bit int, so such values will overflow and therefore won't return what you expect.
Instead of computing the full factorial each time, take a look at each term in the sequence relative to the previous one. For any given term, the next one is -((x*x)/(flag_2*(flag_2-1)) times the previous one. So start with a term of x, then multiply by that factor for each successive term.
There's also a trick to calculating the result to the precision of a double without knowing how many terms you need. I'll leave that as an exercise to the reader.
In the function factorial you are doing an int multiply before assigned to the double return value of the function. Factorials can easily break the int range, such as 20! = 2432902008176640000.
You also returned the wrong variable - the loop counter!
Please change the local variable to double, as
double factorial (int n)
{
double fact = 1;
int flag;
for (flag = 1; flag <= n; flag++)
{
fact *= flag;
}
return fact; // it was the wrong variable, and wrong type
}
Also there is not even any need for a factorial calculation. Note that each term of the series multiplies the previous term by rad and divides by the term number - with a change of sign.
Another fairly naive, 5-minute approach involves computing a look-up table that contains the first 20 or so factorials, i.e 1! .. 20! This requires very little memory and can increase speed over the 'each-time' computation method. A further optimization can easily be realized in the function that pre-computes the factorials, taking advantage of the relationship each has to the previous one.
An approach that efficiently eliminated branching (if X do Y else do Z) in the loops of the two trig functions would provide yet more speed again.
C code
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
const int nMaxTerms=20;
double factorials[nMaxTerms];
double factorial(int n)
{
if (n==1)
return 1;
else
return (double)n * factorial(n - 1.0);
}
void precalcFactorials()
{
for (int i=1; i<nMaxTerms+1; i++)
{
factorials[i-1] = factorial(i);
}
}
/*
sin(x) = x - (x^3)/3! + (x^5)/5! - (x^7)/7! .......
*/
double taylorSine(double rads)
{
double result = rads;
for (int curTerm=1; curTerm<=(nMaxTerms/2)-1; curTerm++)
{
double curTermValue = pow(rads, (curTerm*2)+1);
curTermValue /= factorials[ curTerm*2 ];
if (curTerm & 0x01)
result -= curTermValue;
else
result += curTermValue;
}
return result;
}
/*
cos(x) = 1 - (x^2)/2! + (x^4)/4! - (x^6)/6! .......
*/
double taylorCos(double rads)
{
double result = 1.0;
for (int curTerm=1; curTerm<=(nMaxTerms/2)-1; curTerm++)
{
double curTermValue = pow(rads, (curTerm*2) );
curTermValue /= factorials[ (curTerm*2) - 1 ];
if (curTerm & 0x01)
result -= curTermValue;
else
result += curTermValue;
}
return result;
}
int main()
{
precalcFactorials();
printf("Math sin(0.5) = %f\n", sin(0.5));
printf("taylorSin(0.5) = %f\n", taylorSine(0.5));
printf("Math cos(0.5) = %f\n", cos(0.5));
printf("taylorCos(0.5) = %f\n", taylorCos(0.5));
return 0;
}
output
Math sin(0.5) = 0.479426
taylorSin(0.5) = 0.479426
Math cos(0.5) = 0.877583
taylorCos(0.5) = 0.877583
Javascript
Implemented in javascript, the code produces seemingly identical results (I didn't test very much) to the inbuilt Math library when summing just 7 terms in the sin/cos functions.
window.addEventListener('load', onDocLoaded, false);
function onDocLoaded(evt)
{
console.log('starting');
for (var i=1; i<21; i++)
factorials[i-1] = factorial(i);
console.log('calculated');
console.log(" Math.cos(0.5) = " + Math.cos(0.5));
console.log("taylorCos(0.5) = " + taylorCos(0.5));
console.log('-');
console.log(" Math.sin(0.5) = " + Math.sin(0.5));
console.log("taylorSine(0.5) = " + taylorSine(0.5));
}
var factorials = [];
function factorial(n)
{
if (n==1)
return 1;
else
return n * factorial(n-1);
}
/*
sin(x) = x - (x^3)/3! + (x^5)/5! - (x^7)/7! .......
*/
function taylorSine(x)
{
var result = x;
for (var curTerm=1; curTerm<=7; curTerm++)
{
var curTermValue = Math.pow(x, (curTerm*2)+1);
curTermValue /= factorials[ curTerm*2 ];
if (curTerm & 0x01)
result -= curTermValue;
else
result += curTermValue;
}
return result;
}
/*
cos(x) = 1 - (x^2)/2! + (x^4)/4! - (x^6)/6! .......
*/
function taylorCos(x)
{
var result = 1.0;
for (var curTerm=1; curTerm<=7; curTerm++)
{
var curTermValue = Math.pow(x, (curTerm*2));
curTermValue /= factorials[ (curTerm*2)-1 ];
if (curTerm & 0x01)
result -= curTermValue;
else
result += curTermValue;
}
return result;
}
I have a logical problem in my code, maybe it is caused by overflowing but I can't solve this on my own, so I would be thankful if anyone can help me.
In the following piece of code, I have implemented the function taylor_log(), which can count "n" iterations of taylor polynomial. In the void function I am looking for number of iterations (*limit) which is enough to count a logarithm with desired accuracy compared to log function from .
The thing is that sometimes UINT_MAX is not enough iterations to get the desired accuracy and at this point I want to let the user know that the number of needed iterations is higher than UINT_MAX. But my code don't work, for example for x = 1e+280, eps = 623. It just counts, counts and never give result.
TaylorPolynomial
double taylor_log(double x, unsigned int n){
double f_sum = 1.0;
double sum = 0.0;
for (unsigned int i = 1; i <= n; i++)
{
f_sum *= (x - 1) / x;
sum += f_sum / i;
}
return sum;
}
void guessIt(double x, double eps, unsigned int *limit){
*limit = 10;
double real_log = log(x);
double t_log = taylor_log(x, *limit);
while(myabs(real_log - t_log) > eps)
{
if (*limit == UINT_MAX)
{
*limit = 0;
break;
}
if (*limit >= UINT_MAX/2)
{
*limit = UINT_MAX;
t_log = taylor_log(x, *limit);
}
else
{
*limit = (*limit) *2;
t_log = taylor_log(x, *limit);
}
}
}
EDIT: Ok guys, thanks for your reactions so far. I have changed my code to this:
if (*limit == UINT_MAX-1)
{
*limit = 0;
break;
}
if (*limit >= UINT_MAX/2)
{
*limit = UINT_MAX-1;
t_log = taylor_log(x, *limit);
}
but it still doesn't work correctly, I have set printf to the beggining of taylor_log() function to see the value of "n" and its (..., 671088640, 1342177280, 2684354560, 5, 4, 3, 2, 2, 1, 2013265920, ...). Don't understand it..
This code below assigns the limit to UINT_MAX
if (*limit >= UINT_MAX/2)
{
*limit = UINT_MAX;
t_log = taylor_log(x, *limit);
}
And your for loop is defined like this:
for (unsigned int i = 1; i <= n; i++)
i will ALWAYS be less than or equal to UINT_MAX because there is never going to be a value of i that is greater than UINT_MAX. Because that's the largest value i could ever be. So there is certainly overflow and your loop exit condition is never met. i rolls over to zero and the process repeats indefinitely.
You should change your loop condition to i < n or change your limit to UINT_MAX - 1.
[Edit]
OP coded correctly but must insure a limited range (0.5 < x < 2.0 ?)
Below is a code version that self determines when to stop. Iteration count goes high near x near 0.5 and 2.0. The iteration count needed goes into the millions. Such the alternative coded far below.
double taylor_logA(double x) {
double f_sum = 1.0;
double sum = 0.0;
for (unsigned int i = 1; ; i++) {
f_sum *= (x - 1) / x;
double sum_before = sum;
sum += f_sum / i;
if (sum_before == sum) {
printf("%d\n", i);
break;
}
}
return sum;
}
Wrongalternative implementation of the series: Ref
Sample alternative - it converges faster.
double taylor_log2(double x, unsigned int n) {
double f_sum = 1.0;
double sum = 0.0;
for (unsigned int i = 1; i <= n; i++) {
f_sum *= (x - 1) / 1; // / 1 (or remove)
if (i & 1) sum += f_sum / i;
else sum -= f_sum / i; // subtract even terms
}
return sum;
}
A reasonable number of terms will converge as needed.
Alternatively, continue until terms are too small (maybe 50 or so)
double taylor_log3(double x) {
double f_sum = 1.0;
double sum = 0.0;
for (unsigned int i = 1; ; i++) {
double sum_before = sum;
f_sum *= x - 1;
if (i & 1) sum += f_sum / i;
else sum -= f_sum / i;
if (sum_before == sum) {
printf("%d\n", i);
break;
}
}
return sum;
}
Other improvements possible. example see More efficient series
First, using std::numeric_limits<unsigned int>::max() will make your code more c++-ish than c-ish. Second, you can use the integral type unsigned long long and std::numeric_limits<unsigned long long>::max() for the limit, which is pretty mush the limit for an integral type. If you want a higher limit, you may use long double. floating points also allows you to use infinity with std::numeric_limits<double>::infinity() note that infinity work with double, float and long double.
If neither of these types provide you the precision you need, look at boost::multiprecision
First of all, the Taylor series for the logarithm function only converges for values of 0 < x < 2, so it's quite possible that the eps precision is never hit.
Secondly, are you sure that it loops forever, instead of hitting the *limit >= UINT_MAX/2 after a very long time?
OP is using the series well outside its usable range of 0.5 x < 2.0 with calls like taylor_log(1e280, n)
Even within the range, x values near the limits of 0.5 and 2.0 converge very slowly needing millions+ of iterations. A precise log() will not result. Best to use the 2x range about 1.0.
Create a wrapper function to call the original function in its sweet range of sqrt(2)/2 < x < sqrt(2). Converges, worst case, with about 40 iterations.
#define SQRT_0_5 0.70710678118654752440084436210485
#define LN2 0.69314718055994530941723212145818
// Valid over the range (0...DBL_MAX]
double taylor_logB(double x, unsigned int n) {
int expo;
double signif = frexp(x, &expo);
if (signif < SQRT_0_5) {
signif *= 2;
expo--;
}
double y = taylor_log(signif,n);
y += expo*LN2;
return y;
}
It took me a while conceptual to grasp how to code a loop that would calculate a given series in which a factorial was used.
I coded it--then my teacher told us we had to use a single for loop. I can't seem to grasp how to do something like this. It doesn't make sense how you'd keep the running total of the products across several numbers.
Here is my code; which includes a nested for loop. I really appreciate any and all help.
int main() {
/*init variables*/
int N; //number of terms
float NUMER, DENOM = 1;
float FRAC, sum = 0, x;
/*asks user for value of N*/
printf("Input number of terms: ");
scanf("%i", &N);
/*asks user for value of x*/
printf("Input value of x: ");
scanf("%f", &x);
for (int n = 0; n <= N; n++) {
NUMER = (pow(x, n)); //calculates numerator
for (int fac = 1; fac <= n; fac++) { //calculates factorial using for loop
DENOM = n * fac;
}
if (DENOM <= 0)
printf("\n\nError, dividing by zero.\n\n"); //this is for debugging purposes; disregard
FRAC = NUMER / DENOM; //calculates fraction
sum += FRAC; //running sum of series
}
printf("\nSum of the series is %.1f\n\n", sum); //prints sum of series
return 0;
You want DENOM = n!, so you can just start with DENOM = 1
and update the value inside the loop:
DENOM = 1;
for (int n = 0; n <= N; n++) {
NUMER = (pow(x, n)); //calculates numerator
FRAC = NUMER / DENOM; //calculates fraction
sum += FRAC; //running sum of series
DENOM *= n+1;
}
Instead of computing x^n and n! each time through the outer loop, you can initialize
the quotient to 1.0 before the outer loop, then on each pass through the outer loop,
multiply by x/n to get the next term in the series. This will avoid the need
to call pow(x,n), and use an inner loop to calculate the factorial, each pass through
the outer loop.
If you think about what you would do if calculating a factorial by hand, I think you can figure out how to code this pretty easily.
Lets say you are trying to calculate 11!. Well, you would start at 11, and them multiply by 10. Now you have 110. Now multiply by 9. You have 990. Now multiply by 8...
As you can see, the 11, 10, 9, 8... series is what your for loop is going to be. Just keep your 'current answer' in a variable and keep multiplying it by the number provided by your for loop.
That seems...complicated. Terseness is or can be your friend :D
I don't think it needs to be much more complicated than:
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
int main( int argc, char* argv[] )
{
double limit = 10 ; // how far do we want to go?
double x = 2 ; // some value for X
double xn = 1 ; // by definition, for all X, X^0 is 1
double nf = 1 ; // by convention, 0! is 1
double value = 0 ;
double sum = 0 ;
double n = 0 ;
while ( n < limit )
{
value = xn / nf ; // compute the next element of the series
sum += value ; // add that to the accumulator
xn *= x ; // compute the *next* value for X^n
nf *= (++n) ; // compute the *next* value for N!
}
return 0;
}
You get a more stable answer working the loop in reverse. Many infinite sums numerically come out better summing the smallest terms together first.
f(x,n) = x^0/0! + x^1/1! + x^2/2! + ... + x^n/n!
Let the sum be S(x,n) = x/n
Let the sum of the 2 last terms be S(x,n-1) = x/(n-1) + x/(n-1)*S(x,n)
Let the sum of the 3 last terms be S(x,n-2) = x/(n-2) + x/(n-2)*S(x,n-1)
...
Let the sum of the N last terms be S(x,1) = x/(1) + x/(1)*S(x,1)
double e(double x, unsigned n) {
double sum = 0.0;
while (n > 0) {
sum = x*(1 + sum)/n;
n--;
}
sum += 1.0; // The zero term
return sum;
}
Notice that even if n is large like 1000, and the mathematical answer < DBL_MAX, this loop does not run into floating point overflow so easily.
[edit] But if code must be done in a forward loop, the below calculates each term not as separate products that may overflow, but a unified computation.
double e_forward(double x, unsigned n) {
double sum = 1.0;
double term = 1.0;
for (unsigned i = 1; i <= n; i++) {
term *= x / i;
sum += term;
}
return sum;
}
I created this function CalculateCos:
int Factorial (long int n)
{
long int r = 1;
for (int i = 2; i<=n; i++)
{
r = r*i;
}
return r;
}
float CalculateVariable(int CVnumber, int CVloopCounter)
{
float CVresult = 0;
CVresult = pow(CVnumber, (CVloopCounter*2)) / (long int)Factorial(CVnumber*2);
return CVresult;
}
float CalculateCos(int number)
{
float result = 1;
int loopCounter = 1;
int minusOrPlus = 1;
while(loopCounter <= precision && loopCounter <= 8)
{
if(!minusOrPlus)
{
result = result - CalculateVariable(number, loopCounter);
printf("%f\n", result);
minusOrPlus = 1;
}
else
{
result = result + CalculateVariable(number, loopCounter);
printf("%f\n", result);
minusOrPlus = 0;
}
loopCounter++;
}
return result;
}
The reason why I printf after the subtraction or adding, is because it gives me strange output, like:
Enter a number, for the cos function
6
1.000000
0.999997
1.000095
0.996588
1.122822
-3.421593
160.177368
-5729.385254
Result is: -5729.3852539
Official function result is: 0.9601703
Can you help me to get correct results on this?
UPDATE:
Now my solution is:
float CalculateCos(float number)
{
float result = 0;
float step = 1;
int loopCounter = 1;
while(loopCounter <= 5)
{
step = step * (-number) * number / (((2*loopCounter)-1)*((2*loopCounter)-2));
result += step;
loopCounter++;
}
return result;
}
Current problem:
since your Factorial function returns int and you casts it to long int, its result is going to overflow even before the input goes to 16 in your case (14! > max_int).
You're calculating cos using Taylor series:
cos(x) = 1 - x2/2! + x4/4! - x6/6!
+ ...
I'm not going to write code. But there are some things wrong in your program, which can be fixed easily:
The input is in radian, so number should be a float.
Calculating each step of Taylor series using exponentiation and factorial separately leads to overflow very soon. The correct way is maintaining a float variable: step = 1 at first and in kth loop iteration step = step * (- x) * x / ((2*k-1)*(2*k)). In this way, you simply add step to result in the loop and don't need minusOrPlus anymore.
The number of loop iterations is bounded by 8 which is too small, so the result could be not precise enough.
I don't see you use precision variable anywhere. It could be used to check precision of the result. For example, when abs(step) < precision, we're going to terminate the loop.