storage_number.c 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "../libnetdata.h"
  3. bool is_system_ieee754_double(void) {
  4. static bool logged = false;
  5. struct {
  6. NETDATA_DOUBLE original;
  7. union {
  8. uint64_t i;
  9. NETDATA_DOUBLE d;
  10. };
  11. } tests[] = {
  12. { .original = 1.25, .i = 0x3FF4000000000000 },
  13. { .original = 1.0, .i = 0x3FF0000000000000 },
  14. { .original = 2.0, .i = 0x4000000000000000 },
  15. { .original = 4.0, .i = 0x4010000000000000 },
  16. { .original = 8.8, .i = 0x402199999999999A },
  17. { .original = 16.16, .i = 0x403028F5C28F5C29 },
  18. { .original = 32.32, .i = 0x404028F5C28F5C29 },
  19. { .original = 64.64, .i = 0x405028F5C28F5C29 },
  20. { .original = 128.128, .i = 0x406004189374BC6A },
  21. { .original = 32768.32768, .i = 0x40E0000A7C5AC472 },
  22. { .original = 65536.65536, .i = 0x40F0000A7C5AC472 },
  23. { .original = -65536.65536, .i = 0xC0F0000A7C5AC472 },
  24. { .original = 65535.65535, .i = 0x40EFFFF4F8A0902E },
  25. { .original = -65535.65535, .i = 0xC0EFFFF4F8A0902E },
  26. { .original = 4.503599627e15, .i = 0x432FFFFFFFF4B180 },
  27. { .original = -4.503599627e15, .i = 0xC32FFFFFFFF4B180 },
  28. { .original = 1.25e25, .i = 0x4524ADF4B7320335 },
  29. { .original = 1.25e307, .i = 0x7FB1CCF385EBC8A0 },
  30. { .original = 1.25e-25, .i = 0x3AC357C299A88EA7 },
  31. { .original = 1.25e-100, .i = 0x2B317F7D4ED8C33E },
  32. { .original = NAN, .i = 0x7FF8000000000000 },
  33. { .original = -INFINITY, .i = 0xFFF0000000000000 },
  34. { .original = INFINITY, .i = 0x7FF0000000000000 },
  35. { .original = 1.25e-132, .i = 0x248C6463225AB7EC },
  36. { .original = 0.0, .i = 0x0000000000000000 },
  37. { .original = -0.0, .i = 0x8000000000000000 },
  38. { .original = DBL_MIN, .i = 0x0010000000000000 },
  39. { .original = DBL_MAX, .i = 0x7FEFFFFFFFFFFFFF },
  40. { .original = -DBL_MIN, .i = 0x8010000000000000 },
  41. { .original = -DBL_MAX, .i = 0xFFEFFFFFFFFFFFFF },
  42. };
  43. size_t errors = 0;
  44. size_t elements = sizeof(tests) / sizeof(tests[0]);
  45. for(size_t i = 0; i < elements ; i++) {
  46. uint64_t *ptr = (uint64_t *)&tests[i].original;
  47. if(*ptr != tests[i].i && (tests[i].original == tests[i].d || (isnan(tests[i].original) && isnan(tests[i].d)))) {
  48. if(!logged)
  49. netdata_log_info("IEEE754: test #%zu, value " NETDATA_DOUBLE_FORMAT_G " is represented in this system as %lX, but it was expected as %lX",
  50. i+1, tests[i].original, *ptr, tests[i].i);
  51. errors++;
  52. }
  53. }
  54. if(!errors && sizeof(NETDATA_DOUBLE) == sizeof(uint64_t)) {
  55. if(!logged)
  56. netdata_log_info("IEEE754: system is using IEEE754 DOUBLE PRECISION values");
  57. logged = true;
  58. return true;
  59. }
  60. else {
  61. if(!logged)
  62. netdata_log_info("IEEE754: system is NOT compatible with IEEE754 DOUBLE PRECISION values");
  63. logged = true;
  64. return false;
  65. }
  66. }
  67. storage_number pack_storage_number(NETDATA_DOUBLE value, SN_FLAGS flags) {
  68. // bit 32 = sign 0:positive, 1:negative
  69. // bit 31 = 0:divide, 1:multiply
  70. // bit 30, 29, 28 = (multiplier or divider) 0-7 (8 total)
  71. // bit 27 SN_EXISTS_100
  72. // bit 26 SN_EXISTS_RESET
  73. // bit 25 SN_ANOMALY_BIT = 0: anomalous, 1: not anomalous
  74. // bit 24 to bit 1 = the value
  75. if(unlikely(fpclassify(value) == FP_NAN || fpclassify(value) == FP_INFINITE))
  76. return SN_EMPTY_SLOT;
  77. storage_number r = flags & SN_USER_FLAGS;
  78. if(unlikely(fpclassify(value) == FP_ZERO || fpclassify(value) == FP_SUBNORMAL))
  79. return r;
  80. int m = 0;
  81. NETDATA_DOUBLE n = value, factor = 10;
  82. // if the value is negative
  83. // add the sign bit and make it positive
  84. if(n < 0) {
  85. r += SN_FLAG_NEGATIVE; // the sign bit 32
  86. n = -n;
  87. }
  88. if(n / 10000000.0 > 0x00ffffff) {
  89. factor = 100;
  90. r |= SN_FLAG_NOT_EXISTS_MUL100;
  91. }
  92. // make its integer part fit in 0x00ffffff
  93. // by dividing it by 10 up to 7 times
  94. // and increasing the multiplier
  95. while(m < 7 && n > (NETDATA_DOUBLE)0x00ffffff) {
  96. n /= factor;
  97. m++;
  98. }
  99. if(m) {
  100. // the value was too big, and we divided it
  101. // so, we add a multiplier to unpack it
  102. r += SN_FLAG_MULTIPLY + (m << 27); // the multiplier m
  103. if(n > (NETDATA_DOUBLE)0x00ffffff) {
  104. #ifdef NETDATA_INTERNAL_CHECKS
  105. netdata_log_error("Number " NETDATA_DOUBLE_FORMAT " is too big.", value);
  106. #endif
  107. r += 0x00ffffff;
  108. return r;
  109. }
  110. }
  111. else {
  112. // 0x0019999e is the number that can be multiplied
  113. // by 10 to give 0x00ffffff
  114. // while the value is below 0x0019999e we can
  115. // multiply it by 10, up to 7 times, increasing
  116. // the multiplier
  117. while(m < 7 && n < (NETDATA_DOUBLE)0x0019999e) {
  118. n *= 10;
  119. m++;
  120. }
  121. if (unlikely(n > (NETDATA_DOUBLE)0x00ffffff)) {
  122. n /= 10;
  123. m--;
  124. }
  125. // the value was small enough, and we multiplied it
  126. // so, we add a divider to unpack it
  127. r += (m << 27); // the divider m
  128. }
  129. #ifdef STORAGE_WITH_MATH
  130. // without this there are rounding problems
  131. // example: 0.9 becomes 0.89
  132. r += lrint((double) n);
  133. #else
  134. r += (storage_number)n;
  135. #endif
  136. return r;
  137. }
  138. // Lookup table to make storage number unpacking efficient.
  139. NETDATA_DOUBLE unpack_storage_number_lut10x[4 * 8];
  140. __attribute__((constructor)) void initialize_lut(void) {
  141. // The lookup table is partitioned in 4 subtables based on the
  142. // values of the factor and exp bits.
  143. for (int i = 0; i < 8; i++) {
  144. // factor = 0
  145. unpack_storage_number_lut10x[0 * 8 + i] = 1 / pow(10, i); // exp = 0
  146. unpack_storage_number_lut10x[1 * 8 + i] = pow(10, i); // exp = 1
  147. // factor = 1
  148. unpack_storage_number_lut10x[2 * 8 + i] = 1 / pow(100, i); // exp = 0
  149. unpack_storage_number_lut10x[3 * 8 + i] = pow(100, i); // exp = 1
  150. }
  151. }
  152. /*
  153. int print_netdata_double(char *str, NETDATA_DOUBLE value)
  154. {
  155. char *wstr = str;
  156. int sign = (value < 0) ? 1 : 0;
  157. if(sign) value = -value;
  158. #ifdef STORAGE_WITH_MATH
  159. // without llrintl() there are rounding problems
  160. // for example 0.9 becomes 0.89
  161. unsigned long long uvalue = (unsigned long long int) llrintl(value * (NETDATA_DOUBLE)100000);
  162. #else
  163. unsigned long long uvalue = value * (NETDATA_DOUBLE)100000;
  164. #endif
  165. wstr = print_number_llu_r_smart(str, uvalue);
  166. // make sure we have 6 bytes at least
  167. while((wstr - str) < 6) *wstr++ = '0';
  168. // put the sign back
  169. if(sign) *wstr++ = '-';
  170. // reverse it
  171. char *begin = str, *end = --wstr, aux;
  172. while (end > begin) aux = *end, *end-- = *begin, *begin++ = aux;
  173. // wstr--;
  174. // strreverse(str, wstr);
  175. // remove trailing zeros
  176. int decimal = 5;
  177. while(decimal > 0 && *wstr == '0') {
  178. *wstr-- = '\0';
  179. decimal--;
  180. }
  181. // terminate it, one position to the right
  182. // to let space for a dot
  183. wstr[2] = '\0';
  184. // make space for the dot
  185. int i;
  186. for(i = 0; i < decimal ;i++) {
  187. wstr[1] = wstr[0];
  188. wstr--;
  189. }
  190. // put the dot
  191. if(wstr[2] == '\0') { wstr[1] = '\0'; decimal--; }
  192. else wstr[1] = '.';
  193. // return the buffer length
  194. return (int) ((wstr - str) + 2 + decimal );
  195. }
  196. */