2

I'm very new to C programming. I have to print the values of tan(0) to tan(pi/2) in steps of pi/20 for both single and double precision floats. However, when I use different data types to store the floats, nothing changes between single and double, and I expected the number of digits to change.

#include <stdio.h>
#include <math.h>
#define PI 3.1415926

int main()
{
    float angle = 0.0;
    float pi_single = 3.1415926;
    printf("single precision:\n");
    while(angle < pi_single/2){
            float(tanangle) = 0.0;
        tanangle = tan(angle);
        printf("tan(%f) = %f\n", angle, tanangle);
        angle = angle + pi_single/20;
    }

    double angle2 = 0.0;
    double pi_double = 3.141592653589793;
    printf("double precision:\n");
    while(angle2 < pi_double/2 ){
            double(tanangle2) = 0.0;
        tanangle2 = tan(angle2);
        printf("tan(%lf) = %lf\n", angle2, tanangle2);
        angle2 = angle2 + pi_double/20;
    }
    return 0;
}

I'm trying to replicate the result of this Python program:

import numpy as np
theta_32 = np.arange(0, np.pi/2+np.pi/20, np.pi/20, dtype = 'float32')
print('single precision')
for theta in theta_32:
    print(theta)
    print(np.tan(theta))
print()

[enter image description here][1]
theta_64 = np.arange(0, np.pi/2+np.pi/20, np.pi/20, dtype = 'float64')
print('double precision')
for theta_new in theta_64:
    print(theta_new)
    print(np.tan(theta_new))

  • 1
    `float(tanangle) = 0.0;` is a very weird way to define `tanangle`. Most people write `float tanangle = 0.0;`, and writing it with parentheses will confuse people. Why did you write it that way? – Eric Postpischil Sep 15 '19 at 14:37
  • Right, sorry. I'm not used to declaring variables, that's probably a leftover habit from Python. – pythonraptor Sep 15 '19 at 14:46

3 Answers3

2

You need to use tanf if you want the computation to take place in float rather than double.

Note also that when working with floating point, it's best to use an integral type, int i, say to count from 0 to 19, then use pi * i / 20 for the angle. For further reading see Is floating point math broken?

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
  • Wikipedia gives the format specifier for single precision floats as %f and double precision floats as %lf. Long double is %Lf. – pythonraptor Sep 15 '19 at 14:35
  • @pythonraptor: Oops, thank you and welcome to Stack Overflow. – Bathsheba Sep 15 '19 at 14:38
  • 1
    Both `%f` and `%lf` work for printing `float` and `double` in C99 and later (in C90, only `%f` was defined by the standard). Use `%Lf` for `long double`. With `scanf()` (not used in the question), the formats are `%f` for `float`, `%lf` for `double`, and `%Lf` for `long double`. The reason that `%f` works for printing both `float` and `double` is that default argument promotion rules mean that `float` arguments to `printf()` are automatically converted to `double. – Jonathan Leffler Sep 15 '19 at 14:40
  • I'm sorry, I tried your suggestion and still cannot get a proper solution. Can you provide some more guidance or your own solution? – pythonraptor Sep 15 '19 at 14:54
  • Even with the for loop and tanf vs tan, I still get the same results for both single and double variables. – pythonraptor Sep 15 '19 at 14:57
  • @Bathsheba I posted Python code above to demonstrate what I want to achieve in C. – pythonraptor Sep 15 '19 at 15:03
1

Two main issues here:

First, the tan function takes a double and returns a double. Use tanf instead which takes a float and returns a float. So change this:

tanangle = tan(angle);

To this:

tanangle = tanf(angle);

Second, the %f format specifier by default prints 6 digits of precision. That's not enough to see the different between single and double precision floating point. Expand the precision to say 15 digits and you'll see a difference. So then this:

printf("tan(%f) = %f\n", angle, tanangle);

Becomes:

printf("tan(%.15f) = %.15f\n", angle, tanangle);

And this:

printf("tan(%lf) = %lf\n", angle2, tanangle2);

Becomes:

printf("tan(%.15lf) = %.15lf\n", angle2, tanangle2);
dbush
  • 205,898
  • 23
  • 218
  • 273
  • Thank you! I see a difference now! Two other questions, if I may; In Python, when I store single precision floating points, fewer digits are actually printed than when I print double precision floating points. Why is C different? I also get slightly different results when evaluating the angles in C vs with Python. Also, to get the last angle to be close to pi/2 in double precision, I have to use pi/2 + pi/20 for the while loop. Why doesn't this happen in single precision? – pythonraptor Sep 15 '19 at 15:13
0

I expected the number of digits to change

printf() is controlling the number of digits printed, not the type of the variable.

Use "%e" to see the floating point nature of a float rather than fixed with "%f". To see all the useful digits, rather than print to the default precision of 6, use a precision modifier.

xxx_DECIMAL_DIG - 1 is the number of decimal digits to use with "%e" to see a total of xxx_DECIMAL_DIG significant digits. It is the number of digits need to distinguish all of that floating point type. ref

#include <float.h>

  // printf("tan(%f) = %f\n", angle, tanangle);
  printf("tan(%.*e) = %.*e\n", FLT_DECIMAL_DIG - 1, angle, DBL_DECIMAL_DIG - 1, tanangle);

  // printf("tan(%f) = %f\n", angle2, tanangle2);
  printf("tan(%.*e) = %.*e\n", DBL_DECIMAL_DIG - 1, angle2, DBL_DECIMAL_DIG - 1, tanangle2);

Code could use atanf() to get the float result when the result is saved in a float.

// tanangle = tan(angle);`
tanangle = tanf(angle);`

printf("tan(%.*e) = %.*e\n", FLT_DECIMAL_DIG - 1, angle, FLT_DECIMAL_DIG - 1, tanangle);

Rather than approximate pi with fixed values, let the system calculate the best.

//float pi_single = 3.1415926;
//double pi_double = 3.141592653589793;
float pi_single = acosf(-1);
double pi_double = acos(-1);

Better to use an integer loop

for (int i=0; i<= 20; i++) {
  float angle = i*pi_single/20;
  float tanangle = tanf(angle);
  printf("tan(%.*e) = %.*e\n", 
    FLT_DECIMAL_DIG - 1, angle, 
    FLT_DECIMAL_DIG - 1, tanangle);
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • Perfect, thanks. Is it possible to display in regular decimals instead of scientific notation? – pythonraptor Sep 15 '19 at 16:22
  • Nice answer but one reservation in the way that you suggest `pi` can be computed. Issue with that is that no standard (not even IEEE754) guarantees that the best result for *pi* for your type will be returned. Far better then to define it yourself or use a maths library. I know this is C++ but see https://stackoverflow.com/questions/49778240/does-c11-14-17-or-20-introduce-a-standard-constant-for-pi/49778493#49778493 – Bathsheba Sep 15 '19 at 16:25
  • @pythonraptor. "Is it possible to display in regular decimals instead of scientific notation?" --> Sure, use `"*.f"`. That would provide less information when `x` is small (uninformative leading zeros, truncated digits) or noise when `x` is large (uninformative least significant digits.) Try `printf("%.*g\n", DBL_DECIMAL_DIG, some_double);` as a compromise. – chux - Reinstate Monica Sep 15 '19 at 20:47
  • @Bathsheba Should `acos(-1)` not provide the best machine pi, which I agree is not guaranteed as you comment, the quality of the math library is weak and any trig math.h results would have weaknesses/suspect too. Perhaps as a compromise, , code could specify the pi constant to certainly more digits than than 99.99999% of compilers need today, such as `double pi_d = ‭3.14159265358979323846264338327950288‬; pi_f = ‭3.14159265358979323846264338327950288‬f;`. – chux - Reinstate Monica Sep 15 '19 at 20:56