diff options
Diffstat (limited to 'firmware/common/ap_int.c')
-rw-r--r-- | firmware/common/ap_int.c | 266 |
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 */ | ||
27 | bool 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 */ | ||
55 | char * 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 */ | ||
141 | char * 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 | } | ||