| /* |
| * 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_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; |
| } |