1 1.1 christos #include "test/jemalloc_test.h" 2 1.1 christos 3 1.1 christos #include "jemalloc/internal/fxp.h" 4 1.1 christos 5 1.1 christos static double 6 1.1 christos fxp2double(fxp_t a) { 7 1.1 christos double intpart = (double)(a >> 16); 8 1.1 christos double fracpart = (double)(a & ((1U << 16) - 1)) / (1U << 16); 9 1.1 christos return intpart + fracpart; 10 1.1 christos } 11 1.1 christos 12 1.1 christos /* Is a close to b? */ 13 1.1 christos static bool 14 1.1 christos double_close(double a, double b) { 15 1.1 christos /* 16 1.1 christos * Our implementation doesn't try for precision. Correspondingly, don't 17 1.1 christos * enforce it too strenuously here; accept values that are close in 18 1.1 christos * either relative or absolute terms. 19 1.1 christos */ 20 1.1 christos return fabs(a - b) < 0.01 || fabs(a - b) / a < 0.01; 21 1.1 christos } 22 1.1 christos 23 1.1 christos static bool 24 1.1 christos fxp_close(fxp_t a, fxp_t b) { 25 1.1 christos return double_close(fxp2double(a), fxp2double(b)); 26 1.1 christos } 27 1.1 christos 28 1.1 christos static fxp_t 29 1.1 christos xparse_fxp(const char *str) { 30 1.1 christos fxp_t result; 31 1.1.1.2 christos bool err = fxp_parse(&result, str, NULL); 32 1.1 christos assert_false(err, "Invalid fxp string: %s", str); 33 1.1 christos return result; 34 1.1 christos } 35 1.1 christos 36 1.1 christos static void 37 1.1 christos expect_parse_accurate(const char *str, const char *parse_str) { 38 1.1 christos double true_val = strtod(str, NULL); 39 1.1.1.2 christos fxp_t fxp_val; 40 1.1.1.2 christos char *end; 41 1.1.1.2 christos bool err = fxp_parse(&fxp_val, parse_str, &end); 42 1.1 christos expect_false(err, "Unexpected parse failure"); 43 1.1.1.2 christos expect_ptr_eq( 44 1.1.1.2 christos parse_str + strlen(str), end, "Didn't parse whole string"); 45 1.1.1.2 christos expect_true( 46 1.1.1.2 christos double_close(fxp2double(fxp_val), true_val), "Misparsed %s", str); 47 1.1 christos } 48 1.1 christos 49 1.1 christos static void 50 1.1 christos parse_valid_trial(const char *str) { 51 1.1 christos /* The value it parses should be correct. */ 52 1.1 christos expect_parse_accurate(str, str); 53 1.1 christos char buf[100]; 54 1.1 christos snprintf(buf, sizeof(buf), "%swith_some_trailing_text", str); 55 1.1 christos expect_parse_accurate(str, buf); 56 1.1 christos snprintf(buf, sizeof(buf), "%s with a space", str); 57 1.1 christos expect_parse_accurate(str, buf); 58 1.1 christos snprintf(buf, sizeof(buf), "%s,in_a_malloc_conf_string:1", str); 59 1.1 christos expect_parse_accurate(str, buf); 60 1.1 christos } 61 1.1 christos 62 1.1 christos TEST_BEGIN(test_parse_valid) { 63 1.1 christos parse_valid_trial("0"); 64 1.1 christos parse_valid_trial("1"); 65 1.1 christos parse_valid_trial("2"); 66 1.1 christos parse_valid_trial("100"); 67 1.1 christos parse_valid_trial("345"); 68 1.1 christos parse_valid_trial("00000000123"); 69 1.1 christos parse_valid_trial("00000000987"); 70 1.1 christos 71 1.1 christos parse_valid_trial("0.0"); 72 1.1 christos parse_valid_trial("0.00000000000456456456"); 73 1.1 christos parse_valid_trial("100.00000000000456456456"); 74 1.1 christos 75 1.1 christos parse_valid_trial("123.1"); 76 1.1 christos parse_valid_trial("123.01"); 77 1.1 christos parse_valid_trial("123.001"); 78 1.1 christos parse_valid_trial("123.0001"); 79 1.1 christos parse_valid_trial("123.00001"); 80 1.1 christos parse_valid_trial("123.000001"); 81 1.1 christos parse_valid_trial("123.0000001"); 82 1.1 christos 83 1.1 christos parse_valid_trial(".0"); 84 1.1 christos parse_valid_trial(".1"); 85 1.1 christos parse_valid_trial(".01"); 86 1.1 christos parse_valid_trial(".001"); 87 1.1 christos parse_valid_trial(".0001"); 88 1.1 christos parse_valid_trial(".00001"); 89 1.1 christos parse_valid_trial(".000001"); 90 1.1 christos 91 1.1 christos parse_valid_trial(".1"); 92 1.1 christos parse_valid_trial(".10"); 93 1.1 christos parse_valid_trial(".100"); 94 1.1 christos parse_valid_trial(".1000"); 95 1.1 christos parse_valid_trial(".100000"); 96 1.1 christos } 97 1.1 christos TEST_END 98 1.1 christos 99 1.1 christos static void 100 1.1 christos expect_parse_failure(const char *str) { 101 1.1 christos fxp_t result = FXP_INIT_INT(333); 102 1.1 christos char *end = (void *)0x123; 103 1.1.1.2 christos bool err = fxp_parse(&result, str, &end); 104 1.1 christos expect_true(err, "Expected a parse error on: %s", str); 105 1.1.1.2 christos expect_ptr_eq( 106 1.1.1.2 christos (void *)0x123, end, "Parse error shouldn't change results"); 107 1.1.1.2 christos expect_u32_eq( 108 1.1.1.2 christos result, FXP_INIT_INT(333), "Parse error shouldn't change results"); 109 1.1 christos } 110 1.1 christos 111 1.1 christos TEST_BEGIN(test_parse_invalid) { 112 1.1 christos expect_parse_failure("123."); 113 1.1 christos expect_parse_failure("3.a"); 114 1.1 christos expect_parse_failure(".a"); 115 1.1 christos expect_parse_failure("a.1"); 116 1.1 christos expect_parse_failure("a"); 117 1.1 christos /* A valid string, but one that overflows. */ 118 1.1 christos expect_parse_failure("123456789"); 119 1.1 christos expect_parse_failure("0000000123456789"); 120 1.1 christos expect_parse_failure("1000000"); 121 1.1 christos } 122 1.1 christos TEST_END 123 1.1 christos 124 1.1 christos static void 125 1.1 christos expect_init_percent(unsigned percent, const char *str) { 126 1.1 christos fxp_t result_init = FXP_INIT_PERCENT(percent); 127 1.1 christos fxp_t result_parse = xparse_fxp(str); 128 1.1 christos expect_u32_eq(result_init, result_parse, 129 1.1 christos "Expect representations of FXP_INIT_PERCENT(%u) and " 130 1.1 christos "fxp_parse(\"%s\") to be equal; got %x and %x", 131 1.1 christos percent, str, result_init, result_parse); 132 1.1 christos } 133 1.1 christos 134 1.1 christos /* 135 1.1 christos * Every other test uses either parsing or FXP_INIT_INT; it gets tested in those 136 1.1 christos * ways. We need a one-off for the percent-based initialization, though. 137 1.1 christos */ 138 1.1 christos TEST_BEGIN(test_init_percent) { 139 1.1 christos expect_init_percent(100, "1"); 140 1.1 christos expect_init_percent(75, ".75"); 141 1.1 christos expect_init_percent(1, ".01"); 142 1.1 christos expect_init_percent(50, ".5"); 143 1.1 christos } 144 1.1 christos TEST_END 145 1.1 christos 146 1.1 christos static void 147 1.1.1.2 christos expect_add(const char *astr, const char *bstr, const char *resultstr) { 148 1.1 christos fxp_t a = xparse_fxp(astr); 149 1.1 christos fxp_t b = xparse_fxp(bstr); 150 1.1 christos fxp_t result = xparse_fxp(resultstr); 151 1.1.1.2 christos expect_true(fxp_close(fxp_add(a, b), result), "Expected %s + %s == %s", 152 1.1.1.2 christos astr, bstr, resultstr); 153 1.1 christos } 154 1.1 christos 155 1.1 christos TEST_BEGIN(test_add_simple) { 156 1.1 christos expect_add("0", "0", "0"); 157 1.1 christos expect_add("0", "1", "1"); 158 1.1 christos expect_add("1", "1", "2"); 159 1.1 christos expect_add("1.5", "1.5", "3"); 160 1.1 christos expect_add("0.1", "0.1", "0.2"); 161 1.1 christos expect_add("123", "456", "579"); 162 1.1 christos } 163 1.1 christos TEST_END 164 1.1 christos 165 1.1 christos static void 166 1.1.1.2 christos expect_sub(const char *astr, const char *bstr, const char *resultstr) { 167 1.1 christos fxp_t a = xparse_fxp(astr); 168 1.1 christos fxp_t b = xparse_fxp(bstr); 169 1.1 christos fxp_t result = xparse_fxp(resultstr); 170 1.1.1.2 christos expect_true(fxp_close(fxp_sub(a, b), result), "Expected %s - %s == %s", 171 1.1.1.2 christos astr, bstr, resultstr); 172 1.1 christos } 173 1.1 christos 174 1.1 christos TEST_BEGIN(test_sub_simple) { 175 1.1 christos expect_sub("0", "0", "0"); 176 1.1 christos expect_sub("1", "0", "1"); 177 1.1 christos expect_sub("1", "1", "0"); 178 1.1 christos expect_sub("3.5", "1.5", "2"); 179 1.1 christos expect_sub("0.3", "0.1", "0.2"); 180 1.1 christos expect_sub("456", "123", "333"); 181 1.1 christos } 182 1.1 christos TEST_END 183 1.1 christos 184 1.1 christos static void 185 1.1.1.2 christos expect_mul(const char *astr, const char *bstr, const char *resultstr) { 186 1.1 christos fxp_t a = xparse_fxp(astr); 187 1.1 christos fxp_t b = xparse_fxp(bstr); 188 1.1 christos fxp_t result = xparse_fxp(resultstr); 189 1.1.1.2 christos expect_true(fxp_close(fxp_mul(a, b), result), "Expected %s * %s == %s", 190 1.1.1.2 christos astr, bstr, resultstr); 191 1.1 christos } 192 1.1 christos 193 1.1 christos TEST_BEGIN(test_mul_simple) { 194 1.1 christos expect_mul("0", "0", "0"); 195 1.1 christos expect_mul("1", "0", "0"); 196 1.1 christos expect_mul("1", "1", "1"); 197 1.1 christos expect_mul("1.5", "1.5", "2.25"); 198 1.1 christos expect_mul("100.0", "10", "1000"); 199 1.1 christos expect_mul(".1", "10", "1"); 200 1.1 christos } 201 1.1 christos TEST_END 202 1.1 christos 203 1.1 christos static void 204 1.1.1.2 christos expect_div(const char *astr, const char *bstr, const char *resultstr) { 205 1.1 christos fxp_t a = xparse_fxp(astr); 206 1.1 christos fxp_t b = xparse_fxp(bstr); 207 1.1 christos fxp_t result = xparse_fxp(resultstr); 208 1.1.1.2 christos expect_true(fxp_close(fxp_div(a, b), result), "Expected %s / %s == %s", 209 1.1.1.2 christos astr, bstr, resultstr); 210 1.1 christos } 211 1.1 christos 212 1.1 christos TEST_BEGIN(test_div_simple) { 213 1.1 christos expect_div("1", "1", "1"); 214 1.1 christos expect_div("0", "1", "0"); 215 1.1 christos expect_div("2", "1", "2"); 216 1.1 christos expect_div("3", "2", "1.5"); 217 1.1 christos expect_div("3", "1.5", "2"); 218 1.1 christos expect_div("10", ".1", "100"); 219 1.1 christos expect_div("123", "456", ".2697368421"); 220 1.1 christos } 221 1.1 christos TEST_END 222 1.1 christos 223 1.1 christos static void 224 1.1 christos expect_round(const char *str, uint32_t rounded_down, uint32_t rounded_nearest) { 225 1.1.1.2 christos fxp_t fxp = xparse_fxp(str); 226 1.1 christos uint32_t fxp_rounded_down = fxp_round_down(fxp); 227 1.1 christos uint32_t fxp_rounded_nearest = fxp_round_nearest(fxp); 228 1.1.1.2 christos expect_u32_eq( 229 1.1.1.2 christos rounded_down, fxp_rounded_down, "Mistake rounding %s down", str); 230 1.1 christos expect_u32_eq(rounded_nearest, fxp_rounded_nearest, 231 1.1 christos "Mistake rounding %s to nearest", str); 232 1.1 christos } 233 1.1 christos 234 1.1 christos TEST_BEGIN(test_round_simple) { 235 1.1 christos expect_round("1.5", 1, 2); 236 1.1 christos expect_round("0", 0, 0); 237 1.1 christos expect_round("0.1", 0, 0); 238 1.1 christos expect_round("0.4", 0, 0); 239 1.1 christos expect_round("0.40000", 0, 0); 240 1.1 christos expect_round("0.5", 0, 1); 241 1.1 christos expect_round("0.6", 0, 1); 242 1.1 christos expect_round("123", 123, 123); 243 1.1 christos expect_round("123.4", 123, 123); 244 1.1 christos expect_round("123.5", 123, 124); 245 1.1 christos } 246 1.1 christos TEST_END 247 1.1 christos 248 1.1 christos static void 249 1.1 christos expect_mul_frac(size_t a, const char *fracstr, size_t expected) { 250 1.1.1.2 christos fxp_t frac = xparse_fxp(fracstr); 251 1.1 christos size_t result = fxp_mul_frac(a, frac); 252 1.1 christos expect_true(double_close(expected, result), 253 1.1.1.2 christos "Expected %zu * %s == %zu (fracmul); got %zu", a, fracstr, expected, 254 1.1.1.2 christos result); 255 1.1 christos } 256 1.1 christos 257 1.1 christos TEST_BEGIN(test_mul_frac_simple) { 258 1.1 christos expect_mul_frac(SIZE_MAX, "1.0", SIZE_MAX); 259 1.1 christos expect_mul_frac(SIZE_MAX, ".75", SIZE_MAX / 4 * 3); 260 1.1 christos expect_mul_frac(SIZE_MAX, ".5", SIZE_MAX / 2); 261 1.1 christos expect_mul_frac(SIZE_MAX, ".25", SIZE_MAX / 4); 262 1.1 christos expect_mul_frac(1U << 16, "1.0", 1U << 16); 263 1.1 christos expect_mul_frac(1U << 30, "0.5", 1U << 29); 264 1.1 christos expect_mul_frac(1U << 30, "0.25", 1U << 28); 265 1.1 christos expect_mul_frac(1U << 30, "0.125", 1U << 27); 266 1.1 christos expect_mul_frac((1U << 30) + 1, "0.125", 1U << 27); 267 1.1 christos expect_mul_frac(100, "0.25", 25); 268 1.1 christos expect_mul_frac(1000 * 1000, "0.001", 1000); 269 1.1 christos } 270 1.1 christos TEST_END 271 1.1 christos 272 1.1 christos static void 273 1.1 christos expect_print(const char *str) { 274 1.1 christos fxp_t fxp = xparse_fxp(str); 275 1.1.1.2 christos char buf[FXP_BUF_SIZE]; 276 1.1 christos fxp_print(fxp, buf); 277 1.1 christos expect_d_eq(0, strcmp(str, buf), "Couldn't round-trip print %s", str); 278 1.1 christos } 279 1.1 christos 280 1.1 christos TEST_BEGIN(test_print_simple) { 281 1.1 christos expect_print("0.0"); 282 1.1 christos expect_print("1.0"); 283 1.1 christos expect_print("2.0"); 284 1.1 christos expect_print("123.0"); 285 1.1 christos /* 286 1.1 christos * We hit the possibility of roundoff errors whenever the fractional 287 1.1 christos * component isn't a round binary number; only check these here (we 288 1.1 christos * round-trip properly in the stress test). 289 1.1 christos */ 290 1.1 christos expect_print("1.5"); 291 1.1 christos expect_print("3.375"); 292 1.1 christos expect_print("0.25"); 293 1.1 christos expect_print("0.125"); 294 1.1 christos /* 1 / 2**14 */ 295 1.1 christos expect_print("0.00006103515625"); 296 1.1 christos } 297 1.1 christos TEST_END 298 1.1 christos 299 1.1 christos TEST_BEGIN(test_stress) { 300 1.1.1.2 christos const char *numbers[] = {"0.0", "0.1", "0.2", "0.3", "0.4", "0.5", 301 1.1.1.2 christos "0.6", "0.7", "0.8", "0.9", 302 1.1 christos 303 1.1.1.2 christos "1.0", "1.1", "1.2", "1.3", "1.4", "1.5", "1.6", "1.7", "1.8", 304 1.1.1.2 christos "1.9", 305 1.1 christos 306 1.1.1.2 christos "2.0", "2.1", "2.2", "2.3", "2.4", "2.5", "2.6", "2.7", "2.8", 307 1.1.1.2 christos "2.9", 308 1.1 christos 309 1.1.1.2 christos "17.0", "17.1", "17.2", "17.3", "17.4", "17.5", "17.6", "17.7", 310 1.1.1.2 christos "17.8", "17.9", 311 1.1 christos 312 1.1.1.2 christos "18.0", "18.1", "18.2", "18.3", "18.4", "18.5", "18.6", "18.7", 313 1.1.1.2 christos "18.8", "18.9", 314 1.1 christos 315 1.1.1.2 christos "123.0", "123.1", "123.2", "123.3", "123.4", "123.5", "123.6", 316 1.1.1.2 christos "123.7", "123.8", "123.9", 317 1.1 christos 318 1.1.1.2 christos "124.0", "124.1", "124.2", "124.3", "124.4", "124.5", "124.6", 319 1.1.1.2 christos "124.7", "124.8", "124.9", 320 1.1 christos 321 1.1.1.2 christos "125.0", "125.1", "125.2", "125.3", "125.4", "125.5", "125.6", 322 1.1.1.2 christos "125.7", "125.8", "125.9"}; 323 1.1.1.2 christos size_t numbers_len = sizeof(numbers) / sizeof(numbers[0]); 324 1.1 christos for (size_t i = 0; i < numbers_len; i++) { 325 1.1.1.2 christos fxp_t fxp_a = xparse_fxp(numbers[i]); 326 1.1 christos double double_a = strtod(numbers[i], NULL); 327 1.1 christos 328 1.1 christos uint32_t fxp_rounded_down = fxp_round_down(fxp_a); 329 1.1 christos uint32_t fxp_rounded_nearest = fxp_round_nearest(fxp_a); 330 1.1 christos uint32_t double_rounded_down = (uint32_t)double_a; 331 1.1 christos uint32_t double_rounded_nearest = (uint32_t)round(double_a); 332 1.1 christos 333 1.1 christos expect_u32_eq(double_rounded_down, fxp_rounded_down, 334 1.1 christos "Incorrectly rounded down %s", numbers[i]); 335 1.1 christos expect_u32_eq(double_rounded_nearest, fxp_rounded_nearest, 336 1.1 christos "Incorrectly rounded-to-nearest %s", numbers[i]); 337 1.1 christos 338 1.1 christos for (size_t j = 0; j < numbers_len; j++) { 339 1.1.1.2 christos fxp_t fxp_b = xparse_fxp(numbers[j]); 340 1.1 christos double double_b = strtod(numbers[j], NULL); 341 1.1 christos 342 1.1.1.2 christos fxp_t fxp_sum = fxp_add(fxp_a, fxp_b); 343 1.1 christos double double_sum = double_a + double_b; 344 1.1 christos expect_true( 345 1.1 christos double_close(fxp2double(fxp_sum), double_sum), 346 1.1 christos "Miscomputed %s + %s", numbers[i], numbers[j]); 347 1.1 christos 348 1.1 christos if (double_a > double_b) { 349 1.1.1.2 christos fxp_t fxp_diff = fxp_sub(fxp_a, fxp_b); 350 1.1 christos double double_diff = double_a - double_b; 351 1.1.1.2 christos expect_true(double_close(fxp2double(fxp_diff), 352 1.1.1.2 christos double_diff), 353 1.1 christos "Miscomputed %s - %s", numbers[i], 354 1.1 christos numbers[j]); 355 1.1 christos } 356 1.1 christos 357 1.1.1.2 christos fxp_t fxp_prod = fxp_mul(fxp_a, fxp_b); 358 1.1 christos double double_prod = double_a * double_b; 359 1.1 christos expect_true( 360 1.1 christos double_close(fxp2double(fxp_prod), double_prod), 361 1.1 christos "Miscomputed %s * %s", numbers[i], numbers[j]); 362 1.1 christos 363 1.1 christos if (double_b != 0.0) { 364 1.1.1.2 christos fxp_t fxp_quot = fxp_div(fxp_a, fxp_b); 365 1.1 christos double double_quot = double_a / double_b; 366 1.1.1.2 christos expect_true(double_close(fxp2double(fxp_quot), 367 1.1.1.2 christos double_quot), 368 1.1 christos "Miscomputed %s / %s", numbers[i], 369 1.1 christos numbers[j]); 370 1.1 christos } 371 1.1 christos } 372 1.1 christos } 373 1.1 christos } 374 1.1 christos TEST_END 375 1.1 christos 376 1.1 christos int 377 1.1 christos main(void) { 378 1.1.1.2 christos return test_no_reentrancy(test_parse_valid, test_parse_invalid, 379 1.1.1.2 christos test_init_percent, test_add_simple, test_sub_simple, 380 1.1.1.2 christos test_mul_simple, test_div_simple, test_round_simple, 381 1.1.1.2 christos test_mul_frac_simple, test_print_simple, test_stress); 382 1.1 christos } 383