简体   繁体   中英

Floating point rounding in C

I've run into some weird rounding behaviour with floats. The code below demonstrates the problem. What is the best way to solve this? I've been looking for solutions but haven't had much luck.

#include<stdio.h>

int main(void)
{   
    float t;
    t = 5592411;
    printf("%f\n", 1.5*t);
    t *= 1.5;
    printf("%f\n", t);
    return 0;
}

The code above should print out the same value, but I get this on my setup using GCC 4.7.2:

8388616.500000

8388616.000000

If I use a calculator, I get the first value, so I assume the second is being rounded somehow. I have identical Fortran code which does not round the value(has the 0.5).

1.5 is a double constant rather than a float and C has automatic promotion rules. So when you perform 1.5*t what happens is (i) t is converted to a double ; (ii) that double is multiplied by the double 1.5 ; and (iii) the double is printed (as %f is the formatter for a double ).

Conversely, t *= 1.5 promotes t to a double, performs a double multiplication and then truncates the result to store it back into a [single precision] float .

For evidence, try either:

float t;
t = 5592411;
printf("%f\n", 1.5f*t); // multiply a float by a float, for no promotion
t *= 1.5;
printf("%f\n", t);
return 0;

Or:

double t; // store our intermediate results in a double
t = 5592411;
printf("%f\n", 1.5f*t);
t *= 1.5;
printf("%f\n", t);
return 0;

The first calculation is done with double precision, the second is calculated the same, but truncated to single precision in the assignment to float .

If you use double for your variable, you'll get the same result. It's a good idea to use this type over float whenever accuracy may be a concern.

In the first case, the result is a double which can precisely represent the desired value.

In the second case, the result is a float which can't precisely represent the desired value.

Try the same with double and you'll end up with the same results either way.

#include<stdio.h>

int main(void)
{   
    double t;
    t = 5592411;
    printf("%f\n", 1.5*t);
    t *= 1.5;
    printf("%f\n", t);
    return 0;
}

Writing 1.5 in C code is interpreted as a double, which has more precision than the float type.

The first case,

printf("%f\n", 1.5*t);

results in t being implicitly converted to a double (with greater precision) and then multiplied. The printf function, which casts the input corresponding to %f anyway, prints the result, which is also a double .

The second case has the 1.5 being converted to the float type, which has less precision and cannot store as small details.

If you want to avoid this effect, use 1.5f instead on 1.5 to use floats , or change the type of t to double .

Whether this would work at all depends on the machine representation of floats and doubles. Passing a float on a typical 32 bit architecture pushes 4 bytes on the argument stack. Passing a double would push 8 bytes. Passing a double but using %f is asking to treat it as a float which will look at the first 4 bytes pushed in our typical case. Depending on machine representation this might be close to the intended result or might be way out in left field.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM