浮點型轉字符串是最常見的一個功能了,對於弱類型語言來說更是幾乎感覺不到。但現在問個問題?用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';
Tips:整型數字要加上0x30(十進制48)轉為其對應的ASCII字符,如:9+0x30='9'
4.3、為了說明前面有所提及的整數進位情況,從這里假設為保留一位小數再來演示一次;
例:0.98乘以100=>97,取最后一位得到 '7',7>=5,所以標記進位;下一個得到 '9',加上進位標記變成 10,所以取得數字 '0', 標記位繼續有效,因為這個是小數的最大一位了,所以這個進位要進到整數中去,這里要做好標記;
5.1、整數部分,假設最高支持5位,從高位到低位分別取出數字;
例: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忽略;
例:01000的真正字符串是'1'、'0'、'0'、'0',前的一個'0'忽略
6、拼裝小數部分、整數部分、小數點和可能的負號;
例:保留一位小數得到了 "-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 }