storage_number.c 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "../libnetdata.h"
  3. #define get_storage_number_flags(value) \
  4. ((((storage_number)(value)) & (1 << 24)) | \
  5. (((storage_number)(value)) & (1 << 25)) | \
  6. (((storage_number)(value)) & (1 << 26)))
  7. storage_number pack_storage_number(calculated_number value, uint32_t flags) {
  8. // bit 32 = sign 0:positive, 1:negative
  9. // bit 31 = 0:divide, 1:multiply
  10. // bit 30, 29, 28 = (multiplier or divider) 0-7 (8 total)
  11. // bit 27 SN_EXISTS_100
  12. // bit 26 SN_EXISTS_RESET
  13. // bit 25 SN_ANOMALY_BIT = 0: anomalous, 1: not anomalous
  14. // bit 24 to bit 1 = the value
  15. storage_number r = get_storage_number_flags(flags);
  16. if(!value)
  17. goto RET_SN;
  18. int m = 0;
  19. calculated_number n = value, factor = 10;
  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. if(n / 10000000.0 > 0x00ffffff) {
  27. factor = 100;
  28. r |= SN_EXISTS_100;
  29. }
  30. // make its integer part fit in 0x00ffffff
  31. // by dividing it by 10 up to 7 times
  32. // and increasing the multiplier
  33. while(m < 7 && n > (calculated_number)0x00ffffff) {
  34. n /= factor;
  35. m++;
  36. }
  37. if(m) {
  38. // the value was too big and we divided it
  39. // so we add a multiplier to unpack it
  40. r += (1 << 30) + (m << 27); // the multiplier m
  41. if(n > (calculated_number)0x00ffffff) {
  42. #ifdef NETDATA_INTERNAL_CHECKS
  43. error("Number " CALCULATED_NUMBER_FORMAT " is too big.", value);
  44. #endif
  45. r += 0x00ffffff;
  46. goto RET_SN;
  47. }
  48. }
  49. else {
  50. // 0x0019999e is the number that can be multiplied
  51. // by 10 to give 0x00ffffff
  52. // while the value is below 0x0019999e we can
  53. // multiply it by 10, up to 7 times, increasing
  54. // the multiplier
  55. while(m < 7 && n < (calculated_number)0x0019999e) {
  56. n *= 10;
  57. m++;
  58. }
  59. if (unlikely(n > (calculated_number) (0x00ffffff))) {
  60. n /= 10;
  61. m--;
  62. }
  63. // the value was small enough and we multiplied it
  64. // so we add a divider to unpack it
  65. r += (0 << 30) + (m << 27); // the divider m
  66. }
  67. #ifdef STORAGE_WITH_MATH
  68. // without this there are rounding problems
  69. // example: 0.9 becomes 0.89
  70. r += lrint((double) n);
  71. #else
  72. r += (storage_number)n;
  73. #endif
  74. RET_SN:
  75. if (r == SN_EMPTY_SLOT)
  76. r = SN_ANOMALOUS_ZERO;
  77. return r;
  78. }
  79. // Lookup table to make storage number unpacking efficient.
  80. calculated_number unpack_storage_number_lut10x[4 * 8];
  81. __attribute__((constructor)) void initialize_lut(void) {
  82. // The lookup table is partitioned in 4 subtables based on the
  83. // values of the factor and exp bits.
  84. for (int i = 0; i < 8; i++) {
  85. // factor = 0
  86. unpack_storage_number_lut10x[0 * 8 + i] = 1 / pow(10, i); // exp = 0
  87. unpack_storage_number_lut10x[1 * 8 + i] = pow(10, i); // exp = 1
  88. // factor = 1
  89. unpack_storage_number_lut10x[2 * 8 + i] = 1 / pow(100, i); // exp = 0
  90. unpack_storage_number_lut10x[3 * 8 + i] = pow(100, i); // exp = 1
  91. }
  92. }
  93. /*
  94. int print_calculated_number(char *str, calculated_number value)
  95. {
  96. char *wstr = str;
  97. int sign = (value < 0) ? 1 : 0;
  98. if(sign) value = -value;
  99. #ifdef STORAGE_WITH_MATH
  100. // without llrintl() there are rounding problems
  101. // for example 0.9 becomes 0.89
  102. unsigned long long uvalue = (unsigned long long int) llrintl(value * (calculated_number)100000);
  103. #else
  104. unsigned long long uvalue = value * (calculated_number)100000;
  105. #endif
  106. wstr = print_number_llu_r_smart(str, uvalue);
  107. // make sure we have 6 bytes at least
  108. while((wstr - str) < 6) *wstr++ = '0';
  109. // put the sign back
  110. if(sign) *wstr++ = '-';
  111. // reverse it
  112. char *begin = str, *end = --wstr, aux;
  113. while (end > begin) aux = *end, *end-- = *begin, *begin++ = aux;
  114. // wstr--;
  115. // strreverse(str, wstr);
  116. // remove trailing zeros
  117. int decimal = 5;
  118. while(decimal > 0 && *wstr == '0') {
  119. *wstr-- = '\0';
  120. decimal--;
  121. }
  122. // terminate it, one position to the right
  123. // to let space for a dot
  124. wstr[2] = '\0';
  125. // make space for the dot
  126. int i;
  127. for(i = 0; i < decimal ;i++) {
  128. wstr[1] = wstr[0];
  129. wstr--;
  130. }
  131. // put the dot
  132. if(wstr[2] == '\0') { wstr[1] = '\0'; decimal--; }
  133. else wstr[1] = '.';
  134. // return the buffer length
  135. return (int) ((wstr - str) + 2 + decimal );
  136. }
  137. */
  138. int print_calculated_number(char *str, calculated_number value) {
  139. // info("printing number " CALCULATED_NUMBER_FORMAT, value);
  140. char integral_str[50], fractional_str[50];
  141. char *wstr = str;
  142. if(unlikely(value < 0)) {
  143. *wstr++ = '-';
  144. value = -value;
  145. }
  146. calculated_number integral, fractional;
  147. #ifdef STORAGE_WITH_MATH
  148. fractional = calculated_number_modf(value, &integral) * 10000000.0;
  149. #else
  150. fractional = ((unsigned long long)(value * 10000000ULL) % 10000000ULL);
  151. #endif
  152. unsigned long long integral_int = (unsigned long long)integral;
  153. unsigned long long fractional_int = (unsigned long long)calculated_number_llrint(fractional);
  154. if(unlikely(fractional_int >= 10000000)) {
  155. integral_int += 1;
  156. fractional_int -= 10000000;
  157. }
  158. // info("integral " CALCULATED_NUMBER_FORMAT " (%llu), fractional " CALCULATED_NUMBER_FORMAT " (%llu)", integral, integral_int, fractional, fractional_int);
  159. char *istre;
  160. if(unlikely(integral_int == 0)) {
  161. integral_str[0] = '0';
  162. istre = &integral_str[1];
  163. }
  164. else
  165. // convert the integral part to string (reversed)
  166. istre = print_number_llu_r_smart(integral_str, integral_int);
  167. // copy reversed the integral string
  168. istre--;
  169. while( istre >= integral_str ) *wstr++ = *istre--;
  170. if(likely(fractional_int != 0)) {
  171. // add a dot
  172. *wstr++ = '.';
  173. // convert the fractional part to string (reversed)
  174. char *fstre = print_number_llu_r_smart(fractional_str, fractional_int);
  175. // prepend zeros to reach 7 digits length
  176. int decimal = 7;
  177. int len = (int)(fstre - fractional_str);
  178. while(len < decimal) {
  179. *wstr++ = '0';
  180. len++;
  181. }
  182. char *begin = fractional_str;
  183. while(begin < fstre && *begin == '0') begin++;
  184. // copy reversed the fractional string
  185. fstre--;
  186. while( fstre >= begin ) *wstr++ = *fstre--;
  187. }
  188. *wstr = '\0';
  189. // info("printed number '%s'", str);
  190. return (int)(wstr - str);
  191. }