Search code examples
cmathprecisiontrigonometryfactorial

Problems with precision loss in my cosine function


This is my task:

Write a C function to evaluate the series // cos(x) = x-(x2 /2!)+(x4 /4!)-(x6 /6!)+... etc. Variable realNuber use radians instead of degrees

I lose precision, but I don't understand where. The answer with realNumber = 60 must be 0.500, but I've 0.501. Please help.

#include "stdio.h"
#include "inttypes.h"

double power(float N, uint32_t P){
    double buffer = 1;

    for (int i = 0; i < P; ++i) {
        buffer *= N;
    }

    return buffer;
}

float factorial(float number){
    float result = number;

    if (number == 0) {
        return 0;
    }

    for (int i = 0; i < number - 1; ++i) {
        result *= i + 1;
    }

    return result;
}

float cos(float x){
    float result = x * (3.14159265359 / 180.);
    float polar = -1;

    for (int i = 2; i < 10; i += 2) {
        result += power(result, i) / factorial(i) * polar;
        polar *= -1;
    }

    return result;
}

int main(void){
    float realNumber = 0;
    float result = 0;

    scanf("%f", &realNumber);

    result = cos(realNumber);

    printf("%.13f", result);
}

I tried making changes in function cos(); maybe the problem is in a different place?


Solution

  • You originally wrote:

    Write a C function to evaluate the series // cos(x) = x-(x2 /2!)+(x4 /4!)-(x6 /6!)

    But that is NOT the Taylor series for cos.

    The proper formula is:

    enter image description here

    (Note the 1 in the first term not an x)
    Source

    With a correction to your Taylor series, and some other fix up, I got:

    Output

    Success #stdin #stdout 0s 5392KB
    0.4999999701977
    

    My Code:

    #include "stdio.h"
    #include "inttypes.h"
    
    // No Changes
    double power(float N, uint32_t P){
        double buffer = 1;
    
        for (int i = 0; i < P; ++i) {
            buffer *= N;
        }
    
        return buffer;
    }
    
    // No Changes
    float factorial(float number){
        float result = number;
    
        if (number == 0) {
            return 0;
        }
    
        for (int i = 0; i < number - 1; ++i) {
            result *= i + 1;
        }
    
        return result;
    }
    
    // Minor changes, explained in comments
    float cos(float x){
        x = x * (3.14159265359 / 180.); // Convert Degrees to Radians
        float result = 1;               // Taylor series starts with 1, not with x !!! 
        float polar = -1;
    
        for (int i = 2; i <= 10; i += 2) {
            result += power(x, i) / factorial(i) * polar;
            polar *= -1;
        }
    
        return result;
    }
    
    // Skipped the scanf in favor of hard-coded value, for simplicity.
    int main(void){
        float realNumber = 60;
        float result = 0;
    
        result = cos(realNumber);
    
        printf("%.13f", result);
    }
    

    When I re-wrote the cos function to eliminate using power and factorial functions, I got this:

    double cos(float x){
        x = x * (3.14159265359 / 180.); // Convert Degrees to Radians
        double num   = 1;  // Numerator of the fraction (x^2, x^4...)
        int    sgn   = +1; // Sign, alternating -1, +1
        uint64_t den = 1;  // Denominator: Factorials, 2!, 4!, 6!...
        float ans    = 1;  // Accumulated answer
        for (int i = 2; i <= 10; i += 2) {
            num *= x*x;
            den *= i*i-i;
            sgn *= -1;
            ans += num / den * sgn;
        }
    
        return ans;
    }