输出为 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++6.0的环境下,新建或产生一个MFC文件?