aboutsummaryrefslogtreecommitdiff
path: root/tests/fp/fp-test-log2.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/fp/fp-test-log2.c')
-rw-r--r--tests/fp/fp-test-log2.c118
1 files changed, 118 insertions, 0 deletions
diff --git a/tests/fp/fp-test-log2.c b/tests/fp/fp-test-log2.c
new file mode 100644
index 0000000000..4eae93eb7c
--- /dev/null
+++ b/tests/fp/fp-test-log2.c
@@ -0,0 +1,118 @@
+/*
+ * 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;
+}