]> arthur.barton.de Git - netdata.git/blob - src/storage_number.c
build: migrate to autotools
[netdata.git] / src / storage_number.c
1 #ifdef HAVE_CONFIG_H
2 #include <config.h>
3 #endif
4 #ifdef STORAGE_WITH_MATH
5 #include <math.h>
6 #endif
7
8 #include "common.h"
9 #include "log.h"
10 #include "storage_number.h"
11
12 #if __GNUC__
13 #if __x86_64__ || __ppc64__
14 #define ENVIRONMENT64
15 #else
16 #define ENVIRONMENT32
17 #endif
18 #endif
19
20 storage_number pack_storage_number(calculated_number value, uint32_t flags)
21 {
22         // bit 32 = sign 0:positive, 1:negative
23         // bit 31 = 0:divide, 1:multiply
24         // bit 30, 29, 28 = (multiplier or divider) 0-6 (7 total)
25         // bit 27, 26, 25 flags
26         // bit 24 to bit 1 = the value
27
28         storage_number r = get_storage_number_flags(flags);
29         if(!value) return r;
30
31         int m = 0;
32         calculated_number n = value;
33
34         // if the value is negative
35         // add the sign bit and make it positive
36         if(n < 0) {
37                 r += (1 << 31); // the sign bit 32
38                 n = -n;
39         }
40
41         // make its integer part fit in 0x00ffffff
42         // by dividing it by 10 up to 7 times
43         // and increasing the multiplier
44         while(m < 7 && n > (calculated_number)0x00ffffff) {
45                 n /= 10;
46                 m++;
47         }
48
49         if(m) {
50                 // the value was too big and we divided it
51                 // so we add a multiplier to unpack it
52                 r += (1 << 30) + (m << 27); // the multiplier m
53
54                 if(n > (calculated_number)0x00ffffff) {
55                         error("Number " CALCULATED_NUMBER_FORMAT " is too big.", value);
56                         r += 0x00ffffff;
57                         return r;
58                 }
59         }
60         else {
61                 // 0x0019999e is the number that can be multiplied
62                 // by 10 to give 0x00ffffff
63                 // while the value is below 0x0019999e we can
64                 // multiply it by 10, up to 7 times, increasing
65                 // the multiplier
66                 while(m < 7 && n < (calculated_number)0x0019999e) {
67                         n *= 10;
68                         m++;
69                 }
70
71                 // the value was small enough and we multiplied it
72                 // so we add a divider to unpack it
73                 r += (0 << 30) + (m << 27); // the divider m
74         }
75
76 #ifdef STORAGE_WITH_MATH
77         // without this there are rounding problems
78         // example: 0.9 becomes 0.89
79         r += lrint(n);
80 #else
81         r += (storage_number)n;
82 #endif
83
84         return r;
85 }
86
87 calculated_number unpack_storage_number(storage_number value)
88 {
89         if(!value) return 0;
90
91         int sign = 0, exp = 0;
92
93         value ^= get_storage_number_flags(value);
94
95         if(value & (1 << 31)) {
96                 sign = 1;
97                 value ^= 1 << 31;
98         }
99
100         if(value & (1 << 30)) {
101                 exp = 1;
102                 value ^= 1 << 30;
103         }
104
105         int mul = value >> 27;
106         value ^= mul << 27;
107
108         calculated_number n = value;
109
110         // fprintf(stderr, "UNPACK: %08X, sign = %d, exp = %d, mul = %d, n = " CALCULATED_NUMBER_FORMAT "\n", value, sign, exp, mul, n);
111
112         while(mul > 0) {
113                 if(exp) n *= 10;
114                 else n /= 10;
115                 mul--;
116         }
117
118         if(sign) n = -n;
119         return n;
120 }
121
122 #ifdef ENVIRONMENT32
123 // This trick seems to give an 80% speed increase in 32bit systems
124 // print_calculated_number_llu_r() will just print the digits up to the
125 // point the remaining value fits in 32 bits, and then calls
126 // print_calculated_number_lu_r() to print the rest with 32 bit arithmetic.
127
128 static char *print_calculated_number_lu_r(char *str, unsigned long uvalue) {
129         char *wstr = str;
130         
131         // print each digit
132         do *wstr++ = (char)(48 + (uvalue % 10)); while(uvalue /= 10);
133         return wstr;
134 }
135
136 static char *print_calculated_number_llu_r(char *str, unsigned long long uvalue) {
137         char *wstr = str;
138
139         // print each digit
140         do *wstr++ = (char)(48 + (uvalue % 10)); while((uvalue /= 10) && uvalue > (unsigned long long)0xffffffff);
141         if(uvalue) return print_calculated_number_lu_r(wstr, uvalue);
142         return wstr;
143 }
144 #endif
145
146 int print_calculated_number(char *str, calculated_number value)
147 {
148         char *wstr = str;
149
150         int sign = (value < 0) ? 1 : 0;
151         if(sign) value = -value;
152
153 #ifdef STORAGE_WITH_MATH
154         // without llrint() there are rounding problems
155         // for example 0.9 becomes 0.89
156         unsigned long long uvalue = llrint(value * (calculated_number)100000);
157 #else
158         unsigned long long uvalue = value * (calculated_number)100000;
159 #endif
160
161 #ifdef ENVIRONMENT32
162         if(uvalue > (unsigned long long)0xffffffff)
163                 wstr = print_calculated_number_llu_r(str, uvalue);
164         else
165                 wstr = print_calculated_number_lu_r(str, uvalue);
166 #else
167         do *wstr++ = (char)(48 + (uvalue % 10)); while(uvalue /= 10);
168 #endif
169
170         // make sure we have 6 bytes at least
171         while((wstr - str) < 6) *wstr++ = '0';
172
173         // put the sign back
174         if(sign) *wstr++ = '-';
175
176         // reverse it
177     char *begin = str, *end = --wstr, aux;
178     while (end > begin) aux = *end, *end-- = *begin, *begin++ = aux;
179         // wstr--;
180         // strreverse(str, wstr);
181
182         // remove trailing zeros
183         int decimal = 5;
184         while(decimal > 0 && *wstr == '0') {
185                 *wstr-- = '\0';
186                 decimal--;
187         }
188
189         // terminate it, one position to the right
190         // to let space for a dot
191         wstr[2] = '\0';
192
193         // make space for the dot
194         int i;
195         for(i = 0; i < decimal ;i++) {
196                 wstr[1] = wstr[0];
197                 wstr--;
198         }
199
200         // put the dot
201         if(wstr[2] == '\0') { wstr[1] = '\0'; decimal--; }
202         else wstr[1] = '.';
203
204         // return the buffer length
205         return ( (wstr - str) + 2 + decimal );
206 }