这是一个例子。
#include <stdio.h>
int main(void){
int score1 = 70, score2 = 58, score3 = 99;
float total = 3;
printf("Average: %f\n", (score1 + score2 + score3)/total);
}
它给了我75.666664
。而如果我像这样的代码那样将 Total 替换为 3.0
#include <stdio.h>
int main(void){
int score1 = 70, score2 = 58, score3 = 99;
printf("Average: %f\n", (score1 + score2 + score3)/3.0);
}
它给了我75.666667.
C 中的所有内容都有一个类型,其中包括数字常量/文字,例如
3.0
.3
有类型int
.3.0f
有类型float
.3.0
有类型double
.反过来,
float
和double
在某个系统上可能具有也可能不具有相同的精度。显然double
您的系统具有更高的精度,因此您会注意到差异。关于隐式类型提升的一些注意事项:
score1 + score2 + score3
都是类型,int
因此不会发生隐式升级,结果是int
.这个结果
int
与另一个类型为 的操作数放在一起float
,意味着 会int
被提升到float
操作之前(根据“通常的算术转换”,请参阅上面的链接)。或者万一的话
3.0
,int
结果就已经晋升了double
。printf
并%f
按照发生的情况执行总是期望 adouble
作为输入(%lf
也将起作用),并且如果您将其交给 afloat
,它将被double
称为“默认参数升级”的可变参数函数的奇怪规则隐式升级。但由于最终的类型提升发生在计算结果之后,因此它不会影响精度。
最佳实践:
int
,因为这是众所周知的错误根源。坚持一种类型并在需要时显式转换操作数。float
double
例如,我会像这样重写你的代码:
功能是 100% 等效的,但这是自记录代码,不依赖于隐式提升说“我知道我在做什么”。
相反 - 依赖于静默隐式提升的代码 - 说:“要么我知道我在做什么并依赖于隐式提升,要么我不知道我在做什么并且只是通过(坏)得到了这个表达式的正确/错误运气”。
事实并非如此。
您的第一个示例将总和除以 32 位浮点 3.0f,并计算单精度浮点计算可以表示的最接近 227/3.0f 的近似值。
您的第二个示例将总和除以(隐式)64 位双精度 3.0,并获得双精度可以表示的最接近 227/3.0 的近似值。
如果使用%26.18g 格式打印,区别会更明显。
我建议您将 75.6666666666666666666666 放入十进制转二进制网站Base Convert IEEE754中,看看它在 IEEE754 下的实际二进制 FP 表示中如何工作。
顺便说一句,许多现代优化编译器将在编译时将除以常量转换为乘以预先计算的倒数常量值“1.0f/3.0f”,因为它比除法快得多。检查汇编程序列表,看看您的编译器是否属于这种情况。