-1

I have written the following function for the Taylor series to calculate cosine.

double cosine(int x) {
    x %= 360; // make it less than 360
    double rad = x * (PI / 180);
    double cos = 0;

    int n;

    for(n = 0; n < TERMS; n++) { 
        cos += pow(-1, n) * pow(rad, 2 * n) / fact(2 * n);
    }
    return cos;
}

My issue is that when i input 90 i get the answer -0.000000. (why am i getting -0.000 instead of 0.000?)

Can anybody explain why and how i can solve this issue?
I think it's due to the precision of double. Here is the main() :

int main(void){
int y;
//scanf("%d",&y);
y=90;
printf("sine(%d)= %lf\n",y, sine(y));
printf("cosine(%d)= %lf\n",y, cosine(y));

return 0;
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
FNF
  • 47
  • 7
  • 3
    One likely culprit is the printing routine, which you don't show. And don't make a variable named `cos` when including math.h. – Lundin Oct 07 '19 at 13:31
  • What do you expect instead? – klutt Oct 07 '19 at 13:35
  • 3
    I'm not sure what the issue is you're inquiring about. The value of `cos(90)` is 0. `-0.000000` is essentially 0. BTW, there's a much more efficient way to calculate successive terms. Each term is just `-(rad * rad)/(2*n*(2*n-1))` multiplied by the previous term. So save the term value in each loop iteration for this purpose. – lurker Oct 07 '19 at 13:35
  • 2
    Use `%g` instead of `%f`, you will see to which value tends your result – Mathieu Oct 07 '19 at 13:36
  • 2
    Since `cos(90 + a little bit)` is `0 - a little bit`, this sort of thing is likely. Your code is (probably) working fine. "Never compare floating-point numbers for equality." -0 is close enough to 0 for all practical purposes. – Steve Summit Oct 07 '19 at 13:41
  • Why are you taking an `int` as argument? – klutt Oct 07 '19 at 13:47
  • Lundin, the printing routine has been added – FNF Oct 07 '19 at 13:53
  • 1
    Lurker, why am i getting -0.000 instead of 0.000? – FNF Oct 07 '19 at 13:53
  • 1
    An exact value of pi can't be represented as a `double`, hence your `rad` value isn't exactly 90 degrees (pi / 2), hence the Taylor series won't converge exactly on 0. Even if it would, there's no guarantee the number of terms you chose will make it tend to positive or negative zero. tldr : don't expect to get an exact value `0.0` out of this code - [floating point just doesn't work that way](https://stackoverflow.com/questions/588004/is-floating-point-math-broken). – Sander De Dycker Oct 07 '19 at 14:01
  • Can you provide your values of PI and TERM ? Also, Taylor series estimate a proximate value, which could be slightly ABOVE or BELOW "real" answer (0.0 in thise case). The '-0.000000' indicates that the estimate number is between -1e-9 and (approximately) -1e-300. – dash-o Oct 07 '19 at 14:07
  • @dash-o: “-0.000000” does not indicate the number is between -1e-9 and 1e-300. It could be exactly zero. – Eric Postpischil Oct 07 '19 at 14:39
  • 1
    FNF, Use `printf("cosine(%d)= %lg\n",y, cosine(y));` (g vs f) and report your output. – chux - Reinstate Monica Oct 07 '19 at 14:50
  • @EricPostpischil, while we do not know what PI and TERMS are using. I've tested with standard M_PI, and terms 1 to 20. Th eoutput for TERMS=10 is-3.376e-13, which will display as '-0.000' when truncated by printf to 9 places. Using '%g' will show the 'real value'. – dash-o Oct 07 '19 at 15:06
  • 1
    @dash-o: Nonetheless, an output of “-0.000000” does not indicate the number is between −1e−9 and −1e−300. `printf("%f", -0.);` will print “-0.000000”. – Eric Postpischil Oct 07 '19 at 15:23

2 Answers2

3

It's totally expected that you will not be able to get exact zero outputs for cosine of anything with floating point, regardless of how good your approach to computing it is. This is fundamental to how floating point works.

The mathematical zeros of cosine are odd multiples of pi/2. Because pi is irrational, it's not exactly representable as a double (or any floating point form), and the difference between the nearest neighboring values that are representable is going to be at least pi/2 times DBL_EPSILON, roughly 3e-16 (or corresponding values for other floating point types). For some odd multiples of pi/2, you might "get lucky" and find that it's really close to one of the two neighbors, but on average you're going to find it's about 1e-16 away. So your input is already wrong by 1e-16 or so.

Now, cosine has slope +1 or -1 at its zeros, so the error in the output will be roughly proportional to the error in the input. But to get an exact zero, you'd need error smaller than the smallest representable nonzero double, which is around 2e-308. That's nearly 300 orders of magnitude smaller than the error in the input.

While you coudl in theory "get lucky" and have some multiple if pi/2 that's really really close to the nearest representable double, the likelihood of this, just modelling it as random, is astronomically small. I believe there are even proofs that there is no double x for which the correctly-rounded value of cos(x) is an exact zero. For single-precision (float) this can be determined easily by brute force; for double that's probably also doable but a big computation.

As to why printf is printing -0.000000, it's just that the default for %f is 6 places after the decimal point, which is nowhere near enough to see the first significant digit. Using %e or %g, optionally with a large precision modifier, would show you an approximation of the result you got that actually retains some significance and give you an idea whether your result is good.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
0

My issue is that when i input 90 i get the answer -0.000000. (why am i getting -0.000 instead of 0.000?)

  • cosine(90) is not precise enough to result in a value of 0.0. Use printf("cosine(%d)= %le\n",y, cosine(y)); (note the e) to see a more informative view of the result. Instead, cosine(90) is generating a negative result in the range [-0.0005 ... -0.0] and that is rounded to "-0.000" for printing.

Can anybody explain why and how i can solve this issue?

OP's cosine() lacks sufficient range reduction, which for degrees can be exact.

x %= 360; was a good first step, yet perform a better range reduction to a 90° width like [-45°...45°], [45°...135°], etc.

Also recommend: Use a Taylor series with sufficient terms (e.g. 10) and a good machine PI1. Form the terms more carefully than pow(rad, 2 * n) / fact(2 * n), which inject excessive error.

Example1, example2.

Other improvements possible, yet something to get OP started.


1 #define PI 3.1415926535897932384626433832795

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256