输出为 6.9531e-308 或 0(菜鸟)

Posted

技术标签:

【中文标题】输出为 6.9531e-308 或 0(菜鸟)【英文标题】:Output is either 6.9531e-308 or 0 (noob) 【发布时间】:2017-07-27 21:23:54 【问题描述】:

我正在尝试编写一个程序,该程序将根据电话的开始/结束时间以及通话时间来计算通话费用。一切正常,除了它试图实际显示每次通话的费用和最后的总费用。

每项费用都列为 6.9531e-308 美元。有similar issue 的人通过将有问题的变量初始化为零来解决他们的问题,但是当我这样做时,程序只是开始将每个成本列为 0 美元。

我计算总额的方式似乎也不起作用,因为当成本为 6.9531e-308 美元时,总额为 7.64841e-307 美元,而不是 4.17186e-307 美元。它要么将其中一个成本乘以 11,要么认为需要将 11 个成本相加。

代码如下:

#include <fstream>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <string>
#include <cstring>

using namespace std;

ifstream fin("call_history.txt"); //read from a file

//int main
int main() 

    string temp;
    double total = 0.0;

    while (getline(fin, temp)) 
        istringstream s(temp);
        string token[3];
        int i = 0;

        while(getline(s, token[i], ' '))  //split the string by spaces and store into token[0], token[1], etc.
            i++;
        
        //token[0] stores the day of week
        //token[1] stores the time (i.e "14:32")
        //token[2] stores duration

        istringstream s2(token[1]);
        string time_values[2];
        i = 0;

        while(getline(s2, time_values[i], ':'))  //split token[1] by ':'
            i++;
        
        //time_values[0] stores hour
        //time_values[1] stores minute

        //rename variables for readibility
        string day = token[0];
        int hr = atoi(time_values[0].c_str()); //atoi to convert string to integer

        int minute = atoi(time_values[1].c_str());
        int duration = atoi(token[2].c_str());
        double cost=0; //accumulator
        int start_time = (hr*60)+minute;
        int end_time = start_time+duration;
        int startzone, endzone;

        if (day == "Mo" || day == "Tu" || day == "We" || day == "Th")
            
                day == "weekday";
            
            if (day == "Sa" || day == "Su")
            
                day == "weekend";
            
    //assigning each starting time to a zone
            if ((day == "weekday" || day == "Fr") && (start_time >= 480 && start_time < 1080))
            
                startzone = 1; //call starts on hours
            
            if ((day == "weekday" || day == "Fr") && (start_time <= 480))
            
                startzone = 2; //call starts before 8 am
            
            if ((day == "weekday" || day == "Fr") && (start_time >= 1080))
            
                startzone = 3; //call starts after 6 pm
            
            if (day == "weekend")
            
                startzone = 4; //call starts on the weekend
            
    //assigning each ending time to a zone
    //this program will handle at most 3 zone changes in one call.

            if (day == "weekday" && (end_time >= 480 && end_time <= 1080))
            
                endzone = 1; //call ends on-hours the same day
            
            if (day == "weekday" && (end_time >= 1080 && end_time <= 1920))
            
                endzone = 2; //call ends after 6 pm on the same day, before 8 am on the next day
            
            if (day == "weekday" && (end_time > 1920 && end_time <= 2520))
            
                endzone = 3; //call ends on-hours on the next day
            
            if (day == "weekday" && (end_time > 2520))
            
                endzone = 4; //call ends after 6 pm the next day
            
            if (day == "weekend" && (end_time <= 2880))
            
                endzone = 5; //call starts and ends on the weekend
            
            if (day == "Fr" && (end_time >= 1440 && end_time <= 2880))
            
                endzone = 6; //call goes from weekday to weekend
            
            if (day == "Su" && (end_time >= 1440 && end_time <= 1920))
            
                endzone = 7; //call goes from weekend to weekday off-hours
            

        //Cost calculations
        //CALL STARTS ON HOURS
                if (startzone == 1 && endzone == 1) //call is entirely within on-hours
                
                    cost = duration*0.4;
                
                if (startzone == 1 && endzone == 2) //call starts on-hours and ends before 8 am the next day
                
                    cost = ((1080-start_time)*0.4) + ((end_time-1080)*0.25);
                
                if (startzone == 1 && endzone == 3) //call starts on-hours and ends on-hours the next day
                
                    cost = ((1080-start_time)*0.4) + (840*0.25) + ((end_time-1920)*0.4);
                
                if (startzone == 1 && endzone == 4) //call starts on-hours and ends after 6 pm the next day
                
                    cost = ((1080-start_time)*0.4) + (840*0.25) + (600*0.4) + ((end_time-2520)*0.25);
                
                if ((startzone == 1 && endzone == 6)) //call starts on hours friday and ends on the weekend
                
                    cost = ((1080-start_time)*0.4) + ((360)*0.25) + ((end_time-1440)*0.15);
                

        //CALL STARTS OFF HOURS
                if (startzone == 2 && endzone == 2) //call starts before 8 am and ends before 8 AM the next day
                
                    cost = ((480-start_time)*0.25) + (600*0.4) + ((end_time-1440)*0.25);
                
                if (startzone == 2 && endzone == 3) //call starts before 8 am and ends on-hours the next day
                
                    cost = ((480-start_time)*0.25) + ((end_time-1920)*0.25);
                
                if (startzone == 2 && endzone == 6) //call starts before 8 AM friday and ends on the weekend
                
                    cost = ((480 - start_time)*0.25) + (600*0.4) + (360*0.25) + ((end_time-1440)*0.15);
                
                if (startzone == 3 && endzone ==6) //call starts after 6 PM friday and ends on the weekend
                
                    cost = ((1440-start_time)*0.25) + ((end_time-1440)*0.15);
                
                if ((startzone == 3 && endzone == 2) || (startzone == 2 && end_time <= 480)) //call is entirely within off-hours
                
                    cost = duration*0.25;
                
                if ((startzone == 3 && endzone == 3)) //call starts after 6 pm and ends on-hours the next day
                
                    cost = ((1920-start_time)*0.25) + ((end_time-1920)*0.4);
                
                if ((startzone == 3 && endzone == 4)) //call starts after 6 pm and ends after 6 pm the next day
                
                    cost = ((1920-start_time)*0.25) + (600*0.4) + ((end_time-2520)*0.25);
                

        //CALL STARTS ON WEEKEND
                if (startzone == 4 && endzone == 5) //call is entirely within weekends
                
                    cost = duration*0.15;
                
                if (startzone == 4 && endzone == 7) //call starts on sunday and ends before 8 am monday
                
                    cost = ((1440-start_time)*0.15) + ((end_time-1440)*0.25);
                

        cout << setw(4)<< endl;
        cout << day << " " << hr << ":" << minute << " " << duration << " $" << cost << "\n";
        total += cost;

    
    cout << setw(4) << endl;
    cout << "\tTotal $" << total << "\n";


【问题讨论】:

1.我不希望 cost 显示为 0,我希望它显示为通话费用,由适当的公式 2 计算得出。如何降低值? 是否有可能在成本计算下没有一个 if 语句永远不会被调用,所以 cost 总是 0 这意味着 total 总是 0?您是否尝试过调试您的代码? 调试器。使用调试器。调试器将帮助您在观察变量中的值时分别执行每个语句。请使用调试会话的结果编辑您的帖子。 您能否编辑问题以添加输入示例、预期计算(如何计算成本)以及程序给您的输出? 您发布的代码是输出零还是非常小的正数?您需要一次只关注一个问题,而不是代码的不同版本会做什么。另外,是的,调试器。 【参考方案1】:

问题不(完全)在于您的公式,而在于浮点数的固有限制

这里的关键问题是,在任何两个数字之间,都有无限个实数(实际上是无数个个),而在一个双重代表他们。所以会有 舍入错误。

此外,由于它们以 2 为底而不是以 10 为底存储,因此存在看起来确实应该可以精确表示的数字,但实际上并非如此。标准示例为0.1;在基数 2 中没有有限的表示。

考虑以下几点:

#include <cstdio>
int main() 
    double x = 0.0;
    for (int i = 0; i < 10; i++) 
        x += 0.1;
        printf("%.20f\n", x);
    

这将输出如下内容:

0.10000000000000000555
0.20000000000000001110
0.30000000000000004441
0.40000000000000002220
0.50000000000000000000
0.59999999999999997780
0.69999999999999995559
0.79999999999999993339
0.89999999999999991118
0.99999999999999988898

现在,最后一个数字应该是 1.0, 完全可以表示,但是到那时,舍入误差已经加起来,所以我们得到的结果略有不同。这就是您的代码中发生的事情。有微小的舍入错误加起来为 6.9531e-308,如果没有发生舍入,该值将是 0。最简单的解决方案就是以一种强制将 final 答案四舍五入到准确的格式打印。例如,如果我们在上面的示例中将printf("%.20f\n") 更改为printf("%.1f\n"),它将准确打印您期望的数字。在您的示例中,仅强制它使用科学记数法就足以使其四舍五入为 0。

double x = 6.9531e-308;
printf("%e\n", x);
printf("%f\n", x);
std::cout << x << "\n";
std::cout << std::fixed << x << "\n";

将输出:

6.953100e-308
0.000000
6.9531e-308
0.000000

如果您想了解浮点数的工作原理(我建议您最终了解浮点数的工作原理,即使您还没有处于您的教育阶段),这篇论文是标准参考:What Every Computer Scientist Should Know About Floating-Point Arithmetic

附录:

此外,您在几个地方使用day == "weekday";,您的意思是day = "weekday";,这就是它几乎接近0的原因。这可能是一个更重要的问题,尽管谈论起来不那么有趣。

【讨论】:

谢谢。所以基本上,我要做的就是在 cout 行前面添加一些东西,这样它就只打印已经被四舍五入的东西?此外,当我尝试将 day == "weekday" 更改为 day = "weekday" 时,它会被解释为错误,我无法编译该文件。 @Soulsoaker99 这里发生了两种不同类型的舍入。第一个是舍入误差,当一个数字不能以二进制浮点格式精确表示时,就会发生这种情况。然后是打印时发生的舍入;如果您尝试打印重复的小数,它显然不会打印无限数量的数字,也不会为长但有限的序列打印数百位数字。 std::fixed 所做的是告诉它不要使用科学记数法。 (续...) (...继续) 6.9531e-308 也是四舍五入的;它实际上将是 6.95317234782378423780432e-308 或其他东西,但你调用 setw 所以它只能打印小数点后的 4 位数字。如果它不使用科学计数法,那么在任何非零数字之前都会有数百个零,所以它会被四舍五入为0。至于为什么它不编译,你使用的是什么编译器,错误是什么?我看到的唯一错误是您应该为 atoi 包含

以上是关于输出为 6.9531e-308 或 0(菜鸟)的主要内容,如果未能解决你的问题,请参考以下文章

C语言菜鸟问题:

有关VERILOG语言模块中端口数据类型问题,菜鸟求解。

菜鸟请教:如何在C++6.0的环境下,新建或产生一个MFC文件?

菜鸟系列 Golang 实战 Leetcode —— 面试题16. 数值的整数次方

菜鸟心得_Python中可迭代的类实例只有一次使用机会

JavaScript 简版-菜鸟中的菜鸟2