这是关于UVa在线评判问题的链接.
https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=29&page=show_problem&problem=1078
我的C代码是
#include <stdio.h>
double avg(double * arr,int students)
{
int i;
double average=0;
for(i=0;i<students;i++){
average=average+(*(arr+i));
}
average=average/students;
int temp=average*100;
average=temp/100.0;
return average;
}
double mon(double * arr,int students,double average)
{
int i;
double count=0;
for(i=0;i<students;i++){
if(*(arr+i)<average){
double temp=average-*(arr+i);
int a=temp*100;
temp=a/100.0;
count=count+temp;
}
}
return count;
}
int main(void)
{
// your code goes here
int students;
scanf("%d",&students);
while(students!=0){
double arr[students];
int i;
for(i=0;i<students;i++){
scanf("%lf",&arr[i]);
}
double average=avg(arr,students);
//printf("%lf\n",average);
double money=mon(arr,students,average);
printf("$%.2lf\n",money);
scanf("%d",&students);
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
其中一个输入和输出是
输入
3
0.01
0.03
0.03
0
输出
$ 0.01
我的输出是
0.00美元.
但是,如果我取消注释行printf("%lf",平均值);
输出如下
0.02 //这是我在ideone.com上运行代码
的平均
$ 0.01
请解释为什么会发生这种情况.
我相信我找到了罪魁祸首和合理的解释.
在x86处理器上,FPU在内部以扩展精度运行,这是一种80位格式.所有浮点指令都以此精度运行.如果double实际需要a,编译器将生成代码以将扩展精度值转换为双精度值.至关重要的是,注释掉了printf这样的转换,因为FPU寄存器必须在该函数调用中保存和恢复,并且它们将被保存为doubles(注意avg并且mon都是内联的,因此不会发生保存/恢复).
事实上,printf我们可以使用该行static double dummy = average;来强制double转换,这也会导致错误消失:http://ideone.com/a1wadn
您的值average接近但不完全是0.02因为浮点不准确.当我明确地执行所有计算long double,并打印出值时average,它是以下内容:
long double: 0.01999999999999999999959342418532
double: 0.02000000000000000041633363423443
Run Code Online (Sandbox Code Playgroud)
现在你可以看到问题.添加时printf,average强制将double其推到0.02以上.但是,如果没有printf,average它的原生扩展精度格式将略低于0.02.
当你这样做int a=temp*100;的时候会出现bug.如果没有转换,就会产生a = 1.通过转换,这样做a = 2.
要解决这个问题,只需使用int a=round(temp*100);- 所有奇怪的错误都应该消失.
值得注意的是,这个bug 对代码中的更改非常敏感.任何导致寄存器被保存的东西(例如printf几乎任何地方)都会导致bug消失.因此,这是heisenbug的一个非常好的例子:当你试图调查它时,一个臭虫会消失.
And*_*ini -4
您将双精度数除以整数,这是整数除法。在这种情况下,它将给出 0 值。
如果您将学生转换为双倍,它应该会给您正确的输出。
average=average/(double)students;
Run Code Online (Sandbox Code Playgroud)
根据您的算术,可能还有其他位置需要此操作。