0

Currently I'm new with C and for a course I'm doing an assignment. The assignment is to write two programs that calculate tan(x) with x incrementing from 0 to pi/2. One program has to work with float numbers and the other program needs to work with double precision numbers. The goal of the assignment is to become aware of the differences in output, since it's part of scientific programming course.

So far I wrote this program:

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

int main()
{
    double x;
    
    double pi;
    pi = M_PI;
     
    for (x = 0; x <= pi / 2; x += pi / 20)
    {
        printf("x = %f, tan = %f\n", x, tan(x));
    }
    
    exit(0);
}

I got feedback that the output is double. Yesterday I started a thread related to this code, but since different questions rose up, it seemed appropriate to start a new one.

The general principal of the loop works, but I'm stuck in what to do next.

Let's first work on the double-version of the program. Since I need to calculate tan(x) and x is not 100% exact, due to the decimal incrementation, I think it's important to define the variables as double. Yesterday I learnt that the output of the printf function is double. Therefore I assume that code is correct for the goal to calculate in double.

Here I have a little side question. I also printed .20f and it gave me that specific window. I learned a bit about 32- and 64-bit. Double is 64-bit and can store 15 decimal places in the memory.

Question: Has the above code achieved the double precision goal?

Question: Where do the other decimals (16th to 20th) come from if the memory was already exceeded?

For the float version of the program, I need to define the variables as float.

double x;
double pi;

becomes

float x;
float pi;

Then comes the printf statement and here it becomes a bit confusing, because I know now that the printf function gives output in double precision. But I want single precision and still get it printed. I couldn't find another function that specifically prints in single precision.

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

int main()
{
    float x;

    float pi;
    pi = M_PI;

    for (x = 0; x <= pi / 2; x += pi / 20)
    {
        printf("x = %f, tan = %f\n", x, tan(x));
    }

    exit(0);
}

Question: How do I print in single precision?

Question: What do I need to change in the last program to achieve the goal of the float-program?

I'm eager to learn, but it's a bit hard since we're not allowed at the university anymore due Covid-19. I hope someone could give me feedback.

Thanks, Ter

chqrlie
  • 131,814
  • 10
  • 121
  • 189
Tim
  • 415
  • 2
  • 10
  • 1
    Step 1 is to check out the [IEEE 754 floating point format](https://en.wikipedia.org/wiki/IEEE_754) and learn about what's going on under the hood. Remember that while it can only store a specific number of binary digits, which translates to a rough number of decimal digits, converting from binary to decimal may introduce additional digits at the end as an artifact of the conversion process. For example, 1/8th is 0.125, or three digits, even though 8 in binary is only three bits. – tadman Sep 11 '20 at 07:41
  • 1
    You *can't* get `printf` to print in single-precision, all `float` arguments will automatically be *promoted* to `double`. This is done as part of [*default argument promotion*](https://en.cppreference.com/w/c/language/conversion#Default_argument_promotions) which happens for all [variadic function](https://en.cppreference.com/w/c/language/variadic) argument. – Some programmer dude Sep 11 '20 at 07:43
  • 4
    The `printf` is fine the way it is. But you should be using the `tanf` function. `tanf` takes a `float` argument, and returns a `float` result. – user3386109 Sep 11 '20 at 07:43
  • The output of `printf` is not `double`. The output of `printf` is a sequence of characters, like “1.347”. What do you mean when you say the output of `printf` is `double`? Are you talking about the number of digits it prints? – Eric Postpischil Sep 11 '20 at 11:10
  • Re “Question: Where do the other decimals (16th to 20th) come from if the memory was already exceeded?”: Common C implementations use a binary-based format for floating-point. Statements you see that `double` can store “15 decimal places” are false; it does not store any decimal places. It stores a binary-based number, essentially a real number with little correlation to decimal numerals. When you print more than 15 digits, you are seeing more information about the actual value of the binary-based number in the `double`. – Eric Postpischil Sep 11 '20 at 11:12

2 Answers2

3

printf always prints double but that also means it can print floats since any float can be converted to a double without loss of accuracy. Just because printf prints a double doesn't mean that it can somehow magically add precision to a float.

A double is binary, so it's not meaningful to say that it can store 15 decimal digits. What is true is that the first fifteen decimal digits printed will be accurate.

john
  • 85,011
  • 4
  • 57
  • 81
2

Has the above code achieved the the double precision goal?

No. "%f" only prints 6 places after the decimal point, even when not informative for large values and insufficient for tiny ones. To print all the precision, use "%a" - yet this is not a decimal output.

// printf("x = %f, tan = %f\n",x, tan(x));
printf("x = %a, tan = %a\n",x, tan(x));

To print in decimal, and with sufficient informative precision, use "%.*e" or "%.*g". Details.

printf("x = %.*e, tan = %.*e\n",DBL_DECIMAL_DIG-1,x, DBL_DECIMAL_DIG-1,tan(x));
printf("x = %.*g, tan = %.*g\n",DBL_DECIMAL_DIG  ,x, DBL_DECIMAL_DIG  ,tan(x));

Where do the other decimals (16th to 20th) come from if the memory was already exceeded?

This is an artifact of a decimal output, yet the floating point value is encoded with a binary significand. Exact printing of some double takes hundreds of digits. Frequently DBL_DIG to DBL_DECIMAL_DIG significant digits are useful: typically 15 to 17.

How do I print in single precision?
What do I need to change in the last program to achieve the goal of the float-program?

Use float variables and functions and print in exponential notation. Use FLT_DECIMAL_DIG instead of DBL_DECIMAL_DIG.

// printf("x = %f, tan = %f\n",x, tan(x));
printf("x = %.*e, tan = %.*e\n",FLT_DECIMAL_DIG-1,x, FLT_DECIMAL_DIG-1, tanf(x));

For float, often the default of 6 places is OK.

printf("x = %e, tan = %e\n", x, tanf(x));
printf("x = %g, tan = %g\n", x, tanf(x));

  double d = M_PI / 20;
  printf("x = %a, tan = %a\n", d, tan(d));
  printf("x = %.*e, tan = %.*e\n", DBL_DECIMAL_DIG - 1, d, DBL_DECIMAL_DIG - 1, tan(d));
  printf("x = %.*g, tan = %.*g\n", DBL_DECIMAL_DIG, d, DBL_DECIMAL_DIG, tan(d));

  float f = M_PI / 20;
  printf("x = %a, tan = %a\n", f, tan(f));
  printf("x = %.*e, tan = %.*e\n", FLT_DECIMAL_DIG - 1, f, FLT_DECIMAL_DIG - 1, tanf(f));
  printf("x = %e, tan = %e\n", f, tanf(f));
  printf("x = %g, tan = %g\n", f, tanf(f));

x = 0x1.41b2f769cf0ep-3, tan = 0x1.445f0fbb1cf91p-3
x = 1.5707963267948966e-01, tan = 1.5838444032453627e-01
x = 0.15707963267948966, tan = 0.15838444032453627
x = 0x1.41b2f8p-3, tan = 0x1.445f1p-3
x = 1.57079637e-01, tan = 1.58384442e-01
x = 1.570796e-01, tan = 1.583844e-01
x = 0.15708, tan = 0.158384
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256