2 * Copyright (C) 2000,2001,2003,2004 Manuel Novoa III <mjn3@codepoet.org>
4 * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
6 * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details.
13 #include "_fpmaxtostr.h"
18 * ssize_t _fpmaxtostr(FILE * fp, __fpmax_t x, struct printf_info *info,
19 * __fp_outfunc_t fp_outfunc);
21 * This is derived from the old _dtostr, whic I wrote for uClibc to provide
22 * floating point support for the printf functions. It handles +/- infinity,
23 * nan, and signed 0 assuming you have ieee arithmetic. It also now handles
24 * digit grouping (for the uClibc supported locales) and hexadecimal float
25 * notation. Finally, via the fp_outfunc parameter, it now supports wide
30 * At most DECIMAL_DIG significant digits are kept. Any trailing digits
31 * are treated as 0 as they are really just the results of rounding noise
32 * anyway. If you want to do better, use an arbitary precision arithmetic
35 * It should also be fairly portable, as no assumptions are made about the
36 * bit-layout of doubles. Of course, that does make it less efficient than
40 /*****************************************************************************/
41 /* Don't change anything that follows unless you know what you're doing. */
42 /*****************************************************************************/
43 /* Fairly portable nan check. Bitwise for i386 generated larger code.
44 * If you have a better version, comment this out.
46 #define isnan(x) ((x) != (x))
48 /*****************************************************************************/
49 /* Don't change anything that follows peroid!!! ;-) */
50 /*****************************************************************************/
51 #ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
53 #error FLT_RADIX != 2 is not currently supported
55 #endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
57 #define NUM_HEX_DIGITS ((FPMAX_MANT_DIG + 3)/ 4)
59 #define HEX_DIGITS_PER_BLOCK 8
61 /* Maximum number of subcases to output double is...
63 * 1 - padding and initial digit
64 * 2 - digits left of the radix
65 * 3 - 0s left of the radix or radix
66 * 4 - radix or digits right of the radix
67 * 5 - 0s right of the radix
69 * 7 - trailing space padding
70 * although not all cases may occur.
74 /*****************************************************************************/
76 #define NUM_HEX_DIGIT_BLOCKS \
77 ((NUM_HEX_DIGITS+HEX_DIGITS_PER_BLOCK-1)/HEX_DIGITS_PER_BLOCK)
79 /*****************************************************************************/
81 static const char fmt
[] = "inf\0INF\0nan\0NAN\0.\0,";
83 #define INF_OFFSET 0 /* must be 1st */
84 #define NAN_OFFSET 8 /* must be 2nd.. see hex sign handling */
85 #define DECPT_OFFSET 16
86 #define THOUSEP_OFFSET 18
88 #define EMPTY_STRING_OFFSET 3
90 /*****************************************************************************/
91 #if FPMAX_MAX_10_EXP < -FPMAX_MIN_10_EXP
92 #error scaling code can not handle FPMAX_MAX_10_EXP < -FPMAX_MIN_10_EXP
95 static const __fpmax_t exp10_table
[] =
97 1e1L
, 1e2L
, 1e4L
, 1e8L
, 1e16L
, 1e32L
, /* floats */
98 #if FPMAX_MAX_10_EXP < 32
99 #error unsupported FPMAX_MAX_10_EXP (< 32). ANSI/ISO C requires >= 37.
101 #if FPMAX_MAX_10_EXP >= 64
104 #if FPMAX_MAX_10_EXP >= 128
107 #if FPMAX_MAX_10_EXP >= 256
110 #if FPMAX_MAX_10_EXP >= 512
113 #if FPMAX_MAX_10_EXP >= 1024
116 #if FPMAX_MAX_10_EXP >= 2048
119 #if FPMAX_MAX_10_EXP >= 4096
122 #if FPMAX_MAX_10_EXP >= 8192
123 #error unsupported FPMAX_MAX_10_EXP. please increase table
127 #define EXP10_TABLE_SIZE (sizeof(exp10_table)/sizeof(exp10_table[0]))
128 #define EXP10_TABLE_MAX (1U<<(EXP10_TABLE_SIZE-1))
130 /*****************************************************************************/
131 #ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
134 #error FLT_RADIX != 2 is not currently supported
137 #if FPMAX_MAX_EXP < -FPMAX_MIN_EXP
138 #error scaling code can not handle FPMAX_MAX_EXP < -FPMAX_MIN_EXP
141 static const __fpmax_t exp16_table
[] = {
142 0x1.0p4L
, 0x1.0p8L
, 0x1.0p16L
, 0x1.0p32L
, 0x1.0p64L
,
143 #if FPMAX_MAX_EXP >= 128
146 #if FPMAX_MAX_EXP >= 256
149 #if FPMAX_MAX_EXP >= 512
152 #if FPMAX_MAX_EXP >= 1024
155 #if FPMAX_MAX_EXP >= 2048
158 #if FPMAX_MAX_EXP >= 4096
161 #if FPMAX_MAX_EXP >= 8192
164 #if FPMAX_MAX_EXP >= 16384
167 #if FPMAX_MAX_EXP >= 32768
168 #error unsupported FPMAX_MAX_EXP. please increase table
172 #define EXP16_TABLE_SIZE (sizeof(exp16_table)/sizeof(exp16_table[0]))
173 #define EXP16_TABLE_MAX (1U<<(EXP16_TABLE_SIZE-1))
175 #endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
176 /*****************************************************************************/
178 #define FPO_ZERO_PAD (0x80 | '0')
179 #define FPO_STR_WIDTH (0x80 | ' ');
180 #define FPO_STR_PREC 'p'
182 ssize_t
_fpmaxtostr(FILE * fp
, __fpmax_t x
, struct printf_info
*info
,
183 __fp_outfunc_t fp_outfunc
)
185 #ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
187 __fpmax_t upper_bnd
= 1e9
;
188 #endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
189 #ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
190 uint_fast32_t base
= 10;
191 const __fpmax_t
*power_table
;
192 int dpb
= DIGITS_PER_BLOCK
;
193 int ndb
= NUM_DIGIT_BLOCKS
;
194 int nd
= DECIMAL_DIG
;
195 int sufficient_precision
= 0;
196 #endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
197 #ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__
199 int initial_group
; /* This does not need to be initialized. */
200 int tslen
; /* This does not need to be initialized. */
201 int nblk2
; /* This does not need to be initialized. */
202 const char *ts
; /* This does not need to be initialized. */
203 #endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */
210 intptr_t pc_fwi
[3*MAX_CALLS
];
215 char sign_str
[6]; /* Last 2 are for 1st digit + nul. */
225 if ((mode
|0x20) == 'a') {
226 #ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
229 preci
= NUM_HEX_DIGITS
;
230 sufficient_precision
= 1;
242 if (PRINT_INFO_FLAG_VAL(info
,showsign
)) {
244 } else if (PRINT_INFO_FLAG_VAL(info
,space
)) {
249 pc_fwi
[5] = INF_OFFSET
;
250 if (isnan(x
)) { /* First, check for nan. */
251 pc_fwi
[5] = NAN_OFFSET
;
255 if (x
== 0) { /* Handle 0 now to avoid false positive. */
256 #ifdef __UCLIBC_HAVE_SIGNED_ZERO__
263 if (u
.i
.l1
^ u
.i
.l2
) { /* Handle 'signed' zero. */
266 #endif /* __UCLIBC_HAVE_SIGNED_ZERO__ */
268 goto GENERATE_DIGITS
;
271 if (x
< 0) { /* Convert negatives to positives. */
276 if (__FPMAX_ZERO_OR_INF_CHECK(x
)) { /* Inf since zero handled above. */
280 pc_fwi
[3] = FPO_STR_PREC
;
285 pc_fwi
[5] = (intptr_t)(fmt
+ pc_fwi
[5]);
292 #ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
294 if ((mode
|0x20) == 'a') {
295 lower_bnd
= 0x1.0p31L
;
296 upper_bnd
= 0x1.0p32L
;
297 power_table
= exp16_table
;
298 exp
= HEX_DIGITS_PER_BLOCK
- 1;
299 i
= EXP16_TABLE_SIZE
;
301 dpb
= HEX_DIGITS_PER_BLOCK
;
302 ndb
= NUM_HEX_DIGIT_BLOCKS
;
307 /* upper_bnd = 1e9; */
308 power_table
= exp10_table
;
309 exp
= DIGITS_PER_BLOCK
- 1;
310 i
= EXP10_TABLE_SIZE
;
312 /* dpb = DIGITS_PER_BLOCK; */
313 /* ndb = NUM_DIGIT_BLOCKS; */
319 #else /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
321 #define lower_bnd 1e8
322 #define upper_bnd 1e9
323 #define power_table exp10_table
324 #define dpb DIGITS_PER_BLOCK
326 #define ndb NUM_DIGIT_BLOCKS
327 #define nd DECIMAL_DIG
329 exp
= DIGITS_PER_BLOCK
- 1;
330 i
= EXP10_TABLE_SIZE
;
333 #endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
337 if (x
< lower_bnd
) { /* Do we need to scale up or down? */
344 if (x
* power_table
[i
] < upper_bnd
) {
349 if (x
/ power_table
[i
] >= lower_bnd
) {
358 if (x
>= upper_bnd
) { /* Handle bad rounding case. */
362 assert(x
< upper_bnd
);
367 s
= buf
+ 2; /* Leave space for '\0' and '0'. */
370 uint_fast32_t digit_block
= (uint_fast32_t) x
;
371 assert(digit_block
< upper_bnd
);
372 x
= (x
- digit_block
) * upper_bnd
;
376 s
[- ++j
] = '0' + (digit_block
% base
);
382 /*************************************************************************/
385 *exp_buf
-= ('a' - 'A'); /* e->E and p->P */
390 if ((mode
== 'g') && (preci
> 0)){
398 memset(buf
, '0', DECIMAL_DIG
); /* OK, since 'f' -> decimal case. */
405 *s
++ = 0; /* Terminator for rounding and 0-triming. */
406 *s
= '0'; /* Space to round. */
414 if (*e
>= '0' + (base
/2)) { /* NOTE: We always round away from 0! */
419 do { /* Handle rounding and trim trailing 0s. */
420 *--e
+= i
; /* Add the carry. */
421 } while ((*e
== '0') || (*e
> '0' - 1 + base
));
424 #ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
425 if ((mode
|0x20) == 'a') {
428 for (q
= e
; *q
; --q
) {
430 *q
+= (*exp_buf
- ('p' - 'a') - '9' - 1);
435 exp
*= 4; /* Change from base 16 to base 2. */
438 #endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
441 if (e
<= s
) { /* We carried into an extra digit. */
443 e
= s
; /* Needed if all 0s. */
447 *++e
= 0; /* Terminating nul char. */
449 if ((mode
== 'g') && ((o_exp
>= -4) && (o_exp
<= round
))) {
451 preci
= round
- o_exp
;
459 if (o_exp
< 0) { /* Exponent is < 0, so */
460 *--s
= '0'; /* fake the first 0 digit. */
463 pc_fwi
[3] = FPO_ZERO_PAD
;
465 pc_fwi
[5] = (intptr_t)(sign_str
+ 4);
471 int i
= e
- s
; /* Total digits is 'i'. */
473 #ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__
477 if (PRINT_INFO_FLAG_VAL(info
,group
)
478 && *(p
= __UCLIBC_CURLOCALE
->grouping
)
488 if (o_exp
>= nblk1
) {
489 num_groups
= (o_exp
- nblk1
) / nblk2
+ 1;
490 initial_group
= (o_exp
- nblk1
) % nblk2
;
492 #ifdef __UCLIBC_HAS_WCHAR__
493 if (PRINT_INFO_FLAG_VAL(info
,wide
)) {
494 /* _fp_out_wide() will fix this up. */
495 ts
= fmt
+ THOUSEP_OFFSET
;
498 #endif /* __UCLIBC_HAS_WCHAR__ */
499 ts
= __UCLIBC_CURLOCALE
->thousands_sep
;
500 tslen
= __UCLIBC_CURLOCALE
->thousands_sep_len
;
501 #ifdef __UCLIBC_HAS_WCHAR__
503 #endif /* __UCLIBC_HAS_WCHAR__ */
505 width
-= num_groups
* tslen
;
510 #endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */
511 ppc
[0] = FPO_STR_PREC
;
512 ppc
[2] = (intptr_t)(s
);
513 if (o_exp
>= i
) { /* all digit(s) left of decimal */
518 if (o_exp
>0) { /* have 0s left of decimal */
519 ppc
[0] = FPO_ZERO_PAD
;
521 ppc
[2] = (intptr_t)(fmt
+ EMPTY_STRING_OFFSET
);
524 } else if (o_exp
> 0) { /* decimal between digits */
533 if (PRINT_INFO_FLAG_VAL(info
,alt
)
536 #ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
538 #endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
541 ppc
[0] = FPO_STR_PREC
;
542 #ifdef __LOCALE_C_ONLY
544 ppc
[2] = (intptr_t)(fmt
+ DECPT_OFFSET
);
545 #else /* __LOCALE_C_ONLY */
546 #ifdef __UCLIBC_HAS_WCHAR__
547 if (PRINT_INFO_FLAG_VAL(info
,wide
)) {
548 /* _fp_out_wide() will fix this up. */
550 ppc
[2] = (intptr_t)(fmt
+ DECPT_OFFSET
);
552 #endif /* __UCLIBC_HAS_WCHAR__ */
553 ppc
[1] = __UCLIBC_CURLOCALE
->decimal_point_len
;
554 ppc
[2] = (intptr_t)(__UCLIBC_CURLOCALE
->decimal_point
);
555 #ifdef __UCLIBC_HAS_WCHAR__
557 #endif /* __UCLIBC_HAS_WCHAR__ */
558 #endif /* __LOCALE_C_ONLY */
562 if (++o_exp
< 0) { /* Have 0s right of decimal. */
563 ppc
[0] = FPO_ZERO_PAD
;
565 ppc
[2] = (intptr_t)(fmt
+ EMPTY_STRING_OFFSET
);
568 if (i
) { /* Have digit(s) right of decimal. */
569 ppc
[0] = FPO_STR_PREC
;
571 ppc
[2] = (intptr_t)(s
);
575 if (((o_mode
!= 'g') || PRINT_INFO_FLAG_VAL(info
,alt
))
576 #ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
577 && !sufficient_precision
578 #endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
581 if (i
< preci
) { /* Have 0s right of digits. */
583 ppc
[0] = FPO_ZERO_PAD
;
585 ppc
[2] = (intptr_t)(fmt
+ EMPTY_STRING_OFFSET
);
591 /* Build exponent string. */
593 char *p
= exp_buf
+ sizeof(exp_buf
);
595 char exp_char
= *exp_buf
;
597 #ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
598 int min_exp_dig_plus_2
= ((o_mode
!= 'a') ? (2+2) : (2+1));
599 #else /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
600 #define min_exp_dig_plus_2 (2+2)
601 #endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
608 *--p
= 0; /* nul-terminate */
609 j
= 2; /* Count exp_char and exp_sign. */
611 *--p
= '0' + (exp
% 10);
613 } while ((++j
< min_exp_dig_plus_2
) || exp
); /* char+sign+mindigits */
617 ppc
[0] = FPO_STR_PREC
;
619 ppc
[2] = (intptr_t)(p
);
627 ppc
= pc_fwi
+ 4; /* Need width fields starting with second. */
631 } while (ppc
< ppc_last
);
634 ppc
[0] = FPO_STR_WIDTH
;
635 ppc
[1] = i
= ((*sign_str
) != 0);
636 ppc
[2] = (intptr_t) sign_str
;
638 #ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
639 if (((mode
|0x20) == 'a') && (pc_fwi
[3] >= 16)) { /* Hex sign handling. */
640 /* Hex and not inf or nan, so prefix with 0x. */
641 char *h
= sign_str
+ i
;
643 *++h
= 'x' - 'p' + *exp_buf
;
647 #endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
649 if ((width
-= i
) > 0) {
650 if (PRINT_INFO_FLAG_VAL(info
,left
)) { /* Left-justified. */
651 ppc_last
[0] = FPO_STR_WIDTH
;
653 ppc_last
[2] = (intptr_t)(fmt
+ EMPTY_STRING_OFFSET
);
655 } else if (info
->pad
== '0') { /* 0 padding */
656 ppc
[4] += width
; /* Pad second field. */
658 ppc
[1] += width
; /* Pad first (sign) field. */
666 #ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__
668 if ((ppc
== pc_fwi
+ 6) && num_groups
) {
669 const char *gp
= (const char *) ppc
[2];
671 int blk
= initial_group
;
673 cnt
+= num_groups
* tslen
; /* Adjust count now for sep chars. */
675 /* __printf("\n"); */
677 if (!blk
) { /* Initial group could be 0 digits long! */
679 } else if (len
>= blk
) { /* Enough digits for a group. */
680 /* __printf("norm: len=%d blk=%d \"%.*s\"\n", len, blk, blk, gp); */
681 if (fp_outfunc(fp
, *ppc
, blk
, (intptr_t) gp
) != blk
) {
689 } else { /* Transition to 0s. */
690 /* __printf("trans: len=%d blk=%d \"%.*s\"\n", len, blk, len, gp); */
692 /* __printf("len\n"); */
693 if (fp_outfunc(fp
, *ppc
, len
, (intptr_t) gp
) != len
) {
699 if (ppc
[3] == FPO_ZERO_PAD
) { /* Need to group 0s */
700 /* __printf("zeropad\n"); */
703 gp
= (const char *) ppc
[2];
704 blk
-= len
; /* blk > len, so blk still > 0. */
706 continue; /* Don't decrement num_groups here. */
708 assert(num_groups
== 0);
713 if (num_groups
<= 0) {
718 if (fp_outfunc(fp
, FPO_STR_PREC
, tslen
, (intptr_t) ts
) != tslen
) {
723 /* __printf("num_groups=%d blk=%d\n", num_groups, blk); */
728 #endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */
729 { /* NOTE: Remember 'else' above! */
730 if (fp_outfunc(fp
, *ppc
, ppc
[1], ppc
[2]) != ppc
[1]) {
737 } while (ppc
< ppc_last
);