]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/uclibc/lib/contrib/uclibc/libc/stdio/_fpmaxtostr.c
Inital import
[l4.git] / l4 / pkg / uclibc / lib / contrib / uclibc / libc / stdio / _fpmaxtostr.c
1 /* Copyright (C) 2004       Manuel Novoa III    <mjn3@codepoet.org>
2  *
3  * GNU Library General Public License (LGPL) version 2 or later.
4  *
5  * Dedicated to Toni.  See uClibc/DEDICATION.mjn3 for details.
6  */
7
8 #include "_stdio.h"
9 #include <printf.h>
10 #include <float.h>
11 #include <locale.h>
12 #include <bits/uClibc_fpmax.h>
13
14
15 typedef size_t (__fp_outfunc_t)(FILE *fp, intptr_t type, intptr_t len,
16                                                                 intptr_t buf);
17
18
19 /* Copyright (C) 2000, 2001, 2003      Manuel Novoa III
20  *
21  * Function:
22  *
23  *     ssize_t _fpmaxtostr(FILE * fp, __fpmax_t x, struct printf_info *info,
24  *                         __fp_outfunc_t fp_outfunc);
25  *
26  * This is derived from the old _dtostr, whic I wrote for uClibc to provide
27  * floating point support for the printf functions.  It handles +/- infinity,
28  * nan, and signed 0 assuming you have ieee arithmetic.  It also now handles
29  * digit grouping (for the uClibc supported locales) and hexadecimal float
30  * notation.  Finally, via the fp_outfunc parameter, it now supports wide
31  * output.
32  *
33  * Notes:
34  *
35  * At most DECIMAL_DIG significant digits are kept.  Any trailing digits
36  * are treated as 0 as they are really just the results of rounding noise
37  * anyway.  If you want to do better, use an arbitary precision arithmetic
38  * package.  ;-)
39  *
40  * It should also be fairly portable, as no assumptions are made about the
41  * bit-layout of doubles.  Of course, that does make it less efficient than
42  * it could be.
43  *
44  */
45
46 /*****************************************************************************/
47 /* Don't change anything that follows unless you know what you're doing.     */
48 /*****************************************************************************/
49 /* Fairly portable nan check.  Bitwise for i386 generated larger code.
50  * If you have a better version, comment this out.
51  */
52 #define isnan(x)             ((x) != (x))
53
54 /* Without seminumerical functions to examine the sign bit, this is
55  * about the best we can do to test for '-0'.
56  */
57 #define zeroisnegative(x)    ((1./(x)) < 0)
58
59 /*****************************************************************************/
60 /* Don't change anything that follows peroid!!!  ;-)                         */
61 /*****************************************************************************/
62 #ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
63 #if FLT_RADIX != 2
64 #error FLT_RADIX != 2 is not currently supported
65 #endif
66 #endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
67
68 #define NUM_HEX_DIGITS      ((FPMAX_MANT_DIG + 3)/ 4)
69
70 /* WARNING: Adjust _fp_out_wide() below if this changes! */
71 /* With 32 bit ints, we can get 9 decimal digits per block. */
72 #define DIGITS_PER_BLOCK     9
73 #define HEX_DIGITS_PER_BLOCK 8
74
75 /* Maximum number of subcases to output double is...
76  *  0 - sign
77  *  1 - padding and initial digit
78  *  2 - digits left of the radix
79  *  3 - 0s left of the radix        or   radix
80  *  4 - radix                       or   digits right of the radix
81  *  5 - 0s right of the radix
82  *  6 - exponent
83  *  7 - trailing space padding
84  * although not all cases may occur.
85  */
86 #define MAX_CALLS 8
87
88 /*****************************************************************************/
89
90 #define NUM_DIGIT_BLOCKS   ((DECIMAL_DIG+DIGITS_PER_BLOCK-1)/DIGITS_PER_BLOCK)
91 #define NUM_HEX_DIGIT_BLOCKS \
92    ((NUM_HEX_DIGITS+HEX_DIGITS_PER_BLOCK-1)/HEX_DIGITS_PER_BLOCK)
93
94 /* WARNING: Adjust _fp_out_wide() below if this changes! */
95
96 /* extra space for '-', '.', 'e+###', and nul */
97 #define BUF_SIZE  ( 3 + NUM_DIGIT_BLOCKS * DIGITS_PER_BLOCK )
98
99 /*****************************************************************************/
100
101 static const char fmt[] = "inf\0INF\0nan\0NAN\0.\0,";
102
103 #define INF_OFFSET        0             /* must be 1st */
104 #define NAN_OFFSET        8             /* must be 2nd.. see hex sign handling */
105 #define DECPT_OFFSET     16
106 #define THOUSEP_OFFSET   18
107
108 #define EMPTY_STRING_OFFSET 3
109
110 /*****************************************************************************/
111 #if FPMAX_MAX_10_EXP < -FPMAX_MIN_10_EXP
112 #error scaling code can not handle FPMAX_MAX_10_EXP < -FPMAX_MIN_10_EXP
113 #endif
114
115 static const __fpmax_t exp10_table[] =
116 {
117         1e1L, 1e2L, 1e4L, 1e8L, 1e16L, 1e32L,   /* floats */
118 #if FPMAX_MAX_10_EXP < 32
119 #error unsupported FPMAX_MAX_10_EXP (< 32).  ANSI/ISO C requires >= 37.
120 #endif
121 #if FPMAX_MAX_10_EXP >= 64
122         1e64L,
123 #endif
124 #if FPMAX_MAX_10_EXP >= 128
125         1e128L,
126 #endif
127 #if FPMAX_MAX_10_EXP >= 256
128         1e256L,
129 #endif
130 #if FPMAX_MAX_10_EXP >= 512
131         1e512L,
132 #endif
133 #if FPMAX_MAX_10_EXP >= 1024
134         1e1024L,
135 #endif
136 #if FPMAX_MAX_10_EXP >= 2048
137         1e2048L,
138 #endif
139 #if FPMAX_MAX_10_EXP >= 4096
140         1e4096L
141 #endif
142 #if FPMAX_MAX_10_EXP >= 8192
143 #error unsupported FPMAX_MAX_10_EXP.  please increase table
144 #endif
145 };
146
147 #define EXP10_TABLE_SIZE     (sizeof(exp10_table)/sizeof(exp10_table[0]))
148 #define EXP10_TABLE_MAX      (1U<<(EXP10_TABLE_SIZE-1))
149
150 /*****************************************************************************/
151 #ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
152
153 #if FLT_RADIX != 2
154 #error FLT_RADIX != 2 is not currently supported
155 #endif
156
157 #if FPMAX_MAX_EXP < -FPMAX_MIN_EXP
158 #error scaling code can not handle FPMAX_MAX_EXP < -FPMAX_MIN_EXP
159 #endif
160
161 static const __fpmax_t exp16_table[] = {
162         0x1.0p4L, 0x1.0p8L, 0x1.0p16L, 0x1.0p32L, 0x1.0p64L,
163 #if FPMAX_MAX_EXP >= 128
164         0x1.0p128L,
165 #endif
166 #if FPMAX_MAX_EXP >= 256
167         0x1.0p256L,
168 #endif
169 #if FPMAX_MAX_EXP >= 512
170         0x1.0p512L,
171 #endif
172 #if FPMAX_MAX_EXP >= 1024
173         0x1.0p1024L,
174 #endif
175 #if FPMAX_MAX_EXP >= 2048
176         0x1.0p2048L,
177 #endif
178 #if FPMAX_MAX_EXP >= 4096
179         0x1.0p4096L,
180 #endif
181 #if FPMAX_MAX_EXP >= 8192
182         0x1.0p8192L,
183 #endif
184 #if FPMAX_MAX_EXP >= 16384
185         0x1.0p16384L
186 #endif
187 #if FPMAX_MAX_EXP >= 32768
188 #error unsupported FPMAX_MAX_EXP.  please increase table
189 #endif
190 };
191
192 #define EXP16_TABLE_SIZE     (sizeof(exp16_table)/sizeof(exp16_table[0]))
193 #define EXP16_TABLE_MAX      (1U<<(EXP16_TABLE_SIZE-1))
194
195 #endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
196 /*****************************************************************************/
197
198 #define FPO_ZERO_PAD    (0x80 | '0')
199 #define FPO_STR_WIDTH   (0x80 | ' ');
200 #define FPO_STR_PREC    'p'
201
202 ssize_t _fpmaxtostr(FILE * fp, __fpmax_t x, struct printf_info *info,
203                                         __fp_outfunc_t fp_outfunc) attribute_hidden;
204 ssize_t _fpmaxtostr(FILE * fp, __fpmax_t x, struct printf_info *info,
205                                         __fp_outfunc_t fp_outfunc)
206 {
207 #ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
208         __fpmax_t lower_bnd;
209         __fpmax_t upper_bnd = 1e9;
210 #endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
211 #ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
212         uint_fast32_t base = 10;
213         const __fpmax_t *power_table;
214         int dpb = DIGITS_PER_BLOCK;
215         int ndb = NUM_DIGIT_BLOCKS;
216         int nd = DECIMAL_DIG;
217         int sufficient_precision = 0;
218 #endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
219 #ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__
220         int num_groups = 0;
221         int initial_group;         /* This does not need to be initialized. */
222         int tslen;                         /* This does not need to be initialized. */
223         int nblk2;                         /* This does not need to be initialized. */
224         const char *ts;            /* This does not need to be initialized. */
225 #endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */
226         int round, o_exp;
227         int exp;
228         int width, preci;
229         int cnt;
230         char *s;
231         char *e;
232         intptr_t pc_fwi[3*MAX_CALLS];
233         intptr_t *ppc;
234         intptr_t *ppc_last;
235 #ifdef __UCLIBC_MJN3_ONLY__
236 #warning TODO: The size of exp_buf[] should really be determined by the float constants.
237 #endif /* __UCLIBC_MJN3_ONLY__ */
238         char exp_buf[16];
239         char buf[BUF_SIZE];
240         char sign_str[6];                       /* Last 2 are for 1st digit + nul. */
241         char o_mode;
242         char mode;
243
244
245         width = info->width;
246         preci = info->prec;
247         mode = info->spec;
248
249         *exp_buf = 'e';
250         if ((mode|0x20) == 'a') {
251 #ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
252                 *exp_buf = 'p';
253                 if (preci < 0) {
254                         preci = NUM_HEX_DIGITS;
255                         sufficient_precision = 1;
256                 }
257 #else
258                 mode += ('g' - 'a');
259 #endif
260         }
261
262         if (preci < 0) {
263                 preci = 6;
264         }
265
266         *sign_str = '\0';
267         if (PRINT_INFO_FLAG_VAL(info,showsign)) {
268                 *sign_str = '+';
269         } else if (PRINT_INFO_FLAG_VAL(info,space)) {
270                 *sign_str = ' ';
271         }
272
273         *(sign_str+1) = 0;
274         pc_fwi[5] = INF_OFFSET;
275         if (isnan(x)) {                         /* First, check for nan. */
276                 pc_fwi[5] = NAN_OFFSET;
277                 goto INF_NAN;
278         }
279
280         if (x == 0) {                           /* Handle 0 now to avoid false positive. */
281 #ifdef __UCLIBC_HAVE_SIGNED_ZERO__
282                 if (zeroisnegative(x)) { /* Handle 'signed' zero. */
283                         *sign_str = '-';
284                 }
285 #endif /* __UCLIBC_HAVE_SIGNED_ZERO__ */
286                 exp = -1;
287                 goto GENERATE_DIGITS;
288         }
289
290         if (x < 0) {                            /* Convert negatives to positives. */
291                 *sign_str = '-';
292                 x = -x;
293         }
294
295         if (__FPMAX_ZERO_OR_INF_CHECK(x)) {     /* Inf since zero handled above. */
296         INF_NAN:
297                 info->pad = ' ';
298                 ppc = pc_fwi + 6;
299                 pc_fwi[3] = FPO_STR_PREC;
300                 pc_fwi[4] = 3;
301                 if (mode < 'a') {
302                         pc_fwi[5] += 4;
303                 }
304                 pc_fwi[5] = (intptr_t)(fmt + pc_fwi[5]);
305                 goto EXIT_SPECIAL;
306         }
307
308         {
309                 int i, j;
310 #ifdef __UCLIBC_MJN3_ONLY__
311 #warning TODO: Clean up defines when hexadecimal float notation is unsupported.
312 #endif /* __UCLIBC_MJN3_ONLY__ */
313
314 #ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
315
316                 if ((mode|0x20) == 'a') {
317                         lower_bnd = 0x1.0p31L;
318                         upper_bnd = 0x1.0p32L;
319                         power_table = exp16_table;
320                         exp = HEX_DIGITS_PER_BLOCK - 1;
321                         i = EXP16_TABLE_SIZE;
322                         j = EXP16_TABLE_MAX;
323                         dpb = HEX_DIGITS_PER_BLOCK;
324                         ndb = NUM_HEX_DIGIT_BLOCKS;
325                         nd = NUM_HEX_DIGITS;
326                         base = 16;
327                 } else {
328                         lower_bnd = 1e8;
329                         /*              upper_bnd = 1e9; */
330                         power_table = exp10_table;
331                         exp = DIGITS_PER_BLOCK - 1;
332                         i = EXP10_TABLE_SIZE;
333                         j = EXP10_TABLE_MAX;
334                         /*              dpb = DIGITS_PER_BLOCK; */
335                         /*              ndb = NUM_DIGIT_BLOCKS; */
336                         /*              base = 10; */
337                 }
338
339
340
341 #else  /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
342
343 #define lower_bnd    1e8
344 #define upper_bnd    1e9
345 #define power_table  exp10_table
346 #define dpb          DIGITS_PER_BLOCK
347 #define base         10
348 #define ndb          NUM_DIGIT_BLOCKS
349 #define nd           DECIMAL_DIG
350
351                 exp = DIGITS_PER_BLOCK - 1;
352                 i = EXP10_TABLE_SIZE;
353                 j = EXP10_TABLE_MAX;
354
355 #endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
356
357                 {
358                         int exp_neg = 0;
359                         if (x < lower_bnd) { /* Do we need to scale up or down? */
360                                 exp_neg = 1;
361                         }
362
363                         do {
364                                 --i;
365                                 if (exp_neg) {
366                                         if (x * power_table[i] < upper_bnd) {
367                                                 x *= power_table[i];
368                                                 exp -= j;
369                                         }
370                                 } else {
371                                         if (x / power_table[i] >= lower_bnd) {
372                                                 x /= power_table[i];
373                                                 exp += j;
374                                         }
375                                 }
376                                 j >>= 1;
377                         } while (i);
378                 }
379         }
380         if (x >= upper_bnd) {           /* Handle bad rounding case. */
381                 x /= power_table[0];
382                 ++exp;
383         }
384         assert(x < upper_bnd);
385
386  GENERATE_DIGITS:
387         {
388                 int i, j;
389                 s = buf + 2;                    /* Leave space for '\0' and '0'. */
390                 i = 0;
391                 do {
392                         uint_fast32_t digit_block = (uint_fast32_t) x;
393                         assert(digit_block < upper_bnd);
394 #ifdef __UCLIBC_MJN3_ONLY__
395 #warning CONSIDER: Can rounding be a problem?
396 #endif /* __UCLIBC_MJN3_ONLY__ */
397                         x = (x - digit_block) * upper_bnd;
398                         s += dpb;
399                         j = 0;
400                         do {
401                                 s[- ++j] = '0' + (digit_block % base);
402                                 digit_block /= base;
403                         } while (j < dpb);
404                 } while (++i < ndb);
405         }
406
407         /*************************************************************************/
408
409         if (mode < 'a') {
410                 *exp_buf -= ('a' - 'A'); /* e->E and p->P */
411                 mode += ('a' - 'A');
412         }
413
414         o_mode = mode;
415         if ((mode == 'g') && (preci > 0)){
416                 --preci;
417         }
418         round = preci;
419
420         if (mode == 'f') {
421                 round += exp;
422                 if (round < -1) {
423                         memset(buf, '0', DECIMAL_DIG); /* OK, since 'f' -> decimal case. */
424                     exp = -1;
425                     round = -1;
426                 }
427         }
428
429         s = buf;
430         *s++ = 0;                                       /* Terminator for rounding and 0-triming. */
431         *s = '0';                                       /* Space to round. */
432
433         {
434                 int i;
435                 i = 0;
436                 e = s + nd + 1;
437                 if (round < nd) {
438                         e = s + round + 2;
439                         if (*e >= '0' + (base/2)) {     /* NOTE: We always round away from 0! */
440                                 i = 1;
441                         }
442                 }
443
444                 do {                       /* Handle rounding and trim trailing 0s. */
445                         *--e += i;                      /* Add the carry. */
446                 } while ((*e == '0') || (*e > '0' - 1 + base));
447         }
448
449 #ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
450         if ((mode|0x20) == 'a') {
451                 char *q;
452
453                 for (q = e ; *q ; --q) {
454                         if (*q > '9') {
455                                 *q += (*exp_buf - ('p' - 'a') - '9' - 1);
456                         }
457                 }
458
459                 if (e > s) {
460                         exp *= 4;                       /* Change from base 16 to base 2. */
461                 }
462         }
463 #endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
464
465         o_exp = exp;
466         if (e <= s) {                           /* We carried into an extra digit. */
467                 ++o_exp;
468                 e = s;                                  /* Needed if all 0s. */
469         } else {
470                 ++s;
471         }
472         *++e = 0;                                       /* Terminating nul char. */
473
474         if ((mode == 'g') && ((o_exp >= -4) && (o_exp <= round))) {
475                 mode = 'f';
476                 preci = round - o_exp;
477         }
478
479         exp = o_exp;
480         if (mode != 'f') {
481                 o_exp = 0;
482         }
483
484         if (o_exp < 0) {                        /* Exponent is < 0, so */
485                 *--s = '0';                             /* fake the first 0 digit. */
486         }
487
488         pc_fwi[3] = FPO_ZERO_PAD;
489         pc_fwi[4] = 1;
490         pc_fwi[5] = (intptr_t)(sign_str + 4);
491         sign_str[4] = *s++;
492         sign_str[5] = 0;
493         ppc = pc_fwi + 6;
494
495         {
496                 int i = e - s;                  /* Total digits is 'i'. */
497                 if (o_exp >= 0) {
498 #ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__
499
500                         const char *p;
501
502                         if (PRINT_INFO_FLAG_VAL(info,group)
503                          && *(p = __UCLIBC_CURLOCALE->grouping)
504                         ) {
505                                 int nblk1;
506
507                                 nblk2 = nblk1 = *p;
508                                 if (*++p) {
509                                         nblk2 = *p;
510                                         assert(!*++p);
511                                 }
512
513                                 if (o_exp >= nblk1) {
514                                         num_groups = (o_exp - nblk1) / nblk2 + 1;
515                                         initial_group = (o_exp - nblk1) % nblk2;
516
517 #ifdef __UCLIBC_HAS_WCHAR__
518                                         if (PRINT_INFO_FLAG_VAL(info,wide)) {
519                                                 /* _fp_out_wide() will fix this up. */
520                                                 ts = fmt + THOUSEP_OFFSET;
521                                                 tslen = 1;
522                                         } else {
523 #endif /* __UCLIBC_HAS_WCHAR__ */
524                                                 ts = __UCLIBC_CURLOCALE->thousands_sep;
525                                                 tslen = __UCLIBC_CURLOCALE->thousands_sep_len;
526 #ifdef __UCLIBC_HAS_WCHAR__
527                                         }
528 #endif /* __UCLIBC_HAS_WCHAR__ */
529
530                                         width -= num_groups * tslen;
531                                 }
532                         }
533
534
535 #endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */
536                         ppc[0] = FPO_STR_PREC;
537                         ppc[2] = (intptr_t)(s);
538                         if (o_exp >= i) {               /* all digit(s) left of decimal */
539                                 ppc[1] = i;
540                                 ppc += 3;
541                                 o_exp -= i;
542                                 i = 0;
543                                 if (o_exp>0) {          /* have 0s left of decimal */
544                                         ppc[0] = FPO_ZERO_PAD;
545                                         ppc[1] = o_exp;
546                                         ppc[2] = (intptr_t)(fmt + EMPTY_STRING_OFFSET);
547                                         ppc += 3;
548                                 }
549                         } else if (o_exp > 0) { /* decimal between digits */
550                                 ppc[1] = o_exp;
551                                 ppc += 3;
552                                 s += o_exp;
553                                 i -= o_exp;
554                         }
555                         o_exp = -1;
556                 }
557
558                 if (PRINT_INFO_FLAG_VAL(info,alt)
559                         || (i)
560                         || ((o_mode != 'g')
561 #ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
562                                 && (o_mode != 'a')
563 #endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
564                                 && (preci > 0))
565                         ) {
566                         ppc[0] = FPO_STR_PREC;
567 #ifdef __LOCALE_C_ONLY
568                         ppc[1] = 1;
569                         ppc[2] = (intptr_t)(fmt + DECPT_OFFSET);
570 #else  /* __LOCALE_C_ONLY */
571 #ifdef __UCLIBC_HAS_WCHAR__
572                         if (PRINT_INFO_FLAG_VAL(info,wide)) {
573                                 /* _fp_out_wide() will fix this up. */
574                                 ppc[1] = 1;
575                                 ppc[2] = (intptr_t)(fmt + DECPT_OFFSET);
576                         } else {
577 #endif /* __UCLIBC_HAS_WCHAR__ */
578                                 ppc[1] = __UCLIBC_CURLOCALE->decimal_point_len;
579                                 ppc[2] = (intptr_t)(__UCLIBC_CURLOCALE->decimal_point);
580 #ifdef __UCLIBC_HAS_WCHAR__
581                         }
582 #endif /* __UCLIBC_HAS_WCHAR__ */
583 #endif /* __LOCALE_C_ONLY */
584                         ppc += 3;
585                 }
586
587                 if (++o_exp < 0) {                      /* Have 0s right of decimal. */
588                         ppc[0] = FPO_ZERO_PAD;
589                         ppc[1] = -o_exp;
590                         ppc[2] = (intptr_t)(fmt + EMPTY_STRING_OFFSET);
591                         ppc += 3;
592                 }
593                 if (i) {                                        /* Have digit(s) right of decimal. */
594                         ppc[0] = FPO_STR_PREC;
595                         ppc[1] = i;
596                         ppc[2] = (intptr_t)(s);
597                         ppc += 3;
598                 }
599
600                 if (((o_mode != 'g') || PRINT_INFO_FLAG_VAL(info,alt))
601 #ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
602                         && !sufficient_precision
603 #endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
604                         ) {
605                         i -= o_exp;
606                         if (i < preci) {                /* Have 0s right of digits. */
607                                 i = preci - i;
608                                 ppc[0] = FPO_ZERO_PAD;
609                                 ppc[1] = i;
610                                 ppc[2] = (intptr_t)(fmt + EMPTY_STRING_OFFSET);
611                                 ppc += 3;
612                         }
613                 }
614         }
615
616         /* Build exponent string. */
617         if (mode != 'f') {
618                 char *p = exp_buf + sizeof(exp_buf);
619                 int j;
620                 char exp_char = *exp_buf;
621                 char exp_sign = '+';
622 #ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
623                 int min_exp_dig_plus_2 = ((o_mode != 'a') ? (2+2) : (2+1));
624 #else  /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
625 #define min_exp_dig_plus_2  (2+2)
626 #endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
627
628                 if (exp < 0) {
629                         exp_sign = '-';
630                         exp = -exp;
631                 }
632
633                 *--p = 0;                       /* nul-terminate */
634                 j = 2;                          /* Count exp_char and exp_sign. */
635                 do {
636                         *--p = '0' + (exp % 10);
637                         exp /= 10;
638                 } while ((++j < min_exp_dig_plus_2) || exp); /* char+sign+mindigits */
639                 *--p = exp_sign;
640                 *--p = exp_char;
641
642                 ppc[0] = FPO_STR_PREC;
643                 ppc[1] = j;
644                 ppc[2] = (intptr_t)(p);
645                 ppc += 3;
646         }
647
648  EXIT_SPECIAL:
649         {
650                 int i;
651                 ppc_last = ppc;
652                 ppc = pc_fwi + 4;        /* Need width fields starting with second. */
653                 do {
654                         width -= *ppc;
655                         ppc += 3;
656                 } while (ppc < ppc_last);
657
658                 ppc = pc_fwi;
659                 ppc[0] = FPO_STR_WIDTH;
660                 ppc[1] = i = ((*sign_str) != 0);
661                 ppc[2] = (intptr_t) sign_str;
662
663 #ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
664                 if (((mode|0x20) == 'a') && (pc_fwi[3] >= 16)) { /* Hex sign handling. */
665                         /* Hex and not inf or nan, so prefix with 0x. */
666                         char *h = sign_str + i;
667                         *h = '0';
668                         *++h = 'x' - 'p' + *exp_buf;
669                         *++h = 0;
670                         ppc[1] = (i += 2);
671                 }
672 #endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
673
674                 if ((width -= i) > 0) {
675                         if (PRINT_INFO_FLAG_VAL(info,left)) { /* Left-justified. */
676                                 ppc_last[0] = FPO_STR_WIDTH;
677                                 ppc_last[1] = width;
678                                 ppc_last[2] = (intptr_t)(fmt + EMPTY_STRING_OFFSET);
679                                 ppc_last += 3;
680                         } else if (info->pad == '0') { /* 0 padding */
681                                 ppc[4] += width;        /* Pad second field. */
682                         } else {
683                                 ppc[1] += width;        /* Pad first (sign) field. */
684                         }
685                 }
686
687                 cnt = 0;
688         }
689
690         do {
691 #ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__
692
693                 if ((ppc == pc_fwi + 6) && num_groups) {
694                         const char *gp = (const char *) ppc[2];
695                         int len = ppc[1];
696                         int blk = initial_group;
697
698                         cnt += num_groups * tslen; /* Adjust count now for sep chars. */
699
700 /*                      __printf("\n"); */
701                         do {
702                                 if (!blk) {             /* Initial group could be 0 digits long! */
703                                         blk = nblk2;
704                                 } else if (len >= blk) { /* Enough digits for a group. */
705 /*                                      __printf("norm:  len=%d blk=%d  \"%.*s\"\n", len, blk, blk, gp); */
706                                         if (fp_outfunc(fp, *ppc, blk, (intptr_t) gp) != blk) {
707                                                 return -1;
708                                         }
709                                         assert(gp);
710                                         if (*gp) {
711                                                 gp += blk;
712                                         }
713                                         len -= blk;
714                                 } else {                /* Transition to 0s. */
715 /*                                      __printf("trans: len=%d blk=%d  \"%.*s\"\n", len, blk, len, gp); */
716                                         if (len) {
717 /*                                              __printf("len\n"); */
718                                                 if (fp_outfunc(fp, *ppc, len, (intptr_t) gp) != len) {
719                                                         return -1;
720                                                 }
721                                                 gp += len;
722                                         }
723
724                                         if (ppc[3] == FPO_ZERO_PAD) { /* Need to group 0s */
725 /*                                              __printf("zeropad\n"); */
726                                                 cnt += ppc[1];
727                                                 ppc += 3;
728                                                 gp = (const char *) ppc[2];
729                                                 blk -= len;     /* blk > len, so blk still > 0. */
730                                                 len = ppc[1];
731                                                 continue; /* Don't decrement num_groups here. */
732                                         } else {
733                                                 assert(num_groups == 0);
734                                                 break;
735                                         }
736                                 }
737
738                                 if (num_groups <= 0) {
739                                         break;
740                                 }
741                                 --num_groups;
742
743                                 if (fp_outfunc(fp, FPO_STR_PREC, tslen, (intptr_t) ts) != tslen) {
744                                         return -1;
745                                 }
746                                 blk = nblk2;
747
748 /*                              __printf("num_groups=%d   blk=%d\n", num_groups, blk); */
749
750                         } while (1);
751                 } else
752
753 #endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */
754                 {                                               /* NOTE: Remember 'else' above! */
755                         if (fp_outfunc(fp, *ppc, ppc[1], ppc[2]) != ppc[1]) {
756                                 return -1;
757                         }
758                 }
759
760                 cnt += ppc[1];
761                 ppc += 3;
762         } while (ppc < ppc_last);
763
764         return cnt;
765 }