119 lines
2.7 KiB
C
119 lines
2.7 KiB
C
|
/*
|
||
|
* 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;
|
||
|
}
|