[英]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.