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