summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2018-01-06 07:17:04 -0500
committerFranklin Wei <git@fwei.tk>2019-07-19 22:07:41 -0400
commitb70fecf21ddc21877ec1ae7888d9c18a979e37ad (patch)
tree021ab34f99f1ff9f395a1f38b65d3c8510393229
parent3e2b50ed3b58daa5e3be5ea336d276b1655565f1 (diff)
downloadrockbox-b70fecf21ddc21877ec1ae7888d9c18a979e37ad.tar.gz
rockbox-b70fecf21ddc21877ec1ae7888d9c18a979e37ad.zip
Add proper float formatting to vuprintf
Wanted to see how gnarly it is to do. Big number handling could be done with better algorithms since it can get a bit slow with large integers or tiny fractions with many lead zeros when only a few digits are needed. Anyway, it supports %e, %E, %f, %F, %g and %G. No %a or long double support seems warranted at the moment. Assumes IEEE 754 double format but it's laid out to be able to replace a function to handle others if needed. Tested in a driver program that has a duplicate vuprintf and the content was pasted in once it looked sound enough to put up a patch. Change-Id: I6dae8624d3208e644c88e36e6a17d8fc9144f988
-rw-r--r--firmware/SOURCES3
-rw-r--r--firmware/common/ap_int.c266
-rw-r--r--firmware/common/vuprintf.c475
-rw-r--r--firmware/include/ap_int.h59
4 files changed, 793 insertions, 10 deletions
diff --git a/firmware/SOURCES b/firmware/SOURCES
index 16ae4cc350..89f4f52895 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -199,6 +199,9 @@ libc/gmtime.c
199#endif /* CONFIG_PLATFORM || HAVE_ROCKBOX_C_LIBRARY */ 199#endif /* CONFIG_PLATFORM || HAVE_ROCKBOX_C_LIBRARY */
200 200
201/* Common */ 201/* Common */
202#ifndef BOOTLOADER
203common/ap_int.c
204#endif
202common/version.c 205common/version.c
203common/config.c 206common/config.c
204common/crc32.c 207common/crc32.c
diff --git a/firmware/common/ap_int.c b/firmware/common/ap_int.c
new file mode 100644
index 0000000000..12214534dd
--- /dev/null
+++ b/firmware/common/ap_int.c
@@ -0,0 +1,266 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2018 by Michael A. Sevakis
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include "ap_int.h"
22#include "fixedpoint.h"
23
24/* Miscellaneous large-sized integer functions */
25
26/* round string, base 10 */
27bool round_number_string10(char *p_rdig, long len)
28{
29 /*
30 * * p should point to the digit that determines if rounding should occur
31 * * buffer is updated in reverse
32 * * an additional '1' may be added to the beginning: eg. 9.9 => 10.0
33 */
34#if 1 /* nearest */
35 bool round = p_rdig[0] >= '5';
36#else /* even */
37 bool round = p_rdig[0] >= '5' && (p_rdig[-1] & 1);
38#endif
39
40 while (round && len-- > 0) {
41 int d = *--p_rdig;
42 round = ++d > '9';
43 *p_rdig = round ? '0' : d;
44 }
45
46 if (round) {
47 /* carry to the next place */
48 *--p_rdig = '1';
49 }
50
51 return round;
52}
53
54/* format arbitrary-precision base 10 integer */
55char * format_ap_int10(struct ap_int *a,
56 char *p_end)
57{
58 /*
59 * * chunks are in least-to-most-significant order
60 * * chunk array is used for intermediate division results
61 * * digit string buffer is written high-to-low address order
62 */
63 long numchunks = a->numchunks;
64 char *p = p_end;
65
66 if (numchunks == 0) {
67 /* fast formatting */
68 uint64_t val = a->val;
69
70 do {
71 *--p = val % 10 + '0';
72 val /= 10;
73 } while (val);
74
75 a->len = p_end - p;
76 return p;
77 }
78
79 uint32_t *chunks = a->chunks;
80 long topchunk = numchunks - 1;
81
82 /* if top chunk(s) are zero, ignore */
83 while (topchunk >= 0 && chunks[topchunk] == 0) {
84 topchunk--;
85 }
86
87 /* optimized to divide number by the biggest 10^x a uint32_t can hold
88 so that r_part holds the remainder (x % 1000000000) at the end of
89 the division */
90 do {
91 uint64_t r_part = 0;
92
93 for (long i = topchunk; i >= 0; i--) {
94 /*
95 * Testing showed 29 bits as a sweet spot:
96 * * Is a 32-bit constant (good for 32-bit hardware)
97 * * No more normalization is required than with 30 and 31
98 * (32 bits requires the least but also a large constant)
99 * * Doesn't need to be reduced before hand by subtracting the
100 * divisor in order to keep it 32-bits which obviates the need
101 * to correct with another term of the remainder after
102 * multiplying
103 *
104 * 2305843009 = floor(ldexp(1, 29) / 1000000000.0 * ldexp(1, 32))
105 */
106 static const unsigned long c = 2305843009; /* .213693952 */
107 uint64_t q_part = r_part*c >> 29;
108 r_part = (r_part << 32) | chunks[i];
109 r_part -= q_part*1000000000;
110
111 /* if remainder is still out of modular range, normalize it
112 and carry over into quotient */
113 while (r_part >= 1000000000) {
114 r_part -= 1000000000;
115 q_part++;
116 }
117
118 chunks[i] = q_part;
119 }
120
121 /* if top chunk(s) became zero, ignore from now on */
122 while (topchunk >= 0 && chunks[topchunk] == 0) {
123 topchunk--;
124 }
125
126 /* format each digit chunk, padded to width 9 if not the leading one */
127 uint32_t val = r_part;
128 int len = 8*(topchunk >= 0);
129
130 while (len-- >= 0 || val) {
131 *--p = (val % 10) + '0';
132 val /= 10;
133 }
134 } while (topchunk >= 0);
135
136 a->len = p_end - p;
137 return p;
138}
139
140/* format arbitrary-precision base 10 fraction */
141char * format_ap_frac10(struct ap_int *a,
142 char *p_start,
143 long precision)
144{
145 /*
146 * * chunks are in least-to-most-significant order
147 * * chunk array is used for intermediate multiplication results
148 * * digit string buffer is written low-to-high address order
149 * * high bit of fraction must be left-justified to a chunk
150 * boundary
151 */
152 long numchunks = a->numchunks;
153 bool trimlz = precision < 0;
154 char *p = p_start;
155
156 if (trimlz) {
157 /* trim leading zeros and provide <precision> digits; a->len
158 will end up greater than the specified precision unless the
159 value is zero */
160 precision = -precision;
161 }
162
163 a->len = precision;
164
165 if (numchunks == 0) {
166 /* fast formatting; shift must be <= 60 as top four bits are used
167 for digit carryout */
168 if (trimlz && !a->val) {
169 /* value is zero */
170 trimlz = false;
171 }
172
173 uint64_t val = a->val << (60 - a->shift);
174
175 while (precision > 0) {
176 val *= 10;
177 uint32_t c_part = val >> 60;
178
179 if (trimlz) {
180 if (!c_part) {
181 a->len++;
182 continue;
183 }
184
185 trimlz = false;
186 }
187
188 *p++ = c_part + '0';
189 val ^= (uint64_t)c_part << 60;
190 precision--;
191 }
192
193 return p;
194 }
195
196 uint32_t *chunks = a->chunks;
197 long bottomchunk = 0, topchunk = numchunks;
198
199 while (topchunk > 0 && chunks[topchunk - 1] == 0) {
200 topchunk--;
201 }
202
203 /* optimized to multiply number by the biggest 10^x a uint32_t can hold
204 so that c_part holds the carryover into the integer part at the end
205 of the multiplication */
206 while (precision > 0) {
207 /* if bottom chunk(s) are or became zero, skip them */
208 while (bottomchunk < numchunks && chunks[bottomchunk] == 0) {
209 bottomchunk++;
210 }
211
212 uint32_t c_part = 0;
213
214 for (long i = bottomchunk; i < topchunk; i++) {
215 uint64_t p_part = chunks[i];
216
217 p_part = p_part * 1000000000 + c_part;
218 c_part = p_part >> 32;
219
220 chunks[i] = p_part;
221 }
222
223 if (topchunk < numchunks && c_part) {
224 chunks[topchunk++] = c_part;
225 c_part = 0;
226 }
227
228 int len = 9;
229
230 if (trimlz && bottomchunk < numchunks) {
231 if (!c_part) {
232 a->len += 9;
233 continue;
234 }
235
236 /* first non-zero chunk has leading zeros? */
237 for (uint32_t val = c_part; val < 100000000; val *= 10) {
238 len--;
239 }
240
241 a->len += 9 - len;
242 trimlz = false;
243 }
244
245 /* format each digit chunk, padded to width 9 if not exceeding
246 precision */
247 precision -= len;
248
249 if (precision < 0) {
250 /* remove extra digits */
251 c_part /= ipow(10, -precision);
252 len += precision;
253 }
254
255 p += len;
256
257 char *p2 = p;
258
259 while (len-- > 0) {
260 *--p2 = (c_part % 10) + '0';
261 c_part /= 10;
262 }
263 }
264
265 return p;
266}
diff --git a/firmware/common/vuprintf.c b/firmware/common/vuprintf.c
index 7dd9449e56..8bb9451662 100644
--- a/firmware/common/vuprintf.c
+++ b/firmware/common/vuprintf.c
@@ -23,8 +23,10 @@
23#include <limits.h> 23#include <limits.h>
24#include <string.h> 24#include <string.h>
25#include <stddef.h> 25#include <stddef.h>
26#include <stdlib.h>
26#include "system.h" 27#include "system.h"
27#include "vuprintf.h" 28#include "vuprintf.h"
29#include "ap_int.h"
28 30
29#ifndef BOOTLOADER 31#ifndef BOOTLOADER
30/* turn everything on */ 32/* turn everything on */
@@ -58,7 +60,11 @@
58#define FMT_LENMOD_ll 0x010 /* signed/unsigned long long (%ll<radix>) */ 60#define FMT_LENMOD_ll 0x010 /* signed/unsigned long long (%ll<radix>) */
59#define FMT_LENMOD_t 0x020 /* signed/unsigned ptrdiff_t (%t<radix>) */ 61#define FMT_LENMOD_t 0x020 /* signed/unsigned ptrdiff_t (%t<radix>) */
60#define FMT_LENMOD_z 0x040 /* size_t/ssize_t (%z<radix>) */ 62#define FMT_LENMOD_z 0x040 /* size_t/ssize_t (%z<radix>) */
63#if 0
61#define FMT_LENMOD_L 0x080 /* long double (instead of double) */ 64#define FMT_LENMOD_L 0x080 /* long double (instead of double) */
65#else
66#define FMT_LENMOD_L 0x000
67#endif
62 68
63/* compulsory radixes: c, d, i, u, s */ 69/* compulsory radixes: c, d, i, u, s */
64#define FMT_RADIX_c 0x001 /* single character (%c) */ 70#define FMT_RADIX_c 0x001 /* single character (%c) */
@@ -75,6 +81,18 @@
75#define FMT_RADIX_g 0x800 /* floating point exponent or decimal depending 81#define FMT_RADIX_g 0x800 /* floating point exponent or decimal depending
76 upon value and precision */ 82 upon value and precision */
77 83
84/* TODO: 'a' 'A' */
85#define FMT_RADIX_floats (FMT_RADIX_e|FMT_RADIX_f|FMT_RADIX_g)
86
87#if (FMT_RADIX & FMT_RADIX_floats)
88/* Assumes IEEE 754 double-precision, native-endian; replace to parse and init
89 for some other format */
90#define parse_double parse_ieee754_double
91#define init_double_chunks init_ieee754_double_chunks
92#define format_double_int10 format_ap_int10
93#define format_double_frac10 format_ap_frac10
94#endif
95
78/* avoid defining redundant functions if two or more types can use the same 96/* avoid defining redundant functions if two or more types can use the same
79 * something not getting a macro means it gets assigned its own value and 97 * something not getting a macro means it gets assigned its own value and
80 * formatter */ 98 * formatter */
@@ -374,8 +392,24 @@ struct fmt_buf {
374 or prefix (numeric) */ 392 or prefix (numeric) */
375 char buf[24]; /* work buffer */ 393 char buf[24]; /* work buffer */
376 char bufend[1]; /* buffer end marker and guard '0' */ 394 char bufend[1]; /* buffer end marker and guard '0' */
395#if (FMT_RADIX & FMT_RADIX_floats)
396 int lenmod;
397 int radixchar;
398 int signchar;
399 int alignchar;
400 int width;
401 int precision;
402 char *p;
403#endif
377}; 404};
378 405
406#define PUSHCHAR(ch) \
407 ({ int __rc = push(userp, (ch)); \
408 count += __rc >= 0; \
409 if (__rc <= 0) { \
410 goto done; \
411 } })
412
379/* %d %i */ 413/* %d %i */
380static inline const char * format_d(int val, 414static inline const char * format_d(int val,
381 struct fmt_buf *fmt_buf, 415 struct fmt_buf *fmt_buf,
@@ -530,6 +564,400 @@ static inline const char * format_p(const void *p,
530} 564}
531#endif /* FMT_RADIX_p */ 565#endif /* FMT_RADIX_p */
532 566
567#if (FMT_RADIX & FMT_RADIX_floats)
568/* find out how many uint32_t chunks need to be allocated, if any
569 * if none are needed, finish the init for the number here */
570static long parse_ieee754_double(double f,
571 struct ap_int *ia,
572 struct ap_int *fa,
573 struct fmt_buf *fmt_buf)
574{
575 long rc = 0;
576
577 union {
578 double f;
579 uint64_t f64;
580 } u = { .f = f };
581
582 int e = ((int)(u.f64 >> 52) & 0x7ff) - 1023; /* -1023..1024 */
583 uint64_t mantissa = u.f64 & 0x000fffffffffffffull;
584
585 if (u.f64 >> 63) {
586 fmt_buf->signchar = '-';
587 }
588
589 if (LIKELY(e >= -8 && e <= 63)) { /* -8 to +63 */
590 /* integer, fraction and manipulations fit in uint64_t */
591 mantissa |= 0x0010000000000000ull;
592 ia->numchunks = 0;
593 ia->shift = 0;
594 fa->numchunks = 0;
595
596 if (e < 0) { /* -8 to -1 - fraction */
597 long fracbits = 52 - e;
598 /* int - none */
599 ia->len = 0;
600 ia->val = 0;
601 /* frac */
602 fa->len = fracbits - __builtin_ctzll(mantissa);
603 fa->shift = fracbits;
604 fa->val = mantissa;
605 }
606 else if (e <= 51) { /* 0 to +51 - integer|fraction */
607 long fracbits = 52 - e;
608 /* int */
609 ia->len = base10exp(e) + 2; /* go up + possibly 1 longer */
610 ia->val = mantissa >> fracbits;
611 /* frac */
612 fa->shift = fracbits;
613 fa->val = mantissa ^ (ia->val << fracbits);
614 fa->len = fa->val ? fracbits - __builtin_ctzll(mantissa) : 0;
615 }
616 else { /* +52 to +63 - integer */
617 /* int */
618 ia->len = base10exp(e) + 2;
619 ia->val = mantissa << (e - 52);
620 /* frac - none */
621 fa->len = 0;
622 fa->shift = 0;
623 fa->val = 0;
624 }
625 }
626 else if (e < 0) { /* -1023 to -9 - fraction */
627 /* int - none */
628 ia->numchunks = 0;
629 ia->len = 0;
630 ia->shift = 0;
631 ia->val = 0;
632 /* frac - left-justify on bit 31 of the chunk of the MSb */
633 if (e >= -1022) { /* normal */
634 mantissa |= 0x0010000000000000ull;
635 }
636 else { /* subnormal (including zero) */
637 e = -1022;
638 }
639
640 if (mantissa) {
641 long fracbits = 52 - e;
642 fa->len = fracbits - __builtin_ctzll(mantissa);
643 fa->shift = 31 - ((51 - e) % 32);
644 fa->val = mantissa;
645 fa->basechunk = (fa->shift + 52) / 32;
646 fa->numchunks = (51 - e + fa->shift) / 32 + 1;
647 rc = fa->numchunks;
648 }
649 else { /* zero */
650 fa->numchunks = 0;
651 fa->len = 0;
652 fa->shift = 0;
653 fa->val = 0;
654 }
655 }
656 else if (e <= 1023) { /* +64 to +1023 - integer */
657 /* int - right-justify on bit 0 of the first chunk */
658 ia->val = mantissa | 0x0010000000000000ull;
659 ia->len = base10exp(e) + 2;
660 ia->shift = (e - 52) % 32;
661 ia->basechunk = e / 32;
662 ia->numchunks = ia->basechunk + 1;
663 rc = ia->numchunks;
664 /* frac - none */
665 fa->numchunks = 0;
666 fa->len = 0;
667 fa->shift = 0;
668 fa->val = 0;
669 }
670 else { /* +1024: INF, NAN */
671 rc = -1 - !!mantissa;
672 }
673
674 return rc;
675}
676
677/* construct the arbitrary-precision value in the provided allocation */
678static void init_ieee754_double_chunks(struct ap_int *a,
679 uint32_t *a_chunks)
680{
681 long basechunk = a->basechunk;
682 long shift = a->shift;
683 uint64_t val = a->val;
684
685 a->chunks = a_chunks;
686
687 memset(a_chunks, 0, a->numchunks*sizeof (uint32_t));
688
689 if (shift < 12) {
690 a_chunks[basechunk - 1] = val << shift;
691 a_chunks[basechunk - 0] = val >> (32 - shift);
692 }
693 else {
694 a_chunks[basechunk - 2] = val << shift;
695 a_chunks[basechunk - 1] = val >> (32 - shift);
696 a_chunks[basechunk - 0] = val >> (64 - shift);
697 }
698}
699
700/* format inf, nan strings */
701static void format_inf_nan(struct fmt_buf *fmt_buf, long type)
702{
703 /* certain special values */
704 static const char text[2][2][3] =
705 {
706 { { 'I', 'N', 'F' }, { 'i', 'n', 'f' } },
707 { { 'N', 'A', 'N' }, { 'n', 'a', 'n' } },
708 };
709
710 char *p = fmt_buf->buf;
711 fmt_buf->p = p;
712 fmt_buf->length = 3;
713
714 /* they also have a sign */
715 if (fmt_buf->signchar) {
716 *p++ = fmt_buf->signchar;
717 fmt_buf->length++;
718 }
719
720 memcpy(p, &text[type][(fmt_buf->radixchar >> 5) & 0x1], 3);
721}
722
723/* %e %E %f %F %g %G */
724static int format_double_radix(double f,
725 struct fmt_buf *fmt_buf,
726 vuprintf_push_cb push,
727 void *userp)
728{
729 struct ap_int ia, fa;
730 long rc = parse_double(f, &ia, &fa, fmt_buf);
731
732 if (UNLIKELY(rc < 0)) {
733 format_inf_nan(fmt_buf, -rc - 1);
734 return 0;
735 }
736
737 int count = 0;
738
739 /* default precision is 6 for all formats */
740 int prec_rem = fmt_buf->precision < 0 ? 6 : fmt_buf->precision;
741
742 int exp = exp;
743 int explen = 0;
744
745 switch (fmt_buf->radixchar & 0x3)
746 {
747 case 3: /* %g, %G */
748 fmt_buf->precision = prec_rem;
749 if (prec_rem) {
750 prec_rem--;
751 }
752 case 1: /* %e, %E */
753 explen = 2;
754 break;
755 default:
756 break;
757 }
758
759 if (rc > 0 && ia.numchunks > 0) {
760 /* large integer required */
761 init_double_chunks(&ia, alloca(rc*sizeof(*ia.chunks)));
762 rc = 0;
763 }
764
765 const int bufoffs = 6; /* log rollover + round rollover + leading zeros (%g) */
766 long f_prec = MIN(fa.len, prec_rem + 1);
767 char buf[bufoffs + ia.len + f_prec + 1];
768 char *p_last = &buf[bufoffs + ia.len];
769 char *p_dec = p_last;
770 char *p_first = format_double_int10(&ia, p_last);
771
772 if (explen) {
773 if (!ia.val && fa.len) {
774 p_first = p_last = &buf[bufoffs];
775 f_prec = -f_prec - 1; /* no lead zeros */
776 }
777 else { /* handles 0e+0 too */
778 exp = ia.len - 1;
779
780 if (exp) {
781 prec_rem -= exp;
782
783 if (prec_rem < 0) {
784 p_last += prec_rem + 1;
785 f_prec = 0;
786 }
787 else {
788 f_prec = MIN(fa.len, prec_rem + 1);
789 }
790 }
791 }
792
793 p_dec = p_first + 1;
794 }
795
796 if (f_prec) {
797 if (rc > 0) {
798 /* large integer required */
799 init_double_chunks(&fa, alloca(rc*sizeof(*fa.chunks)));
800 }
801
802 p_last = format_double_frac10(&fa, p_last, f_prec);
803
804 if (f_prec < 0) {
805 f_prec = -f_prec - 1;
806 exp = f_prec - fa.len;
807 }
808
809 prec_rem -= f_prec;
810 }
811
812 if (prec_rem < 0) {
813 prec_rem = 0;
814 p_last--;
815
816 if (round_number_string10(p_last, p_last - p_first)) {
817 /* carried left */
818 p_first--;
819
820 if (explen) {
821 /* slide everything left by 1 */
822 exp++;
823 p_dec--;
824 p_last--;
825 }
826 }
827 }
828
829 if (explen) {
830 if ((fmt_buf->radixchar & 0x3) == 0x3) { /* g, G */
831 /* 'g' is some weird crap */
832 /* now that the final exponent is known and everything rounded,
833 it is possible to decide whether to format similarly to
834 'e' or 'f' */
835 if (fmt_buf->precision > exp && exp >= -4) { /* P > X >= -4 */
836 if (exp >= 0) {
837 /* integer digits will be in the buffer */
838 p_dec = p_first + exp + 1;
839 }
840 else {
841 /* we didn't keep leading zeros and need to regenerate
842 them; space was reserved just in case */
843 p_first = memset(p_dec + exp - 1, '0', -exp);
844 p_dec = p_first + 1;
845 }
846
847 /* suppress exponent */
848 explen = 0;
849 }
850
851 if (!fmt_buf->length) {
852 /* strip any trailing zeros from the fraction */
853 while (p_last > p_dec && p_last[-1] == '0') {
854 p_last--;
855 }
856
857 /* suppress trailing precision fill */
858 prec_rem = 0;
859 }
860 }
861
862 if (explen) {
863 /* build exponent string: 'eħdd' */
864 char *p = fmt_buf->bufend;
865 int signchar = '+';
866
867 if (exp < 0) {
868 signchar = '-';
869 exp = -exp;
870 }
871
872 while (exp || explen < 4) {
873 *--p = exp % 10 + '0';
874 exp /= 10;
875 explen++;
876 }
877
878 *--p = signchar;
879 *--p = fmt_buf->radixchar & ~0x2;
880 }
881 }
882
883 int width = fmt_buf->width;
884 int point = p_last > p_dec || prec_rem || fmt_buf->length;
885 int length = p_last - p_first + !!fmt_buf->signchar + point + explen;
886
887 if (width) {
888 if (width - length <= prec_rem) {
889 width = 0;
890 }
891 else {
892 width -= length + prec_rem;
893 }
894 }
895
896 rc = -1;
897
898 /* left padding */
899 if (fmt_buf->alignchar > '0') {
900 /* space-padded width -- before sign */
901 while (width > 0) {
902 PUSHCHAR(' ');
903 width--;
904 }
905 }
906
907 if (fmt_buf->signchar) {
908 PUSHCHAR(fmt_buf->signchar);
909 }
910
911 if (fmt_buf->alignchar == '0') {
912 /* zero-padded width -- after sign */
913 while (width > 0) {
914 PUSHCHAR('0');
915 width--;
916 }
917 }
918
919 /* integer part */
920 while (p_first < p_dec) {
921 PUSHCHAR(*p_first++);
922 }
923
924 /* decimal point */
925 if (point) {
926 PUSHCHAR('.');
927 }
928
929 /* fractional part */
930 while (p_first < p_last) {
931 PUSHCHAR(*p_first++);
932 }
933
934 /* precision 0-padding */
935 while (prec_rem > 0) {
936 PUSHCHAR('0');
937 prec_rem--;
938 }
939
940 /* exponent */
941 if (explen > 0) {
942 char *p = fmt_buf->bufend;
943 while (explen > 0) {
944 PUSHCHAR(p[-explen--]);
945 }
946 }
947
948 /* right padding */
949 while (width > 0) {
950 PUSHCHAR(' ');
951 width--;
952 }
953
954 rc = 1;
955done:
956 fmt_buf->length = count;
957 return rc;
958}
959#endif /* FMT_RADIX_floats */
960
533/* parse fixed width or precision field */ 961/* parse fixed width or precision field */
534static const char * parse_number_spec(const char *fmt, 962static const char * parse_number_spec(const char *fmt,
535 int ch, 963 int ch,
@@ -558,16 +986,6 @@ int vuprintf(vuprintf_push_cb push, /* call 'push()' for each output letter */
558 const char *fmt, 986 const char *fmt,
559 va_list ap) 987 va_list ap)
560{ 988{
561 #define PUSHCHAR(ch) \
562 do { \
563 int __ch = (ch); \
564 int __rc = push(userp, __ch); \
565 count += __rc >= 0; \
566 if (__rc <= 0) { \
567 goto done; \
568 } \
569 } while (0)
570
571 int count = 0; 989 int count = 0;
572 int ch; 990 int ch;
573 991
@@ -706,6 +1124,9 @@ int vuprintf(vuprintf_push_cb push, /* call 'push()' for each output letter */
706 #if (FMT_LENMOD & FMT_LENMOD_z) 1124 #if (FMT_LENMOD & FMT_LENMOD_z)
707 case 'z': 1125 case 'z':
708 #endif 1126 #endif
1127 #if (FMT_LENMOD & FMT_LENMOD_L)
1128 case 'L':
1129 #endif
709 lenmod = ch; 1130 lenmod = ch;
710 ch = *fmt++; 1131 ch = *fmt++;
711 #if (FMT_LENMOD & (FMT_LENMOD_hh | FMT_LENMOD_ll)) 1132 #if (FMT_LENMOD & (FMT_LENMOD_hh | FMT_LENMOD_ll))
@@ -747,6 +1168,40 @@ int vuprintf(vuprintf_push_cb push, /* call 'push()' for each output letter */
747 break; 1168 break;
748 #endif 1169 #endif
749 1170
1171 #if (FMT_RADIX & FMT_RADIX_floats)
1172 /* any floats gets all of them (except with 'L' and %a, %A for now) */
1173 case 'e':
1174 case 'E':
1175 case 'f':
1176 case 'F':
1177 case 'g':
1178 case 'G':
1179 /* LENMOD_L isn't supported for now and will be rejected automatically */
1180
1181 /* floating point has very different spec interpretations to other
1182 formats and requires special handling */
1183 fmt_buf.length = pfxlen;
1184 fmt_buf.lenmod = lenmod;
1185 fmt_buf.radixchar = ch;
1186 fmt_buf.signchar = signchar;
1187 fmt_buf.alignchar = alignchar;
1188 fmt_buf.width = width;
1189 fmt_buf.precision = precision;
1190
1191 ch = format_double_radix(va_arg(ap, double), &fmt_buf, push, userp);
1192 if (ch) {
1193 count += fmt_buf.length;
1194 if (ch > 0) {
1195 continue;
1196 }
1197
1198 goto done;
1199 }
1200
1201 buf = fmt_buf.p;
1202 break;
1203 #endif
1204
750 /** signed integer **/ 1205 /** signed integer **/
751 case 'd': 1206 case 'd':
752 case 'i': 1207 case 'i':
diff --git a/firmware/include/ap_int.h b/firmware/include/ap_int.h
new file mode 100644
index 0000000000..68cbb2fb71
--- /dev/null
+++ b/firmware/include/ap_int.h
@@ -0,0 +1,59 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2018 by Michael A. Sevakis
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#ifndef AP_INT_H
22#define AP_INT_H
23
24/* Miscellaneous large-sized integer functions */
25
26#include <stdbool.h>
27#include <stdint.h>
28
29/* return floor(log(2)*base2exp) - assists in estimating buffer sizes
30 * when converting to decimal */
31static inline int base10exp(int base2exp)
32{
33 /* 1292913986 = floor(2^32*log(2)) */
34 static const long log10of2 = 1292913986L;
35 return log10of2 * (int64_t)base2exp >> 32;
36}
37
38struct ap_int
39{
40 long numchunks; /* number of uint32_t chunks or zero */
41 long basechunk; /* chunk of start of value bits */
42 uint32_t *chunks; /* pointer to chunk array (caller alloced) */
43 long len; /* length of output */
44 long shift; /* number of fractional bits */
45 uint64_t val; /* value, if it fits and numchunks is zero */
46};
47
48bool round_number_string10(char *p_rdig, long len);
49
50/* format arbitrary-precision base 10 integer */
51char * format_ap_int10(struct ap_int *a,
52 char *p_end);
53
54/* format arbitrary-precision base 10 fraction */
55char * format_ap_frac10(struct ap_int *a,
56 char *p_start,
57 long precision);
58
59#endif /* AP_INT_H */