关于浮点数和字符串转换的函数示例-阿里云开发者社区

开发者社区> 开发与运维> 正文
登录阅读全文

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

简介:
 
这是一位同学发到我邮箱的问题,看起来这位同学很急,不但发了邮件,还几次短消息催我,呵呵,那我回答一下。
嗯,这里还是先说明一下,我一般不太愿意在博文中回答具体程序问题,因为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:
  1. bool IsNumber(char c)   
  2. {   
  3.     if('0'>c) return false;   
  4.     if('9'<c) return false;   
  5.     return true;   
  6. }   
  7. double stof(char* szFloatString)   
  8. {   
  9.     double fRet=0.0;    //试了一下,不能用float,精度太低了,我改用double   
  10.     double fRetLittleNumber=0.0;   
  11.     int nStringLength=strlen(szFloatString);   
  12.     int i=0;   
  13.     if(0>=nStringLength)   
  14.         goto stof_End_Process_Error;   
  15.     char cTarget=0;   
  16.   
  17.     i=0;                //这是在找整数部分   
  18.     //整数部分顺找,找到一个数字,基数*10+这个数字,就是把数字用10进制拼接到基数中   
  19.     while(1)           
  20.     {   
  21.         cTarget=*(szFloatString+i);   
  22.         if('.'==cTarget)   
  23.             goto stof_Get_Little_Number;   
  24.         if('\0'==cTarget)   
  25.             goto stof_End_Process;   
  26.         if(!IsNumber(cTarget))   
  27.             goto stof_End_Process_Error;   
  28.         cTarget-='0';   
  29.         fRet*=10.0;     //先*10   
  30.         fRet+=cTarget;  //再+,原来是1,新数字是2,1*10+2=12   
  31.         i++;   
  32.     }   
  33. stof_Get_Little_Number:     //这是在找小数部分   
  34.     //小数部分,从右向左逆找   
  35.     //找到新数字,基数+新数字/10,这是把新数字拼接到左边   
  36.     i=nStringLength-1;   
  37.     while(1)   
  38.     {   
  39.         cTarget=*(szFloatString+i);   
  40.         if('.'==cTarget)   
  41.             goto stof_I_Get_It;   
  42.         if(!IsNumber(cTarget))   
  43.             goto stof_End_Process_Error;   
  44.         cTarget-='0';   
  45.         fRetLittleNumber+=cTarget;  //先+新数字   
  46.         fRetLittleNumber/=10.0;     //再/10,0+1/10=0.1,0.1+2/10=0.21,明白没有?   
  47.         i--;   
  48.     }   
  49. stof_I_Get_It:          //找到了,拼接数字   
  50.     fRet+=fRetLittleNumber;   
  51.     goto stof_End_Process;   
  52. stof_End_Process_Error: //这是出错了   
  53.     printf("\"%s\" is not float!\n",szFloatString);   
  54.     fRet=0.0;   
  55. stof_End_Process:       //这是返回点   
  56.     return fRet;   
  57. }   
  58. void Test_stof(void)   
  59. {   
  60.     char* pStr="";   
  61.     pStr="123";   
  62.     printf("\"%s\"=%f\n",pStr,stof(pStr));   
  63.     pStr="123.321";   
  64.     printf("\"%s\"=%f\n",pStr,stof(pStr));   
  65.     pStr="1234567890.0987654321";   
  66.     printf("\"%s\"=%f\n",pStr,stof(pStr));   
  67. }   
最后的运行结果:
Code:
  1. "123"=123.000000   
  2. "123.321"=123.321000   
  3. "1234567890.0987654321"=1234567890.098765  
看见没,由于C编译器本身的精度问题,太长的小数被截短了。double还好点,要是float,第二个答案都是123.3209999,误差就太大了。
C语言准确地讲,不是一门太适合数学计算的语言,它浮点精度始终有限,如果要做纯正的数学计算,建议用Fortran等专业的数学计算语言。
当然,C++可以提出一些替代解决方案,比如说,我们自己做个类,里面用特殊的结构,比如就是超常数组来存储一个高精度浮点数的很多位,并提供输入输出方法,以及常用的数学计算方法,说白了,就是我们自己实现一个浮点数类,那可以把精度做得很高的,不过,这显然不是一篇博文能讲清楚的,有兴趣的朋友,可以自己试试。
另外,这个函数没有处理负数,这个题目我留给大家想吧,看懂了我的代码,加个负数支持也很容易。
好吧,第一个问题就回答在这里。
第六个还用回答吗?
sprintf(szBuffer,"%f",fResult);
直接用字符串输出函数输出到一个缓冲区就好了。
不过,为了安全起见,建议使用《0bug-C/C++商用工程之道》一书中的安全字符串构造函数,我这里给你个原型,这个是工程代码,我用了近十年都没出过错,可以直接用的。
Code:
  1. int SafePrintf(char* szBuf,int nMaxLength,char *szFormat, ...)   
  2. {   
  3.     int nListCount=0;   
  4.     va_list pArgList;   
  5.   
  6.     if (!szBuf) goto SafePrintf_END_PROCESS;   
  7.     va_start (pArgList,szFormat);   
  8.     nListCount+=Linux_Win_vsnprintf(szBuf+nListCount,   
  9.         nMaxLength-nListCount,szFormat,pArgList);   
  10.     va_end(pArgList);   
  11.     if(nListCount>(nMaxLength-1)) nListCount=nMaxLength-1;   
  12.     *(szBuf+nListCount)='\0';   
  13.   
  14. SafePrintf_END_PROCESS:   
  15.     return nListCount;   
  16. }   
 简单说,就是给出字符串缓冲区,还要给出一个缓冲区大小,我的函数保证不会打出去溢出,还有,就是不管缓冲区够不够,总之保证你最后得到一个'\0'的结束符。
你先看看吧,有问题再问。

本文转自 tonyxiaohome 51CTO博客,原文链接:http://blog.51cto.com/tonyxiaohome/291275 ,如需转载请自行联系原作者

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享:
开发与运维
使用钉钉扫一扫加入圈子
+ 订阅

集结各类场景实战经验,助你开发运维畅行无忧

其他文章