[英]Inconsistent rounding of cast integers from floats in C
由于这个小问题,我整天都在梳头,事实证明这个问题很难解决,而且我很确定有些事情正在发生,而我还没有意识到。 任何反馈或帮助都将受到欢迎。
我有一个函数,它接受一个浮点值,并返回一串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语句和布尔值的原因是可以将两个浮点数传递给该函数。 一个带有1个小数位,一个带有最多5个小数。我不必大惊小怪的5个小数位,因为我稍后在程序中比较两个浮点数,并且相关的小数似乎是大致一致的。
我的查询源于此行:
int decimalVal = (x-integerVal)*10;
像这样使用时(根据上述逻辑,这是我期望使用的),无论X值如何,输出的字符串都将读取“ X.0”。
将其增加到100:
int decimalVal = (x-integerVal)*100;
仅给出偶数的正确值。 (当浮点数为X.2时,X.2很好),但是我使用奇数取整(X.3浮点数打印X.2,X.5-> X.4)等。
当我将其增加到1000时,我开始看到问题的开始:
int decimalVal = (x-integerVal)*1000;
对于偶数,例如X.4,我得到X.40。 使用奇数时,我得到的浮点数将比原始浮点数少0.01。 例如,X5-> X.49。
显然这里有些不对劲,不精确的小数将被截断。 A)我该如何解决? B)给定算术,我猜应该使用* 10,但是* 100是10的数量级,这使我最接近正确的输出。
任何帮助深表感谢。
您可以使用%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
在各种情况下,OP打印分数的方法均失败
分数接近1.0
。 returnString(0.999999, isTriggTemp)
x
值大于INT_MAX
。
负数。
一种更可靠的方法是先缩放然后分解为整数/分数部分。
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
或可用的。
检查退货类型。
您确定此代码不会崩溃吗? 您的缓冲区太小了,我认为您可能会溢出缓冲区。 无论如何,偶数有效且赔率无效的原因可能是因为当您执行诸如int decimalVal =(x-integerVal)* 10000; 事情四舍五入。 例如,int x =(int)((2.29-2)* 10)将给您2,而不是3。在此示例中,解决方法是在乘以10之前加上.05。我确定您可以算出排除一般规则。
侯皮斯先生的答案是最好的选择,但是为了您的兴趣,您可能需要以浮点数的形式进行计算:
int decimalVal = (x - (float) integerVal) * 10000.0;
另外,您对returnString的声明应为char *而不是char。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.