阿发伯的业余编程心得

导航

自己动手写C语言格式化输出函数(三)

    上接《自己动手写C语言格式化输出函数(二)》。 

    八、格式化浮点数(有关浮点数的数据定义和底层的数据转换函数见自己动手写C语言浮点数转换字符串函数》一文)。

 

  1 // 转换浮点数信息到浮点数记录fRec。参数:格式记录,格式方式标记,浮点数记录
  2 static void GetFloatRec(FormatRec *rec, INT flag, FloatRec *fRec)
  3 {
  4     EXTENDED value;
  5     if (rec->precision < 0)
  6         rec->precision = F_DEFDECIMALS;
  7     else if (rec->precision > F_MAXDECIMALS)
  8         rec->precision = F_MAXDECIMALS;
  9     if (rec->type == TYPE_LLONG)
 10     {
 11         value = *(PEXTENDED)rec->param;
 12         rec->param += TS_EXTENDED;
 13     }
 14     else
 15     {
 16         value = *(double*)rec->param;
 17         rec->param += TS_DOUBLE;
 18     }
 19     switch (flag)
 20     {
 21         case 0:        // %f
 22             FloatResolve(&value, F_MAXPRECISION, rec->precision, fRec);
 23             break;
 24         case 1:        // %e or %E
 25             FloatResolve(&value, rec->precision + 19999, fRec);
 26             break;
 27         case 2:        // %g or %G
 28             FloatResolve(&value, rec->precision, 9999, fRec);
 29     }
 30     if (fRec->negative)
 31         rec->negative = -1;
 32 }
 33 
 34 // 格式化小数字串。参数:缓冲区,格式记录,数字串,数字串长度。返回缓冲区尾偏移
 35 static LPSTR FormatDecimalA(LPSTR buffer, FormatRec *rec, LPCSTR str, INT strLen)
 36 {
 37     LPSTR p = buffer;
 38     INT spaces = rec->width - strLen;
 39     if (rec->negative)
 40     {
 41         spaces --;
 42         if (rec->left || rec->zero)
 43             *p ++ = (rec->negative == -1? CHAR_NEG : CHAR_POS);
 44     }
 45     if (rec->left == FALSE)
 46     {
 47         if (spaces > 0)
 48         {
 49             memset(p, rec->zero? CHAR_ZERO : CHAR_SPACE, spaces);
 50             p += spaces;
 51         }
 52         if (rec->negative && !rec->zero)
 53             *p ++ = (rec->negative == -1? CHAR_NEG : CHAR_POS);
 54     }
 55     memcpy(p, str, strLen);
 56     p += strLen;
 57     if (rec->left && spaces > 0)
 58     {
 59         memset(p, CHAR_SPACE, spaces);
 60         p += spaces;
 61     }
 62     return p;
 63 }
 64 
 65 #define    F_MAXEXPONENT    45
 66 #define    F_MINEXPONENT    -45
 67 
 68 // 输出指数字符串到buffer,返回指数字符串长度
 69 INT PutExponent(LPSTR buffer, CONST FloatRec *rec)
 70 {
 71     LPSTR p = buffer;
 72     INT e, exp = rec->digits[0]? rec->exponent - 1 : 0;
 73     *p ++ = rec->negative & 0x80'E' : 'e';
 74     if (exp < 0)
 75     {
 76         exp = -exp;
 77         *p ++ = '-';
 78     }
 79     else *p ++ = '+';
 80     if ((e = (exp / 1000)) != 0)
 81     {
 82         *p ++ = e + 0x30;
 83         exp %= 1000;
 84     }
 85     *p ++ = exp / 100 + 0x30;
 86     exp %= 100;
 87     *(PUSHORT)p = (((exp % 10) << 8) | (exp / 10)) + 0x3030;
 88     return (INT)(p - buffer + 2);
 89 }
 90 
 91 // 按浮点数记录信息转换为指数格式数字串。
 92 // 参数:缓冲区,浮点数记录,转换精度,是否强制小数位
 93 static INT FloatExponentA(LPSTR buffer, CONST FloatRec *rec, INT precision, BOOL decPoint)
 94 {
 95     LPSTR p = buffer;
 96     LPCSTR digits = rec->digits;
 97     if (rec->exponent > 0 || *digits)
 98         *p ++ = *digits ++;
 99     else
100         *p ++ = '0';
101     if (precision > 0 || decPoint)
102     {
103         for (*p ++ = '.'; precision > 0 && *digits; *p ++ = *digits ++, precision --);
104         for (; precision > 0; *p ++ = '0', precision --);
105     }
106     p += PutExponent(p, rec);
107     return (INT)(p - buffer);
108 }
109 
110 // 按浮点数记录信息转换为小数格式数字串。
111 // 参数:缓冲区,浮点数记录,转换精度,是否强制小数位
112 static INT FloatDecimalA(LPSTR buffer, CONST FloatRec *rec, INT precision, BOOL decPoint)
113 {
114     LPSTR p;
115     LPCSTR digits;
116     INT exp = rec->exponent;
117     if (exp > F_MAXEXPONENT || exp < F_MINEXPONENT)
118         return FloatExponentA(buffer, rec, precision, decPoint);
119     p = buffer;
120     digits = rec->digits;
121     if (exp > 0)
122     {
123         for (; exp > 0 && *digits; *p ++ = *digits ++, exp --);
124         for (; exp > 0; *p ++ = '0', exp --);
125         if (decPoint || precision > 0)
126             *p ++ = '.';
127     }
128     else
129     {
130         exp = -exp;
131         precision -= exp;
132         if (precision < 0)
133         {
134             exp += precision;
135             precision = 0;
136         }
137         *p ++ = '0';
138         if (exp > 0 || decPoint || precision > 0)
139         {
140             *p ++ = '.';
141             for (; exp > 0; *p ++ = '0', exp --);
142         }
143     }
144     for (; precision > 0 && *digits; *p ++ = *digits ++, precision --);
145     for (; precision > 0; *p ++ = '0', precision --);
146     return (INT)(p - buffer);
147 }
148 
149 // 浮点数格式化为小数串。参数:缓冲区,格式记录。返回缓冲区尾偏移
150 static LPSTR FormatFloatFA(LPSTR buffer, FormatRec *rec)
151 {
152     FloatRec fRec;
153     INT len;
154     CHAR tmp[F_MAXDECIMALS+48];
155 
156     GetFloatRec(rec, 0, &fRec);
157     if (fRec.digits[0] > '9')        // nan or inf
158         return FormatDecimalA(buffer, rec, fRec.digits, 3);
159     len = FloatDecimalA(tmp, &fRec, rec->precision, rec->decimals);
160     return FormatDecimalA(buffer, rec, tmp, len);
161 }
162 
163 // 浮点数格式化为指数串。参数:缓冲区,格式记录。返回缓冲区尾偏移
164 static LPSTR FormatFloatEA(LPSTR buffer, FormatRec *rec, CHAR expChar)
165 {
166     FloatRec fRec;
167     INT len;
168     CHAR tmp[F_MAXDECIMALS+8];
169 
170     GetFloatRec(rec, 1, &fRec);
171     if (fRec.digits[0] > '9')        // nan or inf
172         return FormatDecimalA(buffer, rec, fRec.digits, 3);
173     if (expChar == 'E')
174         fRec.negative |= 0x80;    // 高位置1,大写
175     len = FloatExponentA(tmp, &fRec, rec->precision, rec->decimals);
176     return FormatDecimalA(buffer, rec, tmp, len);
177 }
178 
179 // 浮点数格式化为小数串或者指数串。参数:缓冲区,格式记录。返回缓冲区尾偏移
180 static LPSTR FormatFloatGA(LPSTR buffer, FormatRec *rec, CHAR expChar)
181 {
182     FloatRec fRec;
183     INT len, precision;
184     CHAR tmp[F_MAXDECIMALS+48];
185 
186     GetFloatRec(rec, 2, &fRec);
187     if (fRec.digits[0] > '9')    // nan or inf
188         return FormatDecimalA(buffer, rec, fRec.digits, 3);
189     if (expChar == 'G')
190         fRec.negative |= 0x80;    // 高位置1,大写
191     if (fRec.exponent > rec->precision || fRec.exponent < -3)
192     {
193         precision = rec->decimals? rec->precision - 1 : lstrlenA(fRec.digits) - 1;
194         len = FloatExponentA(tmp, &fRec, precision, rec->decimals);
195     }
196     else
197     {
198         precision = rec->decimals? rec->precision - fRec.exponent : lstrlenA(fRec.digits) - fRec.exponent;
199         if (precision < 0) precision = 0;
200         len = FloatDecimalA(tmp, &fRec, precision, rec->decimals);
201     }
202 
203     return FormatDecimalA(buffer, rec, tmp, len);
204 }


    在sprintfA函数中,浮点数的格式化是最复杂的。浮点数有2种表现形式,即小数形式和指数形式,分别用"%f"和"%e"格式表示,另外还有个"%g"格式,这是个自动格式,即sprintfA通过分析后,自行决定采用哪种形式。

    在以小数形式的格式化中,对数据的格式化有个极限长度,不然,在扩展精度浮点数下,有些浮点数长度可达到近5000位,即使是双精度浮点数,最高长度也达300多。在printf系列函数中,这个极限长度随编译器不同而不同,有的将这个值定为100,有的定为单精度浮点数的最大表现形式,即38等,我在这里把它定为了正负45位,当数据超过这个极限长度,就自动采用指数形式来格式化数据了。

    在介绍sprintfA数据定义时就说过,由于sprintfA的可变参数部分没有参数原型供编译器对照,所以在输入浮点数参数时要注意与格式字符串中对应的浮点数精度匹配,32位编译器的浮点数缺省精度是64位双精度数,即使你给的参数变量是个单精度数,也会扩展为双精度数,如果参数变量是long double,而你使用的编译器支持80位扩展精度浮点数时,传递的是80位扩展精度数,否则也是双精度数,如果你给出一个整数,编译器是不会自动转换的。如果你在参数位置输入的是常数就更应该注意了,123,123L,123.0f,123.0,123.0L这几种常数形式是不同的(L也可是小写),分别是整数,长整数,单精度浮点数,双精度浮点数,扩展精度浮点数(如果编译器不支持,也是双精度数)。所以,在32位及以上编译器中格式%f和%lf是等同的,自然,在不支持扩展精度浮点数的编译器中,%llf(%Lf)也等同于%f。

    本文格式化浮点数时用到的FloatResolve函数以及有关数据定义见自己动手写C语言浮点数转换字符串函数》。

    有关sprintfA函数的介绍就全部完毕。文章代码没进行严格的测试。

 

    声明:本文代码主要供学习使用,如作其它用途,出问题慨不负责。

    水平有限,错误在所难免,欢迎指正和指导。邮箱地址:maozefa@hotmail.com

 

 

posted on 2011-12-24 10:09  阿发伯  阅读(1507)  评论(0编辑  收藏  举报