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