删除printf语句的不同答案

Adi*_*rma 8 c

这是关于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



请解释为什么会发生这种情况.

nne*_*neo 8

我相信我找到了罪魁祸首和合理的解释.

在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)

根据您的算术,可能还有其他位置需要此操作。

  • 什么?`double/int` 是双除法。我不知道你在忙什么。 (5认同)
  • @AdityaSharma确实如此,这个答案既不正确又与你的问题无关。如果我更改了代码中的任何内容,我将删除所有“100”x 并严格使用“double”,使用“floor”或“ceil”进行您认为需要的任何舍入。 (4认同)