]> arthur.barton.de Git - netdata.git/blob - src/storage_number.c
Merge remote-tracking branch 'upstream/master' into health
[netdata.git] / src / storage_number.c
1 #include "common.h"
2
3 extern char *print_number_lu_r(char *str, unsigned long uvalue);
4 extern char *print_number_llu_r(char *str, unsigned long long uvalue);
5
6 storage_number pack_storage_number(calculated_number value, uint32_t flags)
7 {
8         // bit 32 = sign 0:positive, 1:negative
9         // bit 31 = 0:divide, 1:multiply
10         // bit 30, 29, 28 = (multiplier or divider) 0-6 (7 total)
11         // bit 27, 26, 25 flags
12         // bit 24 to bit 1 = the value
13
14         storage_number r = get_storage_number_flags(flags);
15         if(!value) return r;
16
17         int m = 0;
18         calculated_number n = value;
19
20         // if the value is negative
21         // add the sign bit and make it positive
22         if(n < 0) {
23                 r += (1 << 31); // the sign bit 32
24                 n = -n;
25         }
26
27         // make its integer part fit in 0x00ffffff
28         // by dividing it by 10 up to 7 times
29         // and increasing the multiplier
30         while(m < 7 && n > (calculated_number)0x00ffffff) {
31                 n /= 10;
32                 m++;
33         }
34
35         if(m) {
36                 // the value was too big and we divided it
37                 // so we add a multiplier to unpack it
38                 r += (1 << 30) + (m << 27); // the multiplier m
39
40                 if(n > (calculated_number)0x00ffffff) {
41                         error("Number " CALCULATED_NUMBER_FORMAT " is too big.", value);
42                         r += 0x00ffffff;
43                         return r;
44                 }
45         }
46         else {
47                 // 0x0019999e is the number that can be multiplied
48                 // by 10 to give 0x00ffffff
49                 // while the value is below 0x0019999e we can
50                 // multiply it by 10, up to 7 times, increasing
51                 // the multiplier
52                 while(m < 7 && n < (calculated_number)0x0019999e) {
53                         n *= 10;
54                         m++;
55                 }
56
57                 // the value was small enough and we multiplied it
58                 // so we add a divider to unpack it
59                 r += (0 << 30) + (m << 27); // the divider m
60         }
61
62 #ifdef STORAGE_WITH_MATH
63         // without this there are rounding problems
64         // example: 0.9 becomes 0.89
65         r += lrint((double) n);
66 #else
67         r += (storage_number)n;
68 #endif
69
70         return r;
71 }
72
73 calculated_number unpack_storage_number(storage_number value)
74 {
75         if(!value) return 0;
76
77         int sign = 0, exp = 0;
78
79         value ^= get_storage_number_flags(value);
80
81         if(value & (1 << 31)) {
82                 sign = 1;
83                 value ^= 1 << 31;
84         }
85
86         if(value & (1 << 30)) {
87                 exp = 1;
88                 value ^= 1 << 30;
89         }
90
91         int mul = value >> 27;
92         value ^= mul << 27;
93
94         calculated_number n = value;
95
96         // fprintf(stderr, "UNPACK: %08X, sign = %d, exp = %d, mul = %d, n = " CALCULATED_NUMBER_FORMAT "\n", value, sign, exp, mul, n);
97
98         while(mul > 0) {
99                 if(exp) n *= 10;
100                 else n /= 10;
101                 mul--;
102         }
103
104         if(sign) n = -n;
105         return n;
106 }
107
108 int print_calculated_number(char *str, calculated_number value)
109 {
110         char *wstr = str;
111
112         int sign = (value < 0) ? 1 : 0;
113         if(sign) value = -value;
114
115 #ifdef STORAGE_WITH_MATH
116         // without llrint() there are rounding problems
117         // for example 0.9 becomes 0.89
118         unsigned long long uvalue = (unsigned long long int) llrint(value * (calculated_number)100000);
119 #else
120         unsigned long long uvalue = value * (calculated_number)100000;
121 #endif
122
123 #ifdef ENVIRONMENT32
124         if(uvalue > (unsigned long long)0xffffffff)
125                 wstr = print_number_llu_r(str, uvalue);
126         else
127                 wstr = print_number_lu_r(str, uvalue);
128 #else
129         do *wstr++ = (char)('0' + (uvalue % 10)); while(uvalue /= 10);
130 #endif
131
132         // make sure we have 6 bytes at least
133         while((wstr - str) < 6) *wstr++ = '0';
134
135         // put the sign back
136         if(sign) *wstr++ = '-';
137
138         // reverse it
139     char *begin = str, *end = --wstr, aux;
140     while (end > begin) aux = *end, *end-- = *begin, *begin++ = aux;
141         // wstr--;
142         // strreverse(str, wstr);
143
144         // remove trailing zeros
145         int decimal = 5;
146         while(decimal > 0 && *wstr == '0') {
147                 *wstr-- = '\0';
148                 decimal--;
149         }
150
151         // terminate it, one position to the right
152         // to let space for a dot
153         wstr[2] = '\0';
154
155         // make space for the dot
156         int i;
157         for(i = 0; i < decimal ;i++) {
158                 wstr[1] = wstr[0];
159                 wstr--;
160         }
161
162         // put the dot
163         if(wstr[2] == '\0') { wstr[1] = '\0'; decimal--; }
164         else wstr[1] = '.';
165
166         // return the buffer length
167         return (int) ((wstr - str) + 2 + decimal );
168 }