Branch data Line data Source code
1 : : #include "rational.h"
2 : : #include <math.h>
3 : : #include <string.h>
4 : :
5 : 20 : STATIC const char *form_write_empty(
6 : : char *result,
7 : : size_t result_size)
8 : : {
9 [ + + + + ]: 20 : if(result == NULL || result_size == 0U)
10 : : {
11 : 4 : return "";
12 : : }
13 : :
14 : 16 : result[0] = '\0';
15 : 16 : return result;
16 : : }
17 : :
18 : 10 : STATIC const char *form_write_zero(
19 : : char *result,
20 : : size_t result_size)
21 : : {
22 [ + + + + ]: 10 : if(result == NULL || result_size == 0U)
23 : : {
24 : 2 : return form_write_empty(result,result_size);
25 : : }
26 : :
27 [ + + ]: 8 : if(result_size <= 1U)
28 : : {
29 : 2 : return form_write_empty(result,result_size);
30 : : }
31 : :
32 : 6 : result[0] = '0';
33 : 6 : result[1] = '\0';
34 : 6 : return result;
35 : : }
36 : :
37 : 49 : static const char *form_uintmax_with_sign_r(
38 : : uintmax_t val,
39 : : bool negative,
40 : : char *result,
41 : : size_t result_size)
42 : : {
43 [ + + + + ]: 49 : if(result == NULL || result_size == 0U)
44 : : {
45 : 4 : return "";
46 : : }
47 : :
48 : 45 : char *write = result + result_size - 1U;
49 : 45 : *write = '\0';
50 : 45 : size_t group = 0U;
51 : :
52 : : do
53 : : {
54 [ + + ]: 173 : if(write == result)
55 : : {
56 : 4 : return form_write_empty(result,result_size);
57 : : }
58 : :
59 [ + + ]: 169 : if(group == 3U)
60 : : {
61 : 37 : *--write = ',';
62 : 37 : group = 0U;
63 : :
64 [ + + ]: 37 : if(write == result)
65 : : {
66 : 1 : return form_write_empty(result,result_size);
67 : : }
68 : : }
69 : :
70 : 168 : const uintmax_t digit = val % 10U;
71 : 168 : *--write = (char)('0' + (int)digit);
72 : 168 : val /= 10U;
73 : 168 : group++;
74 [ + + ]: 168 : } while(val > 0U);
75 : :
76 [ + + ]: 40 : if(negative)
77 : : {
78 [ + + ]: 8 : if(write == result)
79 : : {
80 : 2 : return form_write_empty(result,result_size);
81 : : }
82 : :
83 : 6 : *--write = '-';
84 : : }
85 : :
86 [ + + ]: 38 : if(write != result)
87 : : {
88 : 35 : const size_t used = (size_t)((result + result_size) - write);
89 : 35 : memmove(result,write,used);
90 : : }
91 : :
92 : 38 : return result;
93 : : }
94 : :
95 : : /**
96 : : * @brief Count decimal digits and comma separators for an unsigned integer
97 : : *
98 : : * @param val Integer whose formatted length is needed
99 : : * @return Number of characters excluding the terminating null byte
100 : : */
101 : 57 : static size_t __attribute__ ((const)) form_grouped_uintmax_len(uintmax_t val)
102 : : {
103 : 57 : size_t digits = 1U;
104 : :
105 [ + + ]: 213 : while(val >= 10U)
106 : : {
107 : 156 : val /= 10U;
108 : 156 : digits++;
109 : : }
110 : :
111 : 57 : const size_t groups = (digits - 1U) / 3U;
112 : 57 : return digits + groups;
113 : : }
114 : :
115 : : /**
116 : : *
117 : : * @brief Format 1234567.89 -> 1,234,567.89
118 : : * @details va_arg format got from https://stackoverflow.com/a/23647983/7104681
119 : : * and https://stackoverflow.com/questions/1449805/how-to-format-a-number-using-comma-as-thousands-separator-in-c
120 : : * Fractional precision is reduced silently when needed to make the formatted value fit into `result_size`.
121 : : * @param val - Any long double digit
122 : : * @return Pointer to a string
123 : : *
124 : : */
125 : 33 : const char *form_real_r(
126 : : long double val,
127 : : char *result,
128 : : size_t result_size)
129 : : {
130 [ + + + + ]: 33 : if(result == NULL || result_size == 0U)
131 : : {
132 : 2 : return "";
133 : : }
134 : :
135 : 31 : const long double magnitude = fabsl(val);
136 : :
137 [ + + ]: 31 : if(magnitude < 0.0000000001L)
138 : : {
139 : 5 : return form_write_zero(result,result_size);
140 : : }
141 : :
142 [ + + + + ]: 26 : if(!isfinite(magnitude) || magnitude > (long double)UINTMAX_MAX)
143 : : {
144 : 4 : return form_write_empty(result,result_size);
145 : : }
146 : :
147 : 22 : const uintmax_t integer_part = (uintmax_t)magnitude;
148 : 22 : long double fraction = magnitude - (long double)integer_part;
149 : 22 : unsigned char fraction_digits[10] = {0U};
150 : :
151 [ + + ]: 242 : for(size_t i = 0U; i < 10U; i++)
152 : : {
153 : 220 : fraction *= 10.0L;
154 : 220 : int digit = (int)fraction;
155 : :
156 [ - + ]: 220 : if(digit < 0)
157 : : {
158 : 0 : digit = 0;
159 [ - + ]: 220 : } else if(digit > 9){
160 : 0 : digit = 9;
161 : : }
162 : :
163 : 220 : fraction_digits[i] = (unsigned char)digit;
164 : 220 : fraction -= (long double)digit;
165 : : }
166 : :
167 : 22 : const bool negative = signbit(val) != 0;
168 : :
169 : : /* Reduce fractional precision only when needed to make the value fit. */
170 : 22 : for(size_t precision = 9U;; precision--)
171 : 36 : {
172 : 58 : uintmax_t rounded_integer_part = integer_part;
173 : 58 : unsigned char rounded_fraction_digits[9] = {0U};
174 : :
175 [ + + ]: 400 : for(size_t i = 0U; i < precision; i++)
176 : : {
177 : 342 : rounded_fraction_digits[i] = fraction_digits[i];
178 : : }
179 : :
180 : 58 : bool carry = fraction_digits[precision] >= 5U;
181 : :
182 [ + + + + ]: 90 : for(size_t i = precision; i > 0U && carry; i--)
183 : : {
184 : 32 : const size_t idx = i - 1U;
185 : 32 : const unsigned int rounded = (unsigned int)rounded_fraction_digits[idx] + 1U;
186 : 32 : rounded_fraction_digits[idx] = (unsigned char)(rounded % 10U);
187 : 32 : carry = rounded == 10U;
188 : : }
189 : :
190 [ + + ]: 58 : if(carry)
191 : : {
192 [ - + ]: 5 : if(rounded_integer_part == UINTMAX_MAX)
193 : : {
194 [ # # ]: 0 : if(precision == 0U)
195 : : {
196 : 22 : return form_write_empty(result,result_size);
197 : : }
198 : :
199 : 36 : continue;
200 : : }
201 : :
202 : 5 : rounded_integer_part++;
203 : : }
204 : :
205 : 58 : size_t fractional_len = precision;
206 : :
207 [ + + + + ]: 320 : while(fractional_len > 0U && rounded_fraction_digits[fractional_len - 1U] == 0U)
208 : : {
209 : 262 : fractional_len--;
210 : : }
211 : :
212 [ + + + + ]: 58 : if(rounded_integer_part == 0U && fractional_len == 0U)
213 : : {
214 : 1 : return form_write_zero(result,result_size);
215 : : }
216 : :
217 : 57 : const size_t integer_len = form_grouped_uintmax_len(rounded_integer_part);
218 : 57 : size_t required = integer_len + 1U;
219 : :
220 [ + + ]: 57 : if(negative)
221 : : {
222 : 3 : required++;
223 : : }
224 : :
225 [ + + ]: 57 : if(fractional_len > 0U)
226 : : {
227 : 38 : required += 1U + fractional_len;
228 : : }
229 : :
230 [ + + ]: 57 : if(required > result_size)
231 : : {
232 [ + + ]: 38 : if(precision == 0U)
233 : : {
234 : 2 : return form_write_empty(result,result_size);
235 : : }
236 : :
237 : 36 : continue;
238 : : }
239 : :
240 : 19 : char *write = result + result_size - 1U;
241 : 19 : *write = '\0';
242 : :
243 [ + + ]: 19 : if(fractional_len > 0U)
244 : : {
245 [ + + ]: 56 : for(size_t i = fractional_len; i > 0U; i--)
246 : : {
247 : 45 : *--write = (char)('0' + (int)rounded_fraction_digits[i - 1U]);
248 : : }
249 : :
250 : 11 : *--write = '.';
251 : : }
252 : :
253 : 19 : size_t group = 0U;
254 : 19 : uintmax_t integer_write_part = rounded_integer_part;
255 : :
256 : : do
257 : : {
258 [ + + ]: 88 : if(group == 3U)
259 : : {
260 : 21 : *--write = ',';
261 : 21 : group = 0U;
262 : : }
263 : :
264 : 88 : const uintmax_t digit = integer_write_part % 10U;
265 : 88 : *--write = (char)('0' + (int)digit);
266 : 88 : integer_write_part /= 10U;
267 : 88 : group++;
268 [ + + ]: 88 : } while(integer_write_part > 0U);
269 : :
270 [ + + ]: 19 : if(negative)
271 : : {
272 : 3 : *--write = '-';
273 : : }
274 : :
275 [ + + ]: 19 : if(write != result)
276 : : {
277 : 16 : const size_t used = (size_t)((result + result_size) - write);
278 : 16 : memmove(result,write,used);
279 : : }
280 : :
281 : 19 : return result;
282 : : }
283 : :
284 : : return form_write_empty(result,result_size);
285 : : }
286 : :
287 : 35 : const char *form_uintmax_r(
288 : : uintmax_t val,
289 : : char *result,
290 : : size_t result_size)
291 : : {
292 : 35 : return form_uintmax_with_sign_r(val,false,result,result_size);
293 : : }
294 : :
295 : 14 : const char *form_intmax_r(
296 : : intmax_t val,
297 : : char *result,
298 : : size_t result_size)
299 : : {
300 : 14 : const bool negative = val < 0;
301 : 14 : uintmax_t magnitude = 0U;
302 : :
303 [ + + ]: 14 : if(negative)
304 : : {
305 : 9 : magnitude = (uintmax_t)(-(val + 1)) + 1U;
306 : : } else {
307 : 5 : magnitude = (uintmax_t)val;
308 : : }
309 : :
310 : 14 : return form_uintmax_with_sign_r(magnitude,negative,result,result_size);
311 : : }
312 : :
313 : : // Example (manual smoke test)
314 : : #if 0
315 : : int main(void)
316 : : {
317 : : char buf[FORM_OUTPUT_BUFFER_SIZE];
318 : : long double digit = 837452834.94L;
319 : :
320 : : printf("digit: %s\n",form(digit,buf,sizeof(buf)));
321 : : return 0;
322 : : }
323 : : #endif
|