天天看點

關于浮點數和字元串轉換的函數示例

這是一位同學發到我郵箱的問題,看起來這位同學很急,不但發了郵件,還幾次短消息催我,呵呵,那我回答一下。

嗯,這裡還是先說明一下,我一般不太願意在博文中回答具體程式問題,因為C和C++語言,太靈活了,一個問題,有一萬種解法,另外,每種解法各有其适用面,貿然在博文中回答,必然不是很精确,回答效果不好。

這次呢,我看這位同學比較急迫,我就破個例,不過,正确的做法,我建議大家以後有了具體的程式問題呢,還是到課堂提問,大家來回答。那麼多個腦袋,總比我一個腦袋聰明,想問題也全面,回答效果會比較好哈。

另外,這個程式我是為了回答問題,臨時寫出來的測試代碼,不是工程代碼,大家看看,了解意思就好了,不要貿然代入自己的工程,我不保證它是不是絕對沒有bug的。它甚至不符合我《0bug-C/C++商用工程之道》裡面的無錯化程式思想。僅僅用來說明意思的哈,别被誤導了。

原文如下:

我是浙江大學軟體工程的,csdn上一直浏覽您的首頁。以前我隻重理論,導緻程式設計不行,現在開始狂編,我現在能問您個問題嗎,糾結了好久了……

用C語言模拟計算機浮點數運算

1、float  stof(char *);                    //十進制字元串 --> float  (如:"-1.0" -->  0xFF800000)

2、float  fadd(float, float);

3、float  fsub(float, float);

4、float  fmul(float, float);

5、float  fdiv(float, float);

6、void  ftos(char *, float);           //float --> 十進制字元串(如:0x40000000 --> "2.0")

而我就卡在第一和第六個函數上,我搜遍了網上的資料,搞了好幾天沒有搞出來,我搜到Ieee754的算法又不知道怎麼去運用,我大概可以想到把它小數整數分開都做整數處理,但是感覺還是很難以下手……你能寫個大概讓我看一下嗎,我主要就是第一和第六個函數存在問題,好糾結啊……

我的回答:

嗯,這位同學你好,你的第一個和第六個問題,我來試着回答一下。先說啊,這不是工程代碼,是我為了給你回答問題臨時寫出來的測試代碼,你看看好了,學會了思想自己寫,我不保證其正确性的。

Code:

bool IsNumber(char c)   

{   

    if('0'>c) return false;   

    if('9'<c) return false;   

    return true;   

}   

double stof(char* szFloatString)   

    double fRet=0.0;    //試了一下,不能用float,精度太低了,我改用double   

    double fRetLittleNumber=0.0;   

    int nStringLength=strlen(szFloatString);   

    int i=0;   

    if(0>=nStringLength)   

        goto stof_End_Process_Error;   

    char cTarget=0;   

    i=0;                //這是在找整數部分   

    //整數部分順找,找到一個數字,基數*10+這個數字,就是把數字用10進制拼接到基數中   

    while(1)           

    {   

        cTarget=*(szFloatString+i);   

        if('.'==cTarget)   

            goto stof_Get_Little_Number;   

        if('\0'==cTarget)   

            goto stof_End_Process;   

        if(!IsNumber(cTarget))   

            goto stof_End_Process_Error;   

        cTarget-='0';   

        fRet*=10.0;     //先*10   

        fRet+=cTarget;  //再+,原來是1,新數字是2,1*10+2=12   

        i++;   

    }   

stof_Get_Little_Number:     //這是在找小數部分   

    //小數部分,從右向左逆找   

    //找到新數字,基數+新數字/10,這是把新數字拼接到左邊   

    i=nStringLength-1;   

    while(1)   

            goto stof_I_Get_It;   

        fRetLittleNumber+=cTarget;  //先+新數字   

        fRetLittleNumber/=10.0;     //再/10,0+1/10=0.1,0.1+2/10=0.21,明白沒有?   

        i--;   

stof_I_Get_It:          //找到了,拼接數字   

    fRet+=fRetLittleNumber;   

    goto stof_End_Process;   

stof_End_Process_Error: //這是出錯了   

    printf("\"%s\" is not float!\n",szFloatString);   

    fRet=0.0;   

stof_End_Process:       //這是傳回點   

    return fRet;   

void Test_stof(void)   

    char* pStr="";   

    pStr="123";   

    printf("\"%s\"=%f\n",pStr,stof(pStr));   

    pStr="123.321";   

    pStr="1234567890.0987654321";   

最後的運作結果:

"123"=123.000000   

"123.321"=123.321000   

"1234567890.0987654321"=1234567890.098765  

看見沒,由于C編譯器本身的精度問題,太長的小數被截短了。double還好點,要是float,第二個答案都是123.3209999,誤差就太大了。

C語言準确地講,不是一門太适合數學計算的語言,它浮點精度始終有限,如果要做純正的數學計算,建議用Fortran等專業的數學計算語言。

當然,C++可以提出一些替代解決方案,比如說,我們自己做個類,裡面用特殊的結構,比如就是超常數組來存儲一個高精度浮點數的很多位,并提供輸入輸出方法,以及常用的數學計算方法,說白了,就是我們自己實作一個浮點數類,那可以把精度做得很高的,不過,這顯然不是一篇博文能講清楚的,有興趣的朋友,可以自己試試。

另外,這個函數沒有處理負數,這個題目我留給大家想吧,看懂了我的代碼,加個負數支援也很容易。

好吧,第一個問題就回答在這裡。

第六個還用回答嗎?

sprintf(szBuffer,"%f",fResult);

直接用字元串輸出函數輸出到一個緩沖區就好了。

不過,為了安全起見,建議使用《0bug-C/C++商用工程之道》一書中的安全字元串構造函數,我這裡給你個原型,這個是工程代碼,我用了近十年都沒出過錯,可以直接用的。

int SafePrintf(char* szBuf,int nMaxLength,char *szFormat, ...)   

    int nListCount=0;   

    va_list pArgList;   

    if (!szBuf) goto SafePrintf_END_PROCESS;   

    va_start (pArgList,szFormat);   

    nListCount+=Linux_Win_vsnprintf(szBuf+nListCount,   

        nMaxLength-nListCount,szFormat,pArgList);   

    va_end(pArgList);   

    if(nListCount>(nMaxLength-1)) nListCount=nMaxLength-1;   

    *(szBuf+nListCount)='\0';   

SafePrintf_END_PROCESS:   

    return nListCount;   

 簡單說,就是給出字元串緩沖區,還要給出一個緩沖區大小,我的函數保證不會打出去溢出,還有,就是不管緩沖區夠不夠,總之保證你最後得到一個'\0'的結束符。

你先看看吧,有問題再問。

繼續閱讀