| /* | |
| * fp-test-log2.c - test QEMU's softfloat log2 | |
| * | |
| * Copyright (C) 2020, Linaro, Ltd. | |
| * | |
| * License: GNU GPL, version 2 or later. | |
| * See the COPYING file in the top-level directory. | |
| */ | |
| #ifndef HW_POISON_H | |
| #error Must define HW_POISON_H to work around TARGET_* poisoning | |
| #endif | |
| #include "qemu/osdep.h" | |
| #include "qemu/cutils.h" | |
| #include <math.h> | |
| #include "fpu/softfloat.h" | |
| typedef union { | |
| double d; | |
| float64 i; | |
| } ufloat64; | |
| static int errors; | |
| static void compare(ufloat64 test, ufloat64 real, ufloat64 soft, bool exact) | |
| { | |
| int msb; | |
| uint64_t ulp = UINT64_MAX; | |
| if (real.i == soft.i) { | |
| return; | |
| } | |
| msb = 63 - __builtin_clzll(real.i ^ soft.i); | |
| if (msb < 52) { | |
| if (real.i > soft.i) { | |
| ulp = real.i - soft.i; | |
| } else { | |
| ulp = soft.i - real.i; | |
| } | |
| } | |
| /* glibc allows 3 ulp error in its libm-test-ulps; allow 4 here */ | |
| if (!exact && ulp <= 4) { | |
| return; | |
| } | |
| printf("test: %016" PRIx64 " %+.13a\n" | |
| " sf: %016" PRIx64 " %+.13a\n" | |
| "libm: %016" PRIx64 " %+.13a\n", | |
| test.i, test.d, soft.i, soft.d, real.i, real.d); | |
| if (msb == 63) { | |
| printf("Error in sign!\n\n"); | |
| } else if (msb >= 52) { | |
| printf("Error in exponent: %d\n\n", | |
| (int)(soft.i >> 52) - (int)(real.i >> 52)); | |
| } else { | |
| printf("Error in fraction: %" PRIu64 " ulp\n\n", ulp); | |
| } | |
| if (++errors == 20) { | |
| exit(1); | |
| } | |
| } | |
| int main(int ac, char **av) | |
| { | |
| ufloat64 test, real, soft; | |
| float_status qsf = {0}; | |
| int i; | |
| set_float_2nan_prop_rule(float_2nan_prop_s_ab, &qsf); | |
| set_float_default_nan_pattern(0b01000000, &qsf); | |
| set_float_rounding_mode(float_round_nearest_even, &qsf); | |
| test.d = 0.0; | |
| real.d = -__builtin_inf(); | |
| soft.i = float64_log2(test.i, &qsf); | |
| compare(test, real, soft, true); | |
| test.d = 1.0; | |
| real.d = 0.0; | |
| soft.i = float64_log2(test.i, &qsf); | |
| compare(test, real, soft, true); | |
| test.d = 2.0; | |
| real.d = 1.0; | |
| soft.i = float64_log2(test.i, &qsf); | |
| compare(test, real, soft, true); | |
| test.d = 4.0; | |
| real.d = 2.0; | |
| soft.i = float64_log2(test.i, &qsf); | |
| compare(test, real, soft, true); | |
| test.d = 0x1p64; | |
| real.d = 64.0; | |
| soft.i = float64_log2(test.i, &qsf); | |
| compare(test, real, soft, true); | |
| test.d = __builtin_inf(); | |
| real.d = __builtin_inf(); | |
| soft.i = float64_log2(test.i, &qsf); | |
| compare(test, real, soft, true); | |
| for (i = 0; i < 10000; ++i) { | |
| test.d = drand48() + 1.0; /* [1.0, 2.0) */ | |
| real.d = log2(test.d); | |
| soft.i = float64_log2(test.i, &qsf); | |
| compare(test, real, soft, false); | |
| test.d = drand48() * 100; /* [0.0, 100) */ | |
| real.d = log2(test.d); | |
| soft.i = float64_log2(test.i, &qsf); | |
| compare(test, real, soft, false); | |
| } | |
| return 0; | |
| } |