summaryrefslogtreecommitdiff
path: root/firmware/common/ap_int.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/common/ap_int.c')
-rw-r--r--firmware/common/ap_int.c266
1 files changed, 266 insertions, 0 deletions
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}