输出为6.9531e-308或0(noob)

时间:2017-07-27 21:23:54

标签: c++ string c++11

我正在尝试编写一个程序,根据开始/结束的时间以及需要多长时间来拨打电话费用。一切正常,除非它试图实际显示每次通话的费用和最后的总费用。

每笔费用都列为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 个答案:

答案 0 :(得分:0)

问题不是(完全)与您的公式有关,但浮点数的固有限制

这里的关键问题是,在任意两个数字之间,存在无数个实数(实际上是无数无限数),并且只有一定数量的空间。双重代表他们。所以舍入错误。

此外,由于它们存储在基数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。最简单的解决方案就是以一种格式打印,迫使它将最终答案四舍五入到准确的位置。例如,如果我们在上面的示例中将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

如果你想了解浮点数如何运作的细节(我建议你最终做到,即使你还没有进入你的教育阶段),本文是标准参考:{{ 3}}

<强>附录:

此外,您在day == "weekday";处理day = "weekday";几个地方if($var == "1" $var = true || if $var == "0" $var = false);,这就是为什么它接近0的原因。这可能是一个更重要的重要问题,尽管这个问题要少得多。