C中浮子的铸造整数不一致的圆整

时间:2016-01-19 18:18:14

标签: c casting floating-point precision

我整天都在梳理我的头发,因为这个问题很难解决,而且我很确定表面上还有一些我不知道的事情。任何反馈或帮助都受到了极大的欢迎。

我有一个函数,它接受一个浮点值并返回一个ASCII字符串输出到LCD屏幕上。因此功能是:

char returnString(float x, bool isTriggTemp)
{
    char numberString[3];
    char bufferString[2];
    char bufferStringDec[7];

    int integerVal = (int)x;

    if(isTriggTemp == false)
    {
        int decimalVal = (x-integerVal)*10000;
        sprintf(numberString, "%s.%s", itoa(bufferString,integerVal,10),
             itoa(bufferStringDec,decimalVal,10));
    }
    else
    {
        int decimalVal = (x-integerVal)*1000; 

        sprintf(numberString, "%s.%s", itoa(bufferString,integerVal,10),
             itoa(bufferStringDec,decimalVal,10));
    }

    return numberString;
}

If语句和bool的原因是有两个浮点数可以传递给函数。一个有1个小数位,一个有多达5个。我不会讨论5位小数,因为我在程序后面比较两个浮点数,相关小数似乎大致一致。

我的查询来自这一行:

 int decimalVal = (x-integerVal)*10;

当像这样使用时,在我给出上述逻辑的情况下,我希望使用它,输出的字符串读取" X.0"无论X值如何。

将它增加到100:

 int decimalVal = (x-integerVal)*100;

仅为偶数提供正确的值。 (当浮动是X.2时,X.2很好),但是奇数我似乎得到一个向下的版本(X.3浮点数打印X.2,X.5 - > X.4)等。 / p>

当我将它增加到1000时,我开始看到问题的开始:

int decimalVal = (x-integerVal)*1000;

对于偶数值,例如X.4,我打印了X.40。奇数我比原来的浮点数少0.01。例如。 X.5 - > X.49。

显然这里有些不对劲,并且非精确小数正在被切断。 A)我该如何解决这个问题?和B)鉴于算术我会猜到应该使用* 10但是* 100是10的数量级,这使我最接近正确的输出。

非常感谢任何帮助。

4 个答案:

答案 0 :(得分:2)

您可以使用%f为浮点数编写所需的精度。甚至动态地进行精度等级:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

// Use malloc (drawback: need to track return and deallocate to prevent memory leak.)
char * returnString( float x, int isTriggTemp )
{
    char * buffer = malloc( 128 );
    if( buffer )
        sprintf( buffer, "%0.*f", isTriggTemp ? 4 : 5, x );
    return buffer;
}

// Use static buffer (drawback: non-reentrant)
char * returnStringStatic( float x, int isTriggTemp )
{
    static char buffer[128];
    sprintf( buffer, "%0.*f", isTriggTemp ? 4 : 5, x );
    return buffer;
}

// Use given buffer (drawback: caller needs to be aware of buffer size needs and additional parameters are involved)
char * returnStringGivenBuffer( char * buffer, float x, int isTriggTemp )
{
    sprintf( buffer, "%0.*f", isTriggTemp ? 4 : 5, x );
    return buffer;
}

int main( int argc, char ** argv )
{
    float val = 3.14159;
    int highprecision = 0;

    printf( "Using sprintf\n" );
    printf( "%0.1f\n", val );
    printf( "%0.5f\n", val );
    printf( "%0.*f\n", ( highprecision ? 5 : 1 ), val );
    highprecision = 1;
    printf( "%0.*f\n", ( highprecision ? 5 : 1 ), val );

    printf( "\nUsing sprintf\n" );
    char buffer[128];
    sprintf( buffer, "%0.1f", val );
    printf( "%s\n", buffer );
    sprintf( buffer, "%0.5f", val );
    printf( "%s\n", buffer );
    sprintf( buffer, "%0.*f", ( highprecision ? 5 : 1 ), val );
    printf( "%s\n", buffer );
    highprecision = 1;
    sprintf( buffer, "%0.*f", ( highprecision ? 5 : 1 ), val );
    printf( "%s\n", buffer );

    printf( "\nUsing dynamic allocation\n" );
    char * fval = returnString( val, 0 );
    printf( "%s\n", fval ? fval : "" );
    if( fval ) free( fval );
    fval = returnString( val, 1 );
    printf( "%s\n", fval ? fval : "" );
    if( fval ) free( fval );

    printf( "\nUsing static buffer\n" );
    char * ptr = returnStringStatic( val, 0 );
    printf( "%s\n", ptr );
    ptr = returnStringStatic( val, 1 );
    printf( "%s\n", ptr );

    printf( "\nUsing given buffer\n" );
    ptr = returnStringGivenBuffer( buffer, val, 0 );
    printf( "%s\n", ptr );
    ptr = returnStringGivenBuffer( buffer, val, 1 );
    printf( "%s\n", ptr );

    return 0;
}

结果:

Using sprintf
3.1
3.14159
3.1
3.14159

Using sprintf
3.1
3.14159
3.14159
3.14159

Using dynamic allocation
3.14159
3.1416

Using static buffer
3.14159
3.1416

Using given buffer
3.14159
3.1416

答案 1 :(得分:1)

在各种情况下打印分数的OP方法失败

  1. 分数接近1.0returnString(0.999999, isTriggTemp)

  2. x值大于INT_MAX

  3. 负数。

  4. 一种更可靠的方法,首先缩放然后分解为整数/分数部分。

    char *returnString(float x, bool isTriggTemp) {
       float scale = 10000;
       x = roundf(x * scale);
       float ipart = x/scale;
       float dpart = fmodf(fabsf(x), scale);
       itoa(bufferString,ipart,10);
       itoa(bufferStringDec,dpart,10);  // May need to add leading zeros
       ...
    

    [编辑]

    获得前导零的简单方法:

       static char bufferString[20+7];
       char bufferStringDec[7];
    
       itoa(bufferStringDec,dpart + scale,10);
       bufferStringDec[0] = '.';  // Overwrite leading `1`.
       return strcat(bufferStringDec, bufferStringDec);
    

    当然代码可以使用

    void returnString(char *numberString, size_t size, float x, bool isTriggTemp) {
      snprintf(numberString, sizeof numberString, "%.*f", isTriggTemp ? 3 : 4, x);
    }
    

    在功能完成后,确保缓冲区为static或以其他方式可用。

    检查返回类型。

答案 2 :(得分:1)

你确定这段代码没有崩溃吗?你的缓冲区很小我认为你的缓冲区可能会溢出。无论如何,平原工作和赔率的原因可能不是因为你做了像int decimalVal =(x-integerVal)* 10000这样的事情。事情变得圆滑了。例如,int x =(int)((2.29 - 2)* 10)将为您提供2,而不是3.在此示例中,修复是在乘以10之前添加.05。我确定您可以找出一般规则。

答案 3 :(得分:0)

先生。 Houpis的答案是最好的选择,但是对于你的启发,你可能需要以浮点数的方式进行计算:

int decimalVal = (x - (float) integerVal) * 10000.0;

此外,您对returnString的声明应该是char *而不是char。