Floating point rounding in C

China☆狼群 提交于 2020-01-04 05:49:09

问题


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:


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;



回答2:


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.




回答3:


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;
}



回答4:


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.




回答5:


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.



来源:https://stackoverflow.com/questions/15798338/floating-point-rounding-in-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!