2015-06-27 61 views
1

我正在写一个函数,它假设已将float“length”传递给它,然后显示一个类似于输出的计时器。浮点“长度”意味着在几分钟内。浮点值未被正确存储

当我运行它时,输出应该是:02:03:27:00。相反,它显示02:03:26:100,虽然技术上正确,但它是A)不是它应该如何显示和B)显示在某个地方有一个错误,可能会在未来导致不良结果。

用计算器手动检查后发现所有的数学都是合理的。然后,我评论了格式为零的部分,以查看是否导致错误并且问题仍然存在。然后,我在每次计算后都会放入printf,发现“length”设置为123.45时,它被存储为123.449997?

我不知道它是这样做的。而且因为我不知道这种情况或发生的方式,所以我不能为它写一个可靠的解决方案。

int main() 
{ 

float length; 
float working; 
int hour; 
int min; 
int sec; 
int centi_sec; 
char hzero[2]; 
char mzero[2]; 
char szero[2]; 
char czero[2]; 

    length=123.45; 
    working=floor(length); 
    working=(length-working)*60; 
    sec=floor(working); 
    working-=floor(working); 
    centi_sec=(working*100)+.5; 
    working=floor(length); 
    hour=floor((working/60)); 
    min=working-(hour*60); 
    if(hour<10){ 
     hzero[0]='0'; 
     hzero[1]=""; 
    } 
    else if(hour==0){ 
     hzero[0]='0'; 
     hzero[1]='0'; 
    } 
    else{ 
     hzero[0]=""; 
     hzero[1]=""; 
    } 
    if(min<10){ 
     mzero[0]='0'; 
     mzero[1]=""; 
    } 
    else if(min==0){ 
     mzero[0]='0'; 
     mzero[1]='0'; 
    } 
    else{ 
     mzero[0]=""; 
     mzero[1]=""; 
    } 
    if(sec<10){ 
     szero[0]='0'; 
     szero[1]=""; 
    } 
    else if(sec==0){ 
     szero[0]='0'; 
     szero[1]='0'; 
    } 
    else{ 
     szero[0]=""; 
     szero[1]=""; 
    } 
    if(centi_sec<10){ 
     czero[0]='0'; 
     czero[1]=""; 
    } 
    else if(centi_sec==0){ 
     czero[0]='0'; 
     czero[1]='0'; 
    } 
    else{ 
     czero[0]=""; 
     czero[1]=""; 
    } 
    printf("%s%d:%s%d:%s%d:%s%d\n", hzero, hour, mzero, min, szero, sec, czero, centi_sec); 
    system("pause"); 

} 

我也写一个简短的程序只是这是不是问题,因为我曾错过了完整的程序的东西的影响,它有同样的问题脸颊。

int main() 
{ 

float length=123.45; 

    printf("%f\n", length); 
    system("pause"); 

} 

P.S.当我使用printf来解决问题时,我发现printf正在搞乱零格式。没有太大的问题,因为当我删除它们时,格式化回到它应该如何。尽管如此,printf的格式化并没有任何意义。如果任何人也可以提供这个答案,这将不胜感激。

在此先感谢。

+0

你有一个潜在的问题,因为'floor()'不返回'int'它返回'double'。也不是这个'hzero [0] =“”;'生成编译器警告? –

+0

你有一个很大的非科学数据类型可以使用 – Drew

回答

1

您已分配一个二进制浮点数值为十进制实数值。 二进制浮点数不能完全表示所有实数十进制值。

单精度二进制浮点有利于精确表示大约6位十进制有效数字,123.449997是9位数;所以你已经超出了承诺的精度。默认情况下,%f格式说明符显示6个小数位,但在这种情况下超出了可用精度。

要么使用显示合理精度的格式说明:

printf("%.3f\n", length) ; 

,或者使用double这是很好的15个小数显著数字。

对于高内存带宽和硬件浮点单元(即所有现代台式计算机)的目标,不使用双精度的原因很少。如果您正在处理真正的海量数据,并且需要减少处理时间并且不需要精度,则单精度非常有用。

+0

谢谢,它现在应该如何工作。 – Ulrick

0

代码中的问题与浮点精度无关,精度有限制,并且由于舍入操作引入了错误,但小数点后两位应该没有问题,也不会出现问题不管内部值打印为123.449997,如果你做

printf("%.2f\n", 123.449997); 

123.45将被打印,并且也涉及价值的任何算术运算,其结果将是精度,2足够的小数位正确。

你最重要的问题是您的字符串不能是这样的字符串,因为没有空间终止'\0'

而且数学是错误太多,因为如果centi_sec大于或等于则应该加100一秒,100应从centi_sec中减去,这同样适用于secmin,等等。

这些

char hzero[2]; 
char mzero[2]; 
char szero[2]; 
char czero[2]; 

应该读

char hzero[3]; 
char mzero[3]; 
char szero[3]; 
char czero[3]; 

你也应该不会重复自己,用一个函数

#include <math.h> 
#include <stdio.h> 

void zeropad(int value, char str[3]) 
{ 
    if (value < 10) 
    { 
     str[0] = '0'; 
     str[1] = value + '0'; 
    } 
    else 
    { 
     str[0] = (value - value % 10)/10 + '0'; 
     str[1] = value % 10 + '0'; 
    } 
    str[2] = '\0'; 
} 

int main() 
{ 
    float length; 
    float working; 
    int hour; 
    int min; 
    int sec; 
    int centi_sec; 
    char hzero[3]; 
    char mzero[3]; 
    char szero[3]; 
    char czero[3]; 

    length = 123.45; 
    working = floor(length); 
    working = (length - working) * 60; 
    sec  = floor(working); 
    working -= floor(working); 
    centi_sec = (working * 100) + .5; 
    working = floor(length); 
    hour  = floor(working/60); 
    min  = working - (hour * 60); 

    if (centi_sec >= 100) 
    { 
     sec  += 1; 
     centi_sec -= 100; 
    } 

    if (sec >= 60) 
    { 
     min += 1; 
     sec -= 60; 
    } 

    if (min >= 60) 
    { 
     hour += 1; 
     min -= 60; 
    } 

    zeropad(hour, hzero); 
    zeropad(min, mzero); 
    zeropad(sec, szero); 
    zeropad(centi_sec, czero); 

    printf("%s:%s:%s:%s\n", hzero, mzero, szero, czero); 
} 
+1

这可能是发布的代码有问题,但它与提出的问题无关,应该作为评论发布(如果有的话)。 – Clifford

+0

您的示例中也存在长度问题,但void zeropad确实解决了printf混淆格式的问题。我仍然对c知道,并且我不太了解zeropad中正在发生的一切。我得到了一些,但不是全部。另外,我仍然不明白为什么printf首先会搞乱格式化? – Ulrick

0

浮点数只有这么多的准确性,而不是所有的数字可以在浮点变量中精确表示。

我的第一个建议是传递一个包含4个整数领域

int hours 
int minutes 
int seconds 
int fractionSecondX100 

从而避免整个问题浮筒设置较小的结构。但是,如果您必须使用浮点值,那么google:“如何处理C中的浮点值”,这会在google之后返回很多'点击数',请阅读几个引用的网页,以便您了解处理花车并且知道会发生什么。

1

由于数学原因,小数部分的二进制表示在所有情况下都不能精确。你可以阅读更多关于这个here

为了避免这个问题,你需要添加一半的最小单位的输入。 在这种情况下,这将是1.0/60/100/2

length = 123.45; 
const float epsilon = 1.0/60/100/2; 
length += epsilon; 

你试图做同样的事情与

centi_sec=(working*100)+.5; 

但这只有一个没有其他数字上centi_sec效果,。将其更改回

centi_sec=(working*100); 

也请按照@iharob的建议更改阵列大小。

编辑:您可以完全避免阵列:

#include <math.h> 
#include <stdio.h> 

int main() 
{ 

const float epsilon = 1.0/60/100/2; 
float length; 
float working; 
int hour; 
int min; 
int sec; 
int centi_sec; 

    length=123.45; 
    length += epsilon; 
    working=floor(length); 
    working=(length-working)*60; 
    sec=floor(working); 
    working-=floor(working); 
    centi_sec=(working*100); 
    working=floor(length); 
    hour=floor((working/60)); 
    min=working-(hour*60); 
    printf("%02d:%02d:%02d:%02d\n", hour, min, sec, centi_sec); 
// system("pause"); 

} 

这工作。

+0

这实际上使之不那么准确。 – Ulrick

+0

我做了一个更简单的程序版本,它适用于我。 – alain

+0

问题是float被存储为123.449997,在你的代码中它被存储为124.450081,这是我的不太准确。但它确实完全消除了对零阵列的需求。所以这是一个很大的改进。 – Ulrick