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