我正在编写几个函数,以便在十进制小时(2小时30分钟是2.5小时)和H.MMSS格式(其中两个半小时是2.3000)之间进行转换。我最初使用double实现了这些函数,一切工作正常。然而,随着我的嵌入式项目规模的增长,我用完了闪存,不得不转移到浮动。
这里是我的MWE显示的问题
#include <stdio.h>
#include <math.h>
float toHMS(float inputTime) {
float hours = 0;
float minutes = 0;
float seconds = 0;
// get integral part for the hours
hours = (int)inputTime;
// transform into decimal minutes
inputTime -= hours;
inputTime *= 60;
// Get the minutes
minutes = (int)inputTime;
// Get the seconds
inputTime -= minutes;
seconds = (int)(100 * inputTime);
return hours + minutes/100 + seconds/ 10000;
}
float toHours (float inputTime) {
float hours = 0;
float minutes = 0;
float seconds = 0;
// get integral part for the hours
hours = (int)inputTime;
inputTime = 100 * (inputTime - hours);
minutes = (int)inputTime;
inputTime = inputTime - minutes;
seconds = 100 * inputTime;
// normalize to 60
minutes = minutes / 60;
seconds = seconds / 3600;
return hours + minutes + seconds;
}
float toHours2 (float inputTime) {
float hours = 0;
float minutes = 0;
float seconds = 0;
// get integral part for the hours
hours = (int)inputTime;
inputTime = roundf(100 * (inputTime - hours));
minutes = (int)inputTime;
inputTime = inputTime - minutes;
seconds = 100 * inputTime;
// normalize to 60
minutes = minutes / 60;
seconds = seconds / 3600;
return hours + minutes + seconds;
}
void main(void) {
float hms = toHMS(2.5f);
printf("toHMS(2.5f): %.9f\n", hms);
printf("toHours(toHMS(2.5f)): %.9f\n", toHours(hms));
printf("\n");
printf("toHours(2.3f): %.9f\n", toHours(2.3f));
printf("\n");
printf("toHours2(2.3f): %.9f\n", toHours2(2.3f));
}
结果是:
toHMS(2.5f): 2.299999952
toHours(toHMS(2.5f)): 2.511111021
toHours(2.3f): 2.511111021
toHours2(2.3f): 2.500000000
在我看来,问题出在toHours()中,因为2.3在单个精度浮点数中是2.299999952。在toHours 2()中添加roundf()解决了这个问题。
这是唯一需要的修复,还是有其他输入数字会导致计算失败?我正在努力寻找一个解决方案,并向自己证明它适用于所有可能的输入数字,而不仅仅是2.3f。
谢谢!
2条答案
按热度按时间jexiocij1#
toHours
失败,因为2.30不能以float
常用的格式表示,IEEE-754“单精度”(binary 64)。最接近的可表示值是2.2999999523162841796875。您的例程正确地返回近似的十进制时间2小时29分钟99.999523162841796875秒,约为2.5111。但那不是你想这样解释的2.299999523162841796875;你想把它解释为“2.3000”。下面更多关于如何做到这一点。toHours2
在使用roundf
时失败。对于2.3050f
的输入,它返回约2.516666651,持续2小时31分钟,但正确答案约为2.513888836。对于合理的时间,您可以将
toHours
实现为:只要时间没有增长太大,并且来自一个整数秒的值,
lround(inputTime * 1e4)
就会有效地恢复数字,修复转换为二进制浮点时发生的舍入。然后,可以使用整数算术将该数字分开。对于超过1,024小时的时间,您可能会看到舍入问题,因为1,024是binary 32格式不能再表示间隔小于0.0001的数字的点(间距为1,024/223,约为0.000122),您需要它来区分“H.MMSS”格式的秒。m3eecexj2#
......或者是否有其他输入数字会使计算出错?
是的。这些是因为重复的圆化而出现的。考虑浮点值刚好低于整数的 * hours *。
相反,最小化舍入并使用整数数学执行大多数计算。
将
hours
更改为 * H. MMSS *,缩放,舍入,分割为H,M,S。类似的代码将
H.MMSS
转换为 * hours *。