fxp.c revision 1.1.1.2 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