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