天天看点

关于浮点数和字符串转换的函数示例

这是一位同学发到我邮箱的问题,看起来这位同学很急,不但发了邮件,还几次短消息催我,呵呵,那我回答一下。

嗯,这里还是先说明一下,我一般不太愿意在博文中回答具体程序问题,因为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'的结束符。

你先看看吧,有问题再问。

继续阅读