硬造的轮子趟过的坑--浮点型转字符串函数

浮点型转字符串是最常见的一个功能了,对于弱类型语言来说更是几乎感觉不到。但现在问个问题?用C语言写一个浮点数转字符串的函数,有多难呢?

一开始写这个函数的时候是大二的时候,那时候在学C51单片机,用到1602显示屏,就是下图这货,通常遇到的情况就是要想要在屏幕上显示整数或者浮点数,1602封装的字库里面接口规范里接收的是字符串,所以在写程序时必须先把整数和浮点数转换成字符串。当时我就找到好像说 itoa , ftoa 这样的方法,但是那两个方法需要使用 stdlib.h 库,而Keil C51 IDE里面没有提供这个库,网上找了很久也没有找到答案,于是就自己着手写了。后来才知道,在stdio.h里面有一个sprintf函数可以做这种转换的事情,这已经是一年多后毕业求职时在某家公司面试的时候被告知的。

下面开始介绍浮点型转字符串的基本思路:

1、判断是否为负数,若是负数则作标记,在最后字符串合成时加上负号“-”,并取其绝对值;

  例:-999.98,则设定负数标记位置1,并取其绝对值999.98

2、把浮点串拆分为整数部分和小数部分;

  例:999.98拆分成 999 和 0.98

3、先处理小数部分还是整数部分?答案是小数部分,因为小数部分可能会四舍五入进位,对整数部分造成影响;

4.1、小数部分,假设这里要保留两位小数,取最后一位数字是到哪位呢?实践证明答案是三位,如果取两位,很可能会得到不可思议的结果;

  例:0.98乘以100后得到的结果可能不是98而是97,而乘以1000后得到的结果可能不是980而是979

  因为计算机记录小数是不精确的,在运算时可能会有细小误差,所以我们要取多一位进行四舍五入

4.2、因为可能进位的原因,从最低位开始逐步获取数字,并使用进位标置判断更新数字;

  例:0.98乘以1000=>979,取最后一位数得到 ‘9‘,9>=5,所以标记进位;下一个得到 ‘7‘,加上进位标记变成 ‘8‘,复位进位标记;再下一个得到 ‘9‘;整理起来小数部分得到的字符串就是 ‘9‘、‘8‘;

4.3、为了说明前面有所提及的整数进位情况,从这里假设为保留一位小数再来演示一次;

  例:0.98乘以100=>97,取最后一位得到 ‘7‘,7>=5,所以标记进位;下一个得到 ‘9‘,加上进位标记变成 10,所以取得数字 ‘0‘, 标记位继续有效,因为这个是小数的最大一位了,所以这个进位要进到整数中去,这里要做好标记;

5.1、整数部分,假设最高支持5位,从高位到低位分别取出数字,并加上0x30(十进制48)转为其对应的ASCII字符;

  例:999取得 ‘0‘、‘0‘、‘9‘、‘9‘、‘9‘

5.2、整数部分,处理小数进位到整数来的标记,从最低到最高位逐级进位

  例:这里的 9 一直进位,直到最高一位从 0 变成了 1,‘0‘、‘0‘、‘9‘、‘9‘、‘9‘变成了 ‘0‘、‘1‘、‘0‘、‘0‘、‘0‘

5.3、整数部分,从高位到低位判断非0第一次出现的位置,定为整数部分的字符串,前面的0忽略;

  例:0100的真正字符串是‘1‘、‘0‘、‘0‘、‘0‘,前的一个‘0‘忽略

5.4、拼装小数部分、整数部分、小数点和可能的负号;

  例:保留一位小数得到了 "-1000.0"

陷阱总结:

1.小数部分一定要取到保留的下一位,即使看起来保留的下一位就是0,否则可能会 1.8 变成 "1.7" 而实际是 1.79...

2.小数部分不能直接向整数部分一样乘以一个数然后去掉前面的0,否则会使像 1.02 变成 "1.2",把小数中的前面的0吃掉了;

3.记得取出的数字要转成字符,更要注意大小比较时,数字要与数字相比较,字符要与字符相比较,不可混用,两者大小相差48;

4.留意每一步的进位,小数到整数部分的进位最容易被忽视;

如果没做,真的没想到一个如此基础的函数其间要处理问题是如此之多,很感谢那些算法大牛们为我们铺好一条条康庄大道,让我们在编程的世界里更加轻松地翱翔。下面是我一年前大学三年级时最后更新时用C语言写的实现代码,跟上面说的思路在细节上稍有顺序不同,可能看起来非常冗余,可能还有一些尚未发现的BUG,可能大家会有更高明的实现算法。还请多多交流。

  1 //浮点型转字符串
  2 void float2Str(double fda,char *pString,uint8 dNum){
  3     uint8 i;
  4     bit negative=0;    //负数标志位
  5     bit X999        = 0;        //小数部分四舍五入进位标志
  6     bit XtoZ        = 0;        //小数到整数的进位标志
  7     uint8 intLen=5;
  8     uint8 cdat[6]={0};        //分部分时的字符串
  9     uint8 whole[18]={0};    //整个数的字符串,其中留多一位为0x00
 10
 11     int ida;                //整数部分
 12     double dec;                //小数部分
 13
 14     if (fda < 0){                //若为负数取绝对值
 15         fda = -fda;
 16         negative = 1;
 17     }
 18     ida = (int) (fda) ;
 19     dec = fda - ida;
 20
 21 ///////////////////小数部分转换//////////////
 22     if (dNum >= 6)            //小数最多显示5位
 23         dNum = 5;
 24     switch (dNum +1){
 25         case 6:{
 26             cdat[5] = (char)  (((long) (dec *1000000l))%10);    //0.0000001位
 27             whole[15+ 6-dNum] = cdat[5] + 0x30;
 28             //四舍五入算法
 29             if (X999 == 1){
 30                 if (whole[15+ 6-dNum] < ‘9‘){        //小于9就加1
 31                     whole[15+ 6-dNum] += 1;
 32                     X999 = 0;
 33                 }else{            //否则继续进位,本位置0
 34                     whole[15+ 6-dNum] = ‘0‘;
 35                 }
 36             }
 37
 38             if ( dNum==5){
 39                 if (whole[15+ 6-dNum] >= ‘5‘)
 40                     X999 = 1;
 41                 whole[15+ 6-dNum] = 0x00;
 42             }
 43             //////////////////////////
 44
 45         }
 46         case 5:{
 47             cdat[4] = (char)  (((long) (dec *100000l))%10);        //0.000001位
 48             whole[15+ 5-dNum] = cdat[4] + 0x30;
 49             //四舍五入算法
 50             if (X999 == 1){
 51                 if (whole[15+ 5-dNum] < ‘9‘){        //小于9就加1
 52                     whole[15+ 5-dNum] += 1;
 53                     X999 = 0;
 54                 }else{            //否则继续进位,本位置0
 55                     whole[15+ 5-dNum]  = ‘0‘;
 56                 }
 57             }
 58
 59             if ( dNum==4){
 60                 if (whole[15+ 5-dNum] >= ‘5‘)
 61                     X999 = 1;
 62                 whole[15+ 5-dNum] = 0x00;
 63             }
 64             //////////////////////////
 65
 66         }
 67         case 4:{
 68             cdat[3] = (char)  (((long) (dec *10000l))%10);        //0.00001位
 69             whole[15+ 4-dNum] = cdat[3] + 0x30;
 70             //四舍五入算法
 71             if (X999 == 1){
 72                 if (whole[15+ 4-dNum] < 0x39){        //小于9就加1
 73                     whole[15+ 4-dNum] += 1;
 74                     X999 = 0;
 75                 }else{            //否则继续进位,本位置0
 76                     whole[15+ 4-dNum]  = ‘0‘;
 77                 }
 78             }
 79
 80             if ( dNum==3){
 81                 if (whole[15+ 4-dNum] >= ‘5‘)
 82                     X999 = 1;
 83                 whole[15+ 4-dNum] = 0x00;
 84             }
 85             //////////////////////////
 86
 87         }
 88         case 3:    {
 89             cdat[2] = (char)  (((long) (dec *1000l))%10);            //0.001位
 90             whole[15+ 3-dNum] = cdat[2] + 0x30;
 91             //四舍五入算法
 92             if (X999 == 1){
 93                 if (whole[15+ 3-dNum] < 0x39){        //小于9就加1
 94                     whole[15+ 3-dNum] += 1;
 95                     X999 = 0;
 96                 }else{            //否则继续进位,本位置0
 97                     whole[15+ 3-dNum]  = ‘0‘;
 98                 }
 99             }
100             if ( dNum==2){
101                 if (whole[15+ 3-dNum] >= ‘5‘)
102                     X999 = 1;
103                 whole[15+ 3-dNum] = 0x00;
104             }
105             //////////////////////////
106
107         }
108         case 2:{
109             cdat[1] = (char)  (((long) (dec *100l))%10);            //0.01位
110             whole[15+ 2-dNum] = cdat[1] + 0x30;
111             //四舍五入算法
112             if (X999 == 1)    {
113                 if (whole[15+ 2-dNum] < 0x39){        //小于9就加1
114                     whole[15+ 2-dNum] += 1;
115                     X999 = 0;
116                 }else{            //否则继续进位,本位置0
117                     whole[15+ 2-dNum]  = ‘0‘;
118                 }
119             }
120             if ( dNum==1){
121                 if (whole[15+ 2-dNum] >= ‘5‘)
122                     X999 = 1;
123                 whole[15+ 2-dNum] = 0x00;
124             }
125             //////////////////////////
126
127         }
128         case 1:{
129             cdat[0] = (char)  (((long) (dec *10l))%10);                //0.1位
130             whole[15+ 1-dNum] = cdat[0] + 0x30;
131             //四舍五入算法
132             if (X999 == 1){
133                 if (whole[15+ 1-dNum] < 0x39)
134                     whole[15+ 1-dNum] += 1;
135                 else{
136                     XtoZ = 1;
137                     whole[15+ 1-dNum] = ‘0‘;
138                 }
139                 X999 = 0;
140             }
141
142             if ( dNum==0){
143                 if (whole[15+ 1-dNum] >= ‘5‘)
144                     XtoZ = 1;
145                 whole[15+ 1-dNum] = 0x00;
146             }
147             /////////////////////////
148
149         }
150     }
151
152 /////////////////////添加小数点////////////////
153     whole[15 - dNum] = ‘.‘ ;
154
155 ///////////////////整数部分转换//////////////
156     cdat [0] = (char)(ida / 10000 ) ;
157     cdat [1] = (char)((ida % 10000) /1000);
158     cdat [2] = (char)((ida % 1000) /100);
159     cdat [3] = (char)((ida % 100) /10);
160     cdat [4] = (char)((ida % 10) /1);
161     for (i=0;i<5;i++){                        //转换成ASCII码
162         cdat[i] = cdat[i] + 48;
163     }
164
165     //四舍五入算法,整数部分(未完)
166     if (XtoZ == 1){
167         if (cdat[4] < ‘9‘){                //个位小于9
168             cdat[4] += 1;
169         }else{
170             cdat[4] = ‘0‘;
171             if (cdat[3] < ‘9‘){            //十位小于9
172                 cdat[3] += 1;
173             }else{
174                 cdat[3] = ‘0‘;
175                 if (cdat[2] < ‘9‘){        //百位小于9
176                     cdat[2] += 1;
177                 }else{
178                     cdat[2] = ‘0‘;
179                     if (cdat[1] < ‘9‘){    //千位小于9
180                         cdat[1] += 1;
181                     }else{
182                         cdat[1] = ‘0‘;
183                         cdat[0] += 1;        //万位加1
184                     }
185                 }
186             }
187         }
188         XtoZ = 0;
189     }
190
191     ////////////////////////////////////////////////////
192     if (cdat[0] == ‘0‘){
193         intLen = 4;
194         if (cdat[1] == ‘0‘){
195             intLen = 3;
196             if (cdat[2] == ‘0‘){
197                 intLen = 2;
198                 if (cdat[3] == ‘0‘)
199                     intLen = 1;
200             }
201         }
202     }
203
204     for (i=0;i<5;i++){
205         whole[10 + i - dNum] = cdat[i];
206     }
207 ///////////////////////拼合符点数/////////////////////////////////
208     if (negative == 1){
209         whole [ 14 - intLen - dNum] = ‘-‘;
210         for ( i=(14 - intLen - dNum) ;i<19; i++){
211             *pString = whole[i];
212             pString ++;
213         }
214     }else{
215         for ( i=(15 - intLen - dNum) ;i<19; i++){
216             *pString = whole[i];
217             pString ++;
218         }
219     }
220
221 }
时间: 2024-08-06 15:39:55

硬造的轮子趟过的坑--浮点型转字符串函数的相关文章

微信支付趟过的坑

微信支付趟过的坑 标签: 微信支付 2015-01-21 15:49 25791人阅读 评论(30) 收藏 举报  分类: 微信公众号开发 版权声明:本文为博主原创文章,未经博主允许不得转载. 这段时间在做微信支付开发,在公司的公众号审批下来后,我这边的测试用例也已经开发完毕,于是拿着具体的数据来调试了,大段大段的代码就不贴了,demo里有,这里就说说调试过程中遇到的坑. 第一坑:redirect_url参数错误.因为我选择的的“JS API”支付,这种支付需要网页授权,先获取code,再拿co

我厌倦了 Redux,那就造个轮子 Rectx:第三集

仓库:215566435/rectx 前言 麻烦快去我的仓库里面喷: 老子学不动了,求不要更新. 呵呵,你没想到吧,这玩意儿竟然有第三集!我靠,我自己都没想到,让我们悄悄的回顾一下前两集完全没想到,竟然会有第二集! 我厌倦了 Redux,那就造个轮子 Rectx 第二集: immutable 痛点分析 第一集在这里:我厌倦了Redux,那就造个轮子:Rectx 算了,我都懒得写了,自己看吧,当然不看也无所谓,正式开始. 新的 Rectx 有什么不同? a light-weight state m

Oracle坑之-空字符串与NULL

空字符串与NULL 首先有如下代码 SELECT * FROM Pdc_DataDomain DD INNER JOIN Pdc_DD_Table DDT ON DD.DataDomainID = DDT.DataDomainID AND DD.ApplicationDBID = '3e7c6764d73f4c7786c99e5b72eb6912' AND DDT.TableName <> '#' AND NOT EXISTS (SELECT * FROM pdc_Application_3e

造新轮子啦,让pytesser支持3.x啦~

http://www.songluyi.com/%E9%80%A0%E6%96%B0%E8%BD%AE%E5%AD%90%E5%95%A6%EF%BC%8C%E8%AE%A9pytesser%E6%94%AF%E6%8C%813-x%E5%95%A6/ 0x00 前言 曾经用2.x版本的Python写过一个小小的验证码识别小程序,不到三十行,用的是pytesser 但是pytesser仅仅支持2.x版本,因此我们需要将这个轮子改写为3.x,方便我们调用. (等不及的小伙伴可以直接pip inst

我为什么要造一个轮子——GNova开发小计

GNova最初叫做CSystem,是作为系统库来设计的. 我最初学习C++是在大学时期的专业课,学生时代也曾用C++写过一些算法,但项目和比赛一般是用C#和Java完成的.工作之后,由于项目需要,我们的业务平台需要使用C++进行编写.在当时,我总是认为C++的标准库SL不如Java的lang包和.Net的System库好用,再加上当时接触到了Qt的Core模块,以及项目合作单位自己的基础库封装,于是我渐渐萌生了编写一套属于自己的好用的基础库的想法,CSystem也就应运而生. 现在看来,当时的想

ExtJS 折线图趟过的坑

问题: 1.根据条件检索后绘制折线图,之前的坐标没有清除如图 解决方案: 在绘制之前,清空坐票: leftLine.surface.removeAll(); leftLine.redraw(false); 完整代码如下 storeBar.load({ params: { SDate: bTime, EDate: eTime, ResourceId: resourceid }, callback: function (records, operation, success) { var axesI

踩坑正则表达式-匹配字符串中的整数和小数

首先,明白几个边界匹配器字符的含义. ^:整行字符串的开头位置 \A:整段字符串的开头位置 $:整行字符串的结尾位置 \z:整段字符串的结尾位置 关于\b和\B,官方原版的解释中是A word boundary和A non-word boundary,没有详细的解释,反正我是没看明白,自己测试一下,匹配字符串:6lo.ve,正则表达式:\B\D.结果共三处,包括l.o.v 三个字母. 那么我们是不是可以认为\B\D表示:匹配一个任意非数字的字符,并且字符的前一位是字母或者数字,此时这个单词不是边

Hybrid App Development: 二、关于造轮子以及在Xcode iOS应用开发中加入Cordova

转载请注明出处:http://www.cnblogs.com/xdxer/p/4111552.html [ctrl+左键点击图片可以查看原图] 在上一篇关于Hybrid App Development的文章中,我讨论了一下在iOS下UIWebView的使用方法.但是光使用一个UIWebView所提供的功能还是完全不能满足我们的需求.   关于造轮子的思考: 在UIKit中的UIWebView虽然已经提供了很多功能了,比如JavaScript和Objc之间的通信.但是考虑到一个问题,如果在Hybr

【造轮子】MFC实现BlockingQueue

最近任务需要在MFC下做多线程生产者消费者模式的东西,我找了半天貌似MFC没有类似Java里面BlockingQueue那样的工具(也许是我手残没找到). 网上好像也有很多大佬去实现这个.但是我没仔细去找,看了看一些资料就想着造个轮子玩玩. 实现如下: 主要是利用CCriticalSection保护内置的std::list,然后用CEvent来实现生产者消费者的同步. 参考资料:http://stackoverflow.com/questions/6683356/c-templated-prod