浮动值未正确存储

时间:2015-06-27 20:31:56

标签: c formatting floating-accuracy

我正在编写一个函数,假设将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会搞​​乱格式化。如果有人也能提供这方面的答案,我们将不胜感激。

提前致谢。

4 个答案:

答案 0 :(得分: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");

}

这很有效。

答案 1 :(得分:1)

您已指定带有十进制实数值的二进制浮点值。 二进制浮点不能完全代表所有实际十进制值。

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

使用以合理精度显示的格式说明符:

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

或使用double,这对15位十进制数字有用。

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

答案 2 :(得分:0)

你的代码中的问题与浮点精度没有关系,精度有限制,因为这种舍入的artihmetic操作会引入错误,但对于两位小数,应该没有问题,并且它没有问题。无论内部值是否打印为123.449997,如果你这样做

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

123.45,并且对于涉及该值的任何算术运算,结果对于足够的小数位精度都是正确的,2。

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

数学也是错误的,因为如果centi_sec大于或等于100,则应添加一秒,并且100应从centi_sec中减去,并且同样适用于sec min,依此类推。

这些

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);
}

答案 3 :(得分:0)

浮点数的准确度非常高,并非所有数字都可以用浮点变量精确表示。

我的第一个建议是传递一个包含4个整数字段的小结构

int hours
int minutes
int seconds
int fractionSecondX100

以避免使用浮动设置整个问题。

但是,如果你必须使用浮动值,那么google:“如何处理C 中的浮点值”,这将在google之后返回许多“点击”,阅读几个引用的网页所以你知道如何处理花车并知道会发生什么。