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