unknown
unknown

Reputation: 33

Why is calculation using float variable different from what I think?

#include <stdio.h>

int main(void)
{
    float r = 31.0f;
    float pi = 3.14f;
    float volume = 4.0f / 3.0f * pi * r * r * r;
    
    printf("volume = %f\n", volume);
    
    float k1 = 4.0f / 3.0f;
    printf("k1 = %f\n", k1);

    float k2 = k1 * pi;
    printf("k2 = %f\n", k2);

    float k3 = k2 * r;
    printf("k3 = %f\n", k3);

    float k4 = k3 * r;
    printf("k4 = %f\n", k4);

    float k5 = k4 * r;
    printf("k5 = %f\n", k5);


    return 0;
}

I'm beginner in C.

I thought that variable 'k5' must equal to 4023.387207 x 31 = 124725.003417

(I have learned so far that float variable is accurate up to six decimal points.)

But the output shows that k5 == 124725.000000

Why is this happening like this?

(My English is not very good, so please understand.)

Upvotes: 2

Views: 184

Answers (4)

John Bode
John Bode

Reputation: 123458

You cannot squeeze an infinite number of real values into a finite number of bits, so most floating point values are only approximations of real values. Any arithmetic on floating-point values will have some amount of error in the least significant digits.

Secondly, there are real values that cannot be represented in a finite number of bits. Just like we can only approximately represent 1/3 as 0.3333333..., values like 1/10 can only be approximately represented in binary as 0.000100110011001100110011001...2

Finally, a single-precision float is only guaranteed to be accurate to 6 decimal digits total, not 6 digits after the decimal point. You can be accurate to 0.123456, 123.456000, 123456.000000, or 123456000.000000.

Upvotes: 2

4386427
4386427

Reputation: 44274

On most systems a float with value 124725.000000 has the hex value 0x47f39a80

So the next representable float will have the hex value 0x47f39a81 which correspond to the value 124725.0078125000

No value between 124725.000000 and 124725.0078125000 can be held by a float

So yours is rounded to the first, i.e. 124725.000000

Change all float in your program to double and you'll get another result.

You can try this code:

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

typedef union
{
    unsigned u;
    float f;
} MT;

int main(void)
{
    if (sizeof(float) != sizeof(unsigned)) exit(1);
    
    MT x;
    
    x.f = 124725.0f;
    printf("x.f = %.56f\n", x.f);
    printf("x.u = 0x%08x\n", x.u);

    x.u++;    
    printf("x.f = %.56f\n", x.f);
    printf("x.u = 0x%08x\n", x.u);

    return 0;
}

output on my system

x.f = 124725.00000000000000000000000000000000000000000000000000000000
x.u = 0x47f39a80
x.f = 124725.00781250000000000000000000000000000000000000000000000000
x.u = 0x47f39a81

BTW:

I have learned so far that float variable is accurate up to six decimal points.

Well, that's not true. For some value range float can't even show a single decimal point. For some other value range float can't even show odd numbers.

Upvotes: 4

mcleod_ideafix
mcleod_ideafix

Reputation: 11428

Your first expression:

float volume = 4.0f / 3.0f * pi * r * r * r;

even if it is using floats for constants and variables, allows the compiler to automatically promote them into doubles at calculation time, improving accuracy a bit. It's something like:

float volume = (float) ( (double)4.0f / (double)3.0f * 
               (double)pi * (double)r * (double)r * (double)r );

Making all the multiplications and divisions to be performed with double operands.

On the other hand, your second approach, splitting the expression into smaller expressions and storing the intermediate results in float variables, doesn't allow the compiler to do such promotion (it may do it, but the improved result would loose its gained accuracy when you store it in a float for the next step). For example, your first two expressions would look like this:

float k1 = (float) ((double)4.0f / (double)3.0f );
printf("k1 = %f\n", k1);

float k2 = (float) ( (double)k1 * (double)pi );
printf("k2 = %f\n", k2);

See? Even if 4.0 / 3.0 is calculated with double precission, when you store it into k1, it looses such precission so that smaller precission version is the one that is used with pi. In the first expression, the result of 4.0f / 3.0f, double precission, is multiplied by variable pi and doesn't loose precission.

Upvotes: 4

Roman Pavelka
Roman Pavelka

Reputation: 4171

Floats have finite precision. Replace them with doubles or long doubles and observe the difference.

Longer story: https://en.wikipedia.org/wiki/Single-precision_floating-point_format

Upvotes: 1

Related Questions