Line data Source code
1 : #include "rational.h"
2 :
3 : #define PRINT_CHECKOUT 0
4 :
5 : /**
6 : *
7 : * @brief Locale-aware format 1234567.89 -> 1,234,567.89
8 : * @details va_arg format got from https://stackoverflow.com/a/23647983/7104681
9 : * and https://stackoverflow.com/questions/1449805/how-to-format-a-number-using-comma-as-thousands-separator-in-c
10 : * @param val - Any long double digit
11 : * @return Pointer to a string
12 : *
13 : */
14 15 : const char *form(long double val)
15 : {
16 : static char result[MAX_CHARACTERS];
17 15 : result[0] = '\0'; /* Initialize buffer as empty string */
18 :
19 : // Compare the argument passed to the parent function
20 : // with zero accurate to 10 digits and output zero
21 15 : if(fabsl(val) < 0.0000000001L)
22 : {
23 6 : result[0] = '0';
24 6 : result[1] = '\0';
25 6 : return result;
26 : }
27 :
28 9 : snprintf(result,sizeof(result),"%.9Lf",val);
29 9 : char *pt = NULL;
30 :
31 : // Point pt at "."
32 30 : for(pt = result; *pt && *pt != '.'; pt++)
33 : {
34 : #if PRINT_CHECKOUT
35 : printf("pt: %p, pt addr: %zu\n",(void *)pt,(void *)pt);
36 : #endif
37 : }
38 :
39 : /*
40 : * Adding a thousand separator
41 : *
42 : */
43 :
44 : // Length of fractional part
45 9 : size_t n = (size_t)result + sizeof(result) - (size_t)pt - 1;
46 : #if PRINT_CHECKOUT
47 : printf("n: %zu\n",n);
48 : #endif
49 :
50 : // Step backwards, inserting spaces
51 : while(true)
52 2 : {
53 11 : if(pt <= result + 3)
54 : {
55 9 : break;
56 : }
57 :
58 2 : char *insertion_point = pt - 3; // shift 3 digits
59 :
60 2 : if(*(insertion_point-1) == '-')
61 : {
62 0 : break;
63 : }
64 :
65 2 : memmove(insertion_point + 1,insertion_point,n);
66 : #if PRINT_CHECKOUT
67 : printf("pt: %p, pt addr: %zu\n",(void *)insertion_point,(void *)insertion_point);
68 : #endif
69 : // memmove allows overlap, unlike memcpy
70 2 : *insertion_point = ','; // thousand separator
71 2 : n += 4; // 3 digits + separator
72 2 : pt = insertion_point;
73 : }
74 :
75 : #if PRINT_CHECKOUT
76 : printf("n: %zu\n",n);
77 : #endif
78 :
79 9 : char *ptr = strchr(result,'.');
80 : #if PRINT_CHECKOUT
81 : printf("ptr: %p, ptr addr: %zu\n",(void *)ptr,(void *)ptr);
82 : #endif
83 :
84 9 : if(ptr!=NULL)
85 : {
86 : // Either val is real or integer
87 9 : bool val_is_real = false;
88 :
89 90 : for(pt = ptr+1; pt < (result + sizeof(result)) && *pt != '\0'; pt++)
90 : {
91 81 : if(*pt != '0')
92 : {
93 0 : val_is_real = true;
94 0 : break;
95 : }
96 : }
97 :
98 9 : if(val_is_real)
99 : {
100 0 : *ptr = '.';
101 :
102 : // Remove unnecessary terminated zeroes
103 0 : for(pt = (result + sizeof(result)) - 1; pt >= ptr; --pt)
104 : {
105 : #if PRINT_CHECKOUT
106 : printf("pt: %p, pt addr: %zu, ptr: %p, ptr addr: %zu\n",(void *)pt,(void *)pt,(void *)ptr,(void *)ptr);
107 : #endif
108 :
109 0 : if(*pt == '\0' || *pt == '0' || *pt == ',')
110 : {
111 0 : *pt = '\0';
112 : } else {
113 : break;
114 : }
115 : }
116 : #if 0
117 : printf("\n");
118 : #endif
119 : } else {
120 9 : *ptr = '\0';
121 : }
122 : }
123 :
124 9 : return(result);
125 : }
126 :
127 : // Test
128 : #if 0
129 : int main(void)
130 : {
131 : double digit = 837452834.94;
132 : printf("digit: %s\n",form(digit));
133 : }
134 : #endif
|