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