2

I would like to make a table which can show sin,cos,tan,cot values of chosen degrees. As i said, i couldn't take the right answer of tan90 . I don't know how to change it to 'INFINITY'. I am waiting for your help ! :)

#include<math.h>
#include<stdlib.h>
#include<iostream>

using namespace std;

main()
{
    
    double table[10][4];
    int i=0,j=0;
    double val=0,PI=3.14159;
    val = PI / 180.0;

    printf("\tİstenen Tablo\n\t-------------\n");
    printf("\t ACI\t\t\t SIN\t\t\t COS\t\t\t TAN\t\t\t COT\n\t------\t\t\t------\t\t\t------\t\t\t--- 
 ---\t\t\t------");

    for(i=0;i<=90;i+=10)
    {
        for(j=0;j<5;j++)
        {
            switch (j)
            {
            case 0:table[i/10][j] = i; break;
            case 1:table[i/10][j] = sin(i*val);break;
            case 2:table[i/10][j] = cos(i*val);break;
            case 3:table[i/10][j] = tan(i*val);break;
            case 4:table[i/10][j] = 1/tan(i*val);break;
            }
        }
            printf("\n\t %lf  \t\t %lf\t\t %lf\t\t %lf\t\t %lf\n",table[i/10][0],table[i/10] 
 [1],table[i/10][2],table[i/10][3],table[i/10][4]); 
    }
   return 0;
}
Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
berry.11
  • 88
  • 7
  • 1
    Perhaps you need a more accurate value for `PI`, see: https://stackoverflow.com/questions/1727881/how-to-use-the-pi-constant-in-c – Paul Sanders Apr 10 '21 at 23:00
  • Do not tag both C and C++ except when asking about differences or interactions between the languages. – Eric Postpischil Apr 10 '21 at 23:29
  • The code in your question does not compile, because `main()` is not a proper declaration (use `int main(void)` or `int main(int argc, char *argv[])`), and because string in the second `printf` is split over two lines. Additionally, with `table` defined as `table[10][4]`, the highest index for its second dimension is 3, but this program uses 4, so the behavior is not defined. – Eric Postpischil Apr 10 '21 at 23:31
  • 2
    It's interesting that a very similar question was just asked recently: https://stackoverflow.com/questions/66958777/cant-convert-a-float-in-array-to-string – rustyx Apr 10 '21 at 23:33

3 Answers3

1

The reason you're getting incorrect values is that π is not 3.14159, that's just an approximation to its real value. In fact, any non-infinite series of decimal digits will be an approximation but, the more digits you have, the closer i * val will be to the correct value you should be passing to your trigonometric calls.

A far better value would be M_PI from math.h, which has far more precision than what you currently have.

You still may not get your expected values due to the limited precision of even the double type. If that happens, you will need to adjust what you get before working things out in floating point.

For example, the following functions could be used to get more acceptable values, forcing very specific values on quadrant boundaries so as to avoid even the smallest possibility of imprecision in those results:

double mySin(int degrees) {
    int mod360 = degrees % 360;

    if (mod360 ==   0) return  0.0;
    if (mod360 ==  90) return  1.0;
    if (mod360 == 180) return  0.0;
    if (mod360 == 270) return -1.0;

    return sin(degrees % 360 * M_PI / 180.0);
}

double myCos(int degrees) {
    return mySin(degrees + 90);
}

double myTan(int degrees) {
    int mod360 = degrees % 360;

    if (mod360 ==   0) return  0.0;
    if (mod360 ==  90) return  1.0 / 0.0;
    if (mod360 == 180) return  0.0;
    if (mod360 == 270) return -1.0 / 0.0;

    return tan(mod360 * M_PI / 180.0);
}

double myCot(int degrees) {
    // Now that tan() works properly for the quadrant
    // boundaries, just use normal formula.

    return 1.0 / myTan(degrees);
}

These are built from the ground up to use degree inputs rather than radians and, because you trap the infinite value cases before doing any floating point math (which is inherently imprecise when you're dealing with transcendentals), you can give the correct results for those cases.


A complete program, integrating these functions and getting rid of stuff you don't need is shown below. There's no point storing all those values in an array and then printing out the array (with no other use of it) when you can just print the values immediately:

#include <cmath>
#include <cstdio>

using namespace std;

double mySin(int degrees) {
    int mod360 = degrees % 360;

    if (mod360 ==   0) return  0.0;
    if (mod360 ==  90) return  1.0;
    if (mod360 == 180) return  0.0;
    if (mod360 == 270) return -1.0;

    return sin(mod360 * M_PI / 180.0);
}

double myCos(int degrees) {
    return mySin(degrees + 90);
}

double myTan(int degrees) {
    int mod360 = degrees % 360;

    if (mod360 ==   0) return  0.0;
    if (mod360 ==  90) return  1.0 / 0.0;
    if (mod360 == 180) return  0.0;
    if (mod360 == 270) return -1.0 / 0.0;

    return tan(mod360 * M_PI / 180.0);
}

double myCot(int degrees) {
    // Now that tan() works properly for the quadrant
    // boundaries, just use normal formula.

    return 1.0 / myTan(degrees);
}

int main()
{
    printf("İstenen Tablo\n");
    printf("-------------\n");
    printf("\t    ACI     \t    SIN    \t    COS    \t    TAN    \t    COT\n");
    printf("\t------------\t-----------\t-----------\t-----------\t-----------\n");

    for (int i = 0; i < 360; i += 10) {
        printf("\t%12d\t%9.9lf\t%9.9lf\t%9.9lf\t%9.9lf\n",
            i, mySin(i), myCos(i), myTan(i), myCot(i));
    }
   return 0;
}

The output of that is:

İstenen Tablo
-------------
            ACI             SIN             COS             TAN             COT
        ------------    -----------     -----------     -----------     -----------
                   0    0.000000000     1.000000000     0.000000000           inf
                  10    0.173648178     0.984807753     0.176326981     5.671281820
                  20    0.342020143     0.939692621     0.363970234     2.747477419
                  30    0.500000000     0.866025404     0.577350269     1.732050808
                  40    0.642787610     0.766044443     0.839099631     1.191753593
                  50    0.766044443     0.642787610     1.191753593     0.839099631
                  60    0.866025404     0.500000000     1.732050808     0.577350269
                  70    0.939692621     0.342020143     2.747477419     0.363970234
                  80    0.984807753     0.173648178     5.671281820     0.176326981
                  90    1.000000000     0.000000000           inf       0.000000000
                 100    0.984807753     -0.173648178    -5.671281820    -0.176326981
                 110    0.939692621     -0.342020143    -2.747477419    -0.363970234
                 120    0.866025404     -0.500000000    -1.732050808    -0.577350269
                 130    0.766044443     -0.642787610    -1.191753593    -0.839099631
                 140    0.642787610     -0.766044443    -0.839099631    -1.191753593
                 150    0.500000000     -0.866025404    -0.577350269    -1.732050808
                 160    0.342020143     -0.939692621    -0.363970234    -2.747477419
                 170    0.173648178     -0.984807753    -0.176326981    -5.671281820
                 180    0.000000000     -1.000000000    0.000000000           inf
                 190    -0.173648178    -0.984807753    0.176326981     5.671281820
                 200    -0.342020143    -0.939692621    0.363970234     2.747477419
                 210    -0.500000000    -0.866025404    0.577350269     1.732050808
                 220    -0.642787610    -0.766044443    0.839099631     1.191753593
                 230    -0.766044443    -0.642787610    1.191753593     0.839099631
                 240    -0.866025404    -0.500000000    1.732050808     0.577350269
                 250    -0.939692621    -0.342020143    2.747477419     0.363970234
                 260    -0.984807753    -0.173648178    5.671281820     0.176326981
                 270    -1.000000000    0.000000000          -inf       -0.000000000
                 280    -0.984807753    0.173648178     -5.671281820    -0.176326981
                 290    -0.939692621    0.342020143     -2.747477419    -0.363970234
                 300    -0.866025404    0.500000000     -1.732050808    -0.577350269
                 310    -0.766044443    0.642787610     -1.191753593    -0.839099631
                 320    -0.642787610    0.766044443     -0.839099631    -1.191753593
                 330    -0.500000000    0.866025404     -0.577350269    -1.732050808
                 340    -0.342020143    0.939692621     -0.363970234    -2.747477419
                 350    -0.173648178    0.984807753     -0.176326981    -5.671281820

Note the disparity between signs on the infinities and forced zeros. While both 0 / 1 and 0 / -1 can be treated as zero (there are no negative zeros despite the fact IEEE754 allows them), the values for 1 / 0 and -1 / 0 are given +inf and -inf respectively.

My more advanced mathematical buddies may disagree but I think I've got that right.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • Thanks for your help :) But I couldn't manage to combine your code with mine. Should I change something extra? – berry.11 Apr 10 '21 at 23:38
  • When i try the code, i get the error that a function-definition is not allowed here before '{' token. – berry.11 Apr 10 '21 at 23:54
  • 1
    @sm.dmr: Based on your error, it looks like you've just put my code in the wrong place in your program but I can't say for sure without seeing it. However, I've shown a complete program at the end of the answer that compiles and runs okay so that should hopefully be enough to help you out. – paxdiablo Apr 11 '21 at 00:23
1

tan(x) is extremely sensitive to even tiny variations in the argument around odd multiples of pi/2 because its derivative 1/cos^2(x) is also unbounded around those points. This, along with the limited precision of the computer floating points, cause significant errors when evaluating tan around odd multiples of pi/2.

The better strategy is to first reduce the angle to the first octant [0, 45) (degrees) then use basic trig identities to derive the target value. This will provide better precision overall, and will also give an actual inf value at 90 degrees since 0 is an exactly representable number, and tan(0) = 0.

An example of such an implementation is below.

#include <math.h>

const double deg2rad = M_PI / 180.0;

double tan_degrees(int n)
{
    bool neg = false, inv = false;

    if(n < 0) { n = -n; neg = true; }        // tan(-x)       = -tan(x)
    n %= 180;                                // tan(x + pi)   =  tan(x)
    if(n > 90) { n = 180 - n; neg = !neg; }  // tan(pi - x)   = -tan(x) 
    if(n > 45) { n = 90 - n;  inv = true; }  // tan(pi/2 - x) = 1 / tan(x)

    double val = tan(n * deg2rad);
    if(neg) val = -val;
    if(inv) val = 1. / val;
    return val;
}
dxiv
  • 16,984
  • 2
  • 27
  • 49
0

Common C implementations use the IEEE-754 “double” format (also called binary64) for their double type. The closest value to π/2 representable in this format is 884279719003555 / 562949953421312, which is 1.5707963267948965579989817342720925807952880859375. It differs from π/2 by about 6.12323399573676588613033•10−17. Its tangent is about 1.633123935319536975•1016. (The closest representable value to its tangent is 1.633123935319537•1016.) So the best result you can get by converting 90 degrees to radians and applying tan to the result is 1.633123935319536975•1016.

Because of this, it is not possible to get ∞ from tan(x). All numbers representable in this double format are rational, so none are exactly multiples of π/2, so none have tangents that are infinity.

(There are some numbers representable in the format that are closer to an odd multiple of π/2 than the value above. This is due to the irregularity of the irrational period of π with respect to the representable numbers. However, from memory, none of those numbers are sufficiently close to an odd multiple of π/2 that their tangent is outside the finite range of this double format, which would cause the tan function to overflow to an infinity.)

You might check whether the math library you are using provides a tanpi or tanPi function. This and related functions are recommended by IEEE 754-2008. Apple supplies tanPi with the name __tanPi. __tanPi(x) returns tan(πx), subject to calculation errors. __tanPi(.5) returns ∞.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312