]> arthur.barton.de Git - netdata.git/blob - src/storage_number.c
Merge pull request #1998 from ktsaou/master
[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 }