diff mbox series

RFC: Testing different long double formats

Message ID 7a7a6812-d24b-2018-b875-d85cbccb8eed@redhat.com
State New
Headers show
Series RFC: Testing different long double formats | expand

Commit Message

Florian Weimer July 2, 2018, 3:11 p.m. UTC
The attached patch attempts to cover the -mlong-double-64 case.  Is this 
a direction in which we want to move?

The new test compares the 64-bit long double implementation against the 
double implementation on a few arbitrary test values, assuming that 
discrepancy due to incorrect redirection would show up very quickly.

The test needs Jakub's patch add:

   <https://sourceware.org/ml/libc-alpha/2017-08/msg01131.html>

I've tested this on ppc64 only so far.

Thanks,
Florian

Comments

Joseph Myers July 2, 2018, 3:31 p.m. UTC | #1
On Mon, 2 Jul 2018, Florian Weimer wrote:

> The attached patch attempts to cover the -mlong-double-64 case.  Is this a
> direction in which we want to move?

I think we want to move in the direction Tulio gave in 
<https://sourceware.org/ml/libc-alpha/2018-06/msg00686.html>: once all the 
ldouble tests are built for both -mabi=ieeelongdouble and 
-mabi=ibmlongdouble, it should be straightforward to build them for 
-mlong-double-64 as well for ldbl-opt configurations (and since my commit 
bc9620d040b7494f457ccb750c9797b47ed76ada to build them for long double = 
double configurations, they should just work when built with 
-mlong-double-64, including using the correct ulps).

Much the same applies for other -mlong-double-64 redirections: the 
-mabi=ieeelongdouble test infrastructure should be added in a form that 
makes it each to add ldbl-opt testing of the same tests with 
-mlong-double-64.

> The test needs Jakub's patch add:
> 
>   <https://sourceware.org/ml/libc-alpha/2017-08/msg01131.html>

Please commit that patch with such a wrapper for libio/bits/stdio-ldbl.h 
as well.  (It's possible that stdio-common/bits/printf-ldbl.h, 
misc/bits/syslog-ldbl.h, stdlib/bits/monetary-ldbl.h will only end up 
being needed in tests run within those directories, and likewise new 
argp-ldbl.h, err-ldbl.h, error-ldbl.h that don't yet exist, though there 
would be nothing wrong with having wrappers for them as well so that 
linknamespace or check-installed-headers tests could be run for long 
double variants if desired.)
diff mbox series

Patch

Subject: [PATCH] math: Add test-nldbl
To: libc-alpha@sourceware.org

2018-07-02  Florian Weimer  <fweimer@redhat.com>

	* sysdeps/ieee754/ldbl-opt/test-nldbl.c: New file.
	* sysdeps/ieee754/ldbl-opt/test-nldbl-check1.c: Likewise.
	* sysdeps/ieee754/ldbl-opt/test-nldbl-check2.c: Likewise.
	* sysdeps/ieee754/ldbl-opt/test-nldbl-check3.c: Likewise.
	* sysdeps/ieee754/ldbl-opt/Makefile (tests): Add test-nldbl.
	(CFLAGS-test-nldbl.c): Switch to 64-bit long double and disable
	built-ins.

diff --git a/sysdeps/ieee754/ldbl-opt/Makefile b/sysdeps/ieee754/ldbl-opt/Makefile
index ef790adc77..1a9227bf0a 100644
--- a/sysdeps/ieee754/ldbl-opt/Makefile
+++ b/sysdeps/ieee754/ldbl-opt/Makefile
@@ -175,4 +175,7 @@  CFLAGS-nldbl-yn.c = -fno-builtin-ynl
 tests += test-narrow-macros-ldbl-64
 CFLAGS-test-narrow-macros-ldbl-64.c += -mlong-double-64
 
+tests += test-nldbl
+CFLAGS-test-nldbl.c += -mlong-double-64 -fno-builtin
+
 endif
diff --git a/sysdeps/ieee754/ldbl-opt/test-nldbl-check1.c b/sysdeps/ieee754/ldbl-opt/test-nldbl-check1.c
new file mode 100644
index 0000000000..3765e93df3
--- /dev/null
+++ b/sysdeps/ieee754/ldbl-opt/test-nldbl-check1.c
@@ -0,0 +1,48 @@ 
+/* Verify single-argument functions.
+   Copyright (C) 2018 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+static void
+NAME (const char *name, RETURN_TYPE_L (*func) (ARG0_TYPE_L),
+      RETURN_TYPE (*reference) (ARG0_TYPE))
+{
+  for (int do_negative = 0; do_negative < 2; ++do_negative)
+    for (size_t i = 0; i < array_length (VALUES); ++i)
+      {
+        ARG0_TYPE value = VALUES[i];
+        if (do_negative)
+          value = -value;
+
+        /* volatile to avoid excess precision.  */
+        volatile RETURN_TYPE expected = reference (value);
+        RETURN_TYPE_L actual = func (value);
+        if (!RESULT_EQUAL (expected, actual))
+          {
+            support_record_failure ();
+            REPORT_ERROR (name, value, expected, actual);
+          }
+      }
+}
+
+#undef NAME
+#undef ARG0_TYPE
+#undef ARG0_TYPE_L
+#undef RETURN_TYPE_L
+#undef RETURN_TYPE
+#undef VALUES
+#undef RESULT_EQUAL
+#undef REPORT_ERROR
diff --git a/sysdeps/ieee754/ldbl-opt/test-nldbl-check2.c b/sysdeps/ieee754/ldbl-opt/test-nldbl-check2.c
new file mode 100644
index 0000000000..4dd63c5211
--- /dev/null
+++ b/sysdeps/ieee754/ldbl-opt/test-nldbl-check2.c
@@ -0,0 +1,59 @@ 
+/* Verify two-argument functions.
+   Copyright (C) 2018 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+static void
+NAME (const char *name, RETURN_TYPE_L (*func) (ARG0_TYPE_L, ARG1_TYPE_L),
+      RETURN_TYPE (*reference) (ARG0_TYPE, ARG1_TYPE))
+{
+  for (int do_negative = 0; do_negative < 2; ++do_negative)
+    for (size_t i = 0; i < array_length (VALUES0); ++i)
+      {
+        ARG0_TYPE value0 = VALUES0[i];
+        if (do_negative)
+          value0 = -value0;
+
+        for (int do_negative1 = 0; do_negative1 < 2; ++do_negative1)
+          for (size_t i1 = 0; i1 < array_length (VALUES1); ++i1)
+            {
+              ARG1_TYPE value1 = VALUES1[i1];
+              if (do_negative1)
+                value1 = -value1;
+
+              /* volatile to avoid excess precision.  */
+              volatile RETURN_TYPE expected = reference (value0, value1);
+              RETURN_TYPE_L actual = func (value0, value1);
+              if (!RESULT_EQUAL (expected, actual))
+                {
+                  support_record_failure ();
+                  REPORT_ERROR (name, value0, value1, expected, actual);
+                }
+            }
+      }
+}
+
+#undef NAME
+#undef ARG0_TYPE
+#undef ARG0_TYPE_L
+#undef ARG1_TYPE
+#undef ARG1_TYPE_L
+#undef RETURN_TYPE_L
+#undef RETURN_TYPE
+#undef VALUES0
+#undef VALUES1
+#undef RESULT_EQUAL
+#undef REPORT_ERROR
diff --git a/sysdeps/ieee754/ldbl-opt/test-nldbl-check3.c b/sysdeps/ieee754/ldbl-opt/test-nldbl-check3.c
new file mode 100644
index 0000000000..eac9fdd543
--- /dev/null
+++ b/sysdeps/ieee754/ldbl-opt/test-nldbl-check3.c
@@ -0,0 +1,73 @@ 
+/* Verify three-argument functions.
+   Copyright (C) 2018 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+static void
+NAME (const char *name,
+      RETURN_TYPE_L (*func) (ARG0_TYPE_L, ARG1_TYPE_L, ARG2_TYPE_L),
+      RETURN_TYPE (*reference) (ARG0_TYPE, ARG1_TYPE, ARG2_TYPE))
+{
+  for (int do_negative = 0; do_negative < 2; ++do_negative)
+    for (size_t i = 0; i < array_length (VALUES0); ++i)
+      {
+        ARG0_TYPE value0 = VALUES0[i];
+        if (do_negative)
+          value0 = -value0;
+
+        for (int do_negative1 = 0; do_negative1 < 2; ++do_negative1)
+          for (size_t i1 = 0; i1 < array_length (VALUES1); ++i1)
+            {
+              ARG1_TYPE value1 = VALUES1[i1];
+              if (do_negative1)
+                value1 = -value1;
+
+              for (int do_negative2 = 0; do_negative2 < 2; ++do_negative2)
+                for (size_t i2 = 0; i2 < array_length (VALUES2); ++i2)
+                  {
+                    ARG2_TYPE value2 = VALUES2[i2];
+                    if (do_negative2)
+                      value2 = -value2;
+
+                    /* volatile to avoid excess precision.  */
+                    volatile RETURN_TYPE expected
+                      = reference (value0, value1, value2);
+                    RETURN_TYPE_L actual = func (value0, value1, value2);
+                    if (!RESULT_EQUAL (expected, actual))
+                      {
+                        support_record_failure ();
+                        REPORT_ERROR (name, value0, value1, value2,
+                                      expected, actual);
+                      }
+                  }
+            }
+      }
+}
+
+#undef NAME
+#undef ARG0_TYPE
+#undef ARG0_TYPE_L
+#undef ARG1_TYPE
+#undef ARG1_TYPE_L
+#undef ARG2_TYPE
+#undef ARG2_TYPE_L
+#undef RETURN_TYPE_L
+#undef RETURN_TYPE
+#undef VALUES0
+#undef VALUES1
+#undef VALUES2
+#undef RESULT_EQUAL
+#undef REPORT_ERROR
diff --git a/sysdeps/ieee754/ldbl-opt/test-nldbl.c b/sysdeps/ieee754/ldbl-opt/test-nldbl.c
new file mode 100644
index 0000000000..7cb507a716
--- /dev/null
+++ b/sysdeps/ieee754/ldbl-opt/test-nldbl.c
@@ -0,0 +1,925 @@ 
+/* Tests for nldbl redirects.
+   Copyright (C) 2018 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <array_length.h>
+#include <complex.h>
+#include <float.h>
+#include <math.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <support/xmemstream.h>
+
+/* These are lists of arbitrary values which are used to check that
+   the long double functions yield identical results to double
+   functions (so they are redirected to the right variant).  */
+static const double double_values[] =
+  {
+    0.0, DBL_MIN, FLT_MIN, 0.5,
+    1.0, 1.5, M_SQRT2, 2.0, M_E, 3.0, M_PI, 4.0, 5.0,
+    1.0e10, FLT_MAX, 1.0e100, DBL_MAX, HUGE_VAL,
+  };
+static const int int_values[] =
+  {
+    0, 1, 2, 10
+  };
+
+static double complex complex_values[2
+                                     * sizeof (double_values)
+                                     / sizeof (double_values[0])
+                                     * sizeof (double_values)
+                                     / sizeof (double_values[0])];
+static void
+init_complex_values (void)
+{
+  size_t pos = 0;
+  for (size_t i = 0; i < array_length (double_values); ++i)
+    for (size_t j = 0; j < array_length (double_values); ++j)
+      for (int do_negative = 0; do_negative < 2; ++do_negative)
+        {
+          double imag = double_values[j];
+          if (do_negative)
+            imag = -imag;
+          complex_values[pos] = double_values[i] + imag * I;
+          ++pos;
+        }
+}
+
+static void
+print_double (double value)
+{
+  if (value != value)
+    printf ("NaN");
+  else
+    printf ("%.17e (%.14a)", value, value);
+}
+
+static void
+print_complex (double complex value)
+{
+  if (value != value)
+    printf ("NaN");
+  else
+    printf ("%.17e+%.17eI (%.14a+%.14aI)",
+            creal (value), cimag (value), creal (value), cimag (value));
+}
+
+/* Format codes used here:
+
+   i   int
+   f   float
+   d   double
+   D   long double
+   c   double complex
+   C   long double complex
+
+   We cannot use printf codes because there is none for float.  */
+
+static bool
+result_equal_i (int expected, int actual)
+{
+  return expected == actual;
+}
+
+static bool
+result_equal_d (double expected, double actual)
+{
+  bool expected_nan = expected != expected;
+  bool actual_nan = actual != actual;
+  return expected_nan == actual_nan && (expected_nan || expected == actual);
+}
+
+static bool
+result_equal_f (float expected, float actual)
+{
+  bool expected_nan = expected != expected;
+  bool actual_nan = actual != actual;
+  return expected_nan == actual_nan && (expected_nan || expected == actual);
+}
+
+static bool
+result_equal_D (double expected, long double actual)
+{
+  bool expected_nan = expected != expected;
+  bool actual_nan = actual != actual;
+  return expected_nan == actual_nan && (expected_nan || expected == actual);
+}
+
+static bool
+result_equal_C (double complex expected, long double complex actual)
+{
+  bool expected_nan = expected != expected;
+  bool actual_nan = actual != actual;
+  return expected_nan == actual_nan && (expected_nan || expected == actual);
+}
+
+static void
+report_error_iD (const char *name, double value, int expected, int actual)
+{
+  printf ("error: %s result differs at ", name);
+  print_double (value);
+  printf ("\n  expected: %d\n  actual: %d\n", expected, actual);
+}
+
+static void
+report_error_DD (const char *name, double value, double expected,
+                 long double actual)
+{
+  printf ("error: %s result differs at ", name);
+  print_double (value);
+  printf ("\n  expected: ");
+  print_double (expected);
+  printf ("\n  actual: ");
+  print_double (actual);
+  putchar ('\n');
+}
+
+static void
+report_error_DC (const char *name, double complex value,
+                 double complex expected, long double actual)
+{
+  printf ("error: %s result differs at ", name);
+  print_complex (value);
+  printf ("\n  expected: ");
+  print_double (expected);
+  printf ("\n  actual: ");
+  print_double (actual);
+  putchar ('\n');
+}
+
+static void
+report_error_CC (const char *name, double complex value,
+                 double complex expected, long double complex actual)
+{
+  printf ("error: %s result differs at ", name);
+  print_complex (value);
+  printf ("\n  expected: ");
+  print_complex (expected);
+  printf ("\n  actual: ");
+  print_complex (actual);
+  putchar ('\n');
+}
+
+static void
+report_error_DDD (const char *name, double value0, double value1,
+                  double expected, long double actual)
+{
+  printf ("error: %s result differs at ", name);
+  print_double (value0);
+  printf (", ");
+  print_double (value1);
+  printf ("\n  expected: ");
+  print_double (expected);
+  printf ("\n  actual: ");
+  print_double (actual);
+  putchar ('\n');
+}
+
+static void
+report_error_CCC (const char *name, double complex value0,
+                  double complex value1,
+                  double complex expected, long double complex actual)
+{
+  printf ("error: %s result differs at ", name);
+  print_complex (value0);
+  printf (", ");
+  print_complex (value1);
+  printf ("\n  expected: ");
+  print_complex (expected);
+  printf ("\n  actual: ");
+  print_complex (actual);
+  putchar ('\n');
+}
+
+static void
+report_error_DDDD (const char *name, double value0, double value1,
+                   double value2, double expected, long double actual)
+{
+  printf ("error: %s result differs at ", name);
+  print_double (value0);
+  printf (", ");
+  print_double (value1);
+  printf (", ");
+  print_double (value2);
+  printf ("\n  expected: ");
+  print_double (expected);
+  printf ("\n  actual: ");
+  print_double (actual);
+  putchar ('\n');
+}
+
+static void
+report_error_iDD (const char *name, double value0, double value1,
+                  int expected, int actual)
+{
+  printf ("error: %s result differs at ", name);
+  print_double (value0);
+  printf (", ");
+  print_double (value1);
+  printf ("\n  expected: %d\n  actual: %d\n", expected, actual);
+}
+
+static void
+report_error_DDi (const char *name, double value0, int value1,
+                  double expected, long double actual)
+{
+  printf ("error: %s result differs at ", name);
+  print_double (value0);
+  printf (", %d\n  expected: ", value1);
+  print_double (expected);
+  printf ("\n  actual: ");
+  print_double (actual);
+  putchar ('\n');
+}
+
+static void
+report_error_DiD (const char *name, double value0, int value1,
+                  double expected, long double actual)
+{
+  printf ("error: %s result differs at %d, ", value0);
+  print_double (value1);
+  printf ("\n  expected: ");
+  print_double (expected);
+  printf ("\n  actual: ");
+  print_double (actual);
+  putchar ('\n');
+}
+
+#define NAME check_DD
+#define RETURN_TYPE_L long double
+#define ARG0_TYPE_L long double
+#define RETURN_TYPE double
+#define ARG0_TYPE double
+#define VALUES double_values
+#define RESULT_EQUAL result_equal_D
+#define REPORT_ERROR report_error_DD
+#include "test-nldbl-check1.c"
+
+#define NAME check_iD
+#define RETURN_TYPE_L int
+#define ARG0_TYPE_L long double
+#define RETURN_TYPE int
+#define ARG0_TYPE double
+#define VALUES double_values
+#define RESULT_EQUAL result_equal_i
+#define REPORT_ERROR report_error_iD
+#include "test-nldbl-check1.c"
+
+#define NAME check_DC
+#define RETURN_TYPE_L long double
+#define ARG0_TYPE_L long double complex
+#define RETURN_TYPE double
+#define ARG0_TYPE double complex
+#define VALUES complex_values
+#define RESULT_EQUAL result_equal_D
+#define REPORT_ERROR report_error_DC
+#include "test-nldbl-check1.c"
+
+#define NAME check_CC
+#define RETURN_TYPE_L long double complex
+#define ARG0_TYPE_L long double complex
+#define RETURN_TYPE double complex
+#define ARG0_TYPE double complex
+#define VALUES complex_values
+#define RESULT_EQUAL result_equal_C
+#define REPORT_ERROR report_error_CC
+#include "test-nldbl-check1.c"
+
+#define NAME check_DDD
+#define RETURN_TYPE_L long double
+#define ARG0_TYPE_L long double
+#define ARG1_TYPE_L long double
+#define RETURN_TYPE double
+#define ARG0_TYPE double
+#define ARG1_TYPE double
+#define VALUES0 double_values
+#define VALUES1 double_values
+#define RESULT_EQUAL result_equal_D
+#define REPORT_ERROR report_error_DDD
+#include "test-nldbl-check2.c"
+
+#define NAME check_iDD
+#define RETURN_TYPE_L int
+#define ARG0_TYPE_L long double
+#define ARG1_TYPE_L long double
+#define RETURN_TYPE int
+#define ARG0_TYPE double
+#define ARG1_TYPE double
+#define VALUES0 double_values
+#define VALUES1 double_values
+#define RESULT_EQUAL result_equal_i
+#define REPORT_ERROR report_error_iDD
+#include "test-nldbl-check2.c"
+
+#define NAME check_dDD
+#define RETURN_TYPE_L double
+#define ARG0_TYPE_L long double
+#define ARG1_TYPE_L long double
+#define RETURN_TYPE double
+#define ARG0_TYPE double
+#define ARG1_TYPE double
+#define VALUES0 double_values
+#define VALUES1 double_values
+#define RESULT_EQUAL result_equal_d
+#define REPORT_ERROR report_error_DDD
+#include "test-nldbl-check2.c"
+
+#define NAME check_fDD
+#define RETURN_TYPE_L float
+#define ARG0_TYPE_L long double
+#define ARG1_TYPE_L long double
+#define RETURN_TYPE float
+#define ARG0_TYPE double
+#define ARG1_TYPE double
+#define VALUES0 double_values
+#define VALUES1 double_values
+#define RESULT_EQUAL result_equal_f
+#define REPORT_ERROR report_error_DDD
+#include "test-nldbl-check2.c"
+
+#define NAME check_DDi
+#define RETURN_TYPE_L long double
+#define ARG0_TYPE_L long double
+#define ARG1_TYPE_L int
+#define RETURN_TYPE double
+#define ARG0_TYPE double
+#define ARG1_TYPE int
+#define VALUES0 double_values
+#define VALUES1 int_values
+#define RESULT_EQUAL result_equal_D
+#define REPORT_ERROR report_error_DDi
+#include "test-nldbl-check2.c"
+
+#define NAME check_DiD
+#define RETURN_TYPE_L long double
+#define ARG0_TYPE_L int
+#define ARG1_TYPE_L long double
+#define RETURN_TYPE double
+#define ARG0_TYPE int
+#define ARG1_TYPE double
+#define VALUES0 int_values
+#define VALUES1 double_values
+#define RESULT_EQUAL result_equal_D
+#define REPORT_ERROR report_error_DiD
+#include "test-nldbl-check2.c"
+
+#define NAME check_CCC
+#define RETURN_TYPE_L long double complex
+#define ARG0_TYPE_L long double complex
+#define ARG1_TYPE_L long double complex
+#define RETURN_TYPE double complex
+#define ARG0_TYPE double complex
+#define ARG1_TYPE double complex
+#define VALUES0 complex_values
+#define VALUES1 complex_values
+#define RESULT_EQUAL result_equal_C
+#define REPORT_ERROR report_error_CCC
+#include "test-nldbl-check2.c"
+
+#define NAME check_DDDD
+#define RETURN_TYPE_L long double
+#define ARG0_TYPE_L long double
+#define ARG1_TYPE_L long double
+#define ARG2_TYPE_L long double
+#define RETURN_TYPE double
+#define ARG0_TYPE double
+#define ARG1_TYPE double
+#define ARG2_TYPE double
+#define VALUES0 double_values
+#define VALUES1 double_values
+#define VALUES2 double_values
+#define RESULT_EQUAL result_equal_D
+#define REPORT_ERROR report_error_DDDD
+#include "test-nldbl-check3.c"
+
+/* Wrappers for type-generic functions.  */
+
+static int
+signbit_iD (long double x)
+{
+  return signbit (x);
+}
+
+static int
+signbit_id (double x)
+{
+  return signbit (x);
+}
+
+static int
+fpclassify_iD (long double x)
+{
+  return fpclassify (x);
+}
+
+static int
+fpclassify_id (double x)
+{
+  return fpclassify (x);
+}
+
+static void
+single_argument_functions (void)
+{
+  check_DD ("acosl", &acosl, &acos);
+  check_DD ("acoshl", &acoshl, &acosh);
+  check_DD ("asinl", &asinl, &asin);
+  check_DD ("asinhl", &asinhl, &asinh);
+  check_DD ("atanl", &atanl, &atan);
+  check_DD ("atanhl", &atanhl, &atanh);
+  check_DD ("cbrtl", &cbrtl, &cbrt);
+  check_DD ("ceill", &ceill, &ceil);
+  check_DD ("cosl", &cosl, &cos);
+  check_DD ("coshl", &coshl, &cosh);
+  check_DD ("erfl", &erfl, &erf);
+  check_DD ("erfcl", &erfcl, &erfc);
+  check_DD ("exp10l", &exp10l, &exp10);
+  check_DD ("exp2l", &exp2l, &exp2);
+  check_DD ("expl", &expl, &exp);
+  check_DD ("expm1l", &expm1l, &expm1);
+  check_DD ("fabsl", &fabsl, &fabs);
+  check_DD ("floorl", &floorl, &floor);
+  check_DD ("gammal", &gammal, &gamma);
+  check_DD ("j0l", &j0l, &j0);
+  check_DD ("j1l", &j1l, &j1);
+  check_DD ("lgammal", &lgammal, &lgamma);
+  check_DD ("log10l", &log10l, &log10);
+  check_DD ("log1pl", &log1pl, &log1p);
+  check_DD ("log2l", &log2l, &log2);
+  check_DD ("logbl", &logbl, &logb);
+  check_DD ("logl", &logl, &log);
+  check_DD ("nearbyintl", &nearbyintl, &nearbyint);
+  check_DD ("nextdownl", &nextdownl, &nextdown);
+  check_DD ("nextupl", &nextupl, &nextup);
+  check_DD ("roundl", &roundl, &round);
+  check_DD ("roundevenl", &roundevenl, &roundeven);
+  check_DD ("significandl", &significandl, &significand);
+  check_DD ("sinl", &sinl, &sin);
+  check_DD ("sinhl", &sinhl, &sinh);
+  check_DD ("sqrtl", &sqrtl, &sqrt);
+  check_DD ("tanl", &tanl, &tan);
+  check_DD ("tanhl", &tanhl, &tanh);
+  check_DD ("tgammal", &tgammal, &tgamma);
+  check_DD ("truncl", &truncl, &trunc);
+  check_DD ("y0l", &y0l, &y0);
+  check_DD ("y1l", &y1l, &y1);
+
+  check_iD ("finitel", &finitel, &finite);
+  check_iD ("fpclassify", &fpclassify_iD, &fpclassify_id);
+  check_iD ("isinfl", &isinfl, &isinf);
+  check_iD ("isnanl", &isnanl, &isnan);
+  check_iD ("signbit", &signbit_iD, &signbit_id);
+
+  check_DC ("cabsl", &cabsl, &cabs);
+  check_DC ("cargl", &cargl, &carg);
+  check_DC ("cimagl", &cimagl, &cimag);
+  check_DC ("creal", &creall, &creal);
+
+  check_CC ("cacosl", &cacosl, &cacos);
+  check_CC ("cacoshl", &cacoshl, &cacosh);
+  check_CC ("casinl", &casinl, &casin);
+  check_CC ("casinhl", &casinhl, &casinh);
+  check_CC ("catanl", &catanl, &catan);
+  check_CC ("catanhl", &catanhl, &catanh);
+  check_CC ("ccosl", &ccosl, &ccos);
+  check_CC ("ccoshl", &ccoshl, &ccosh);
+  check_CC ("cexpl", &cexpl, &cexp);
+  check_CC ("clog10l", &clog10l, &clog10);
+  check_CC ("clogl", &clogl, &clog);
+  check_CC ("conjl", &conjl, &conj);
+  check_CC ("cprojl", &cprojl, &cproj);
+  check_CC ("csinl", &csinl, &csin);
+  check_CC ("csinhl", &csinhl, &csinh);
+  check_CC ("csqrtl", &csqrtl, &csqrt);
+  check_CC ("ctanl", &ctanl, &ctan);
+  check_CC ("ctanhl", &ctanhl, &ctanh);
+}
+
+/* Helper functions for result checking.  Not implemented in libm
+   because they are trivial.  */
+
+static double
+dadd (double x, double y)
+{
+  return x + y;
+}
+
+static double
+dsub (double x, double y)
+{
+  return x - y;
+}
+
+static double
+dmul (double x, double y)
+{
+  return x * y;
+}
+
+static double
+ddiv (double x, double y)
+{
+  return x / y;
+}
+
+static void
+two_argument_functions (void)
+{
+  check_DDD ("atan2l", atan2l, atan2);
+  check_DDD ("copysignl", copysignl, copysign);
+  check_DDD ("fdiml", &fdiml, &fdim);
+  check_DDD ("fmaxl", &fmaxl, &fmax);
+  check_DDD ("fmaxmagl", &fmaxmagl, &fmaxmag);
+  check_DDD ("fminl", &fminl, &fmin);
+  check_DDD ("fminmagl", &fminmagl, &fminmag);
+  check_DDD ("fmodl", &fmodl, &fmod);
+  check_DDD ("hypotl", &hypotl, &hypot);
+  check_DDD ("nextafterl", &nextafterl, &nextafter);
+  check_DDD ("powl", &powl, &pow);
+  check_DDD ("remainderl", &remainderl, &remainder);
+  check_DDD ("scalbl", &scalbl, &scalb);
+
+  check_dDD ("daddl", &daddl, &dadd);
+  check_dDD ("ddivl", &ddivl, &ddiv);
+  check_dDD ("dmull", &dmull, &dmul);
+  check_dDD ("dsubl", &dsubl, &dsub);
+
+  check_fDD ("faddl", &faddl, &fadd);
+  check_fDD ("fdivl", &fdivl, &fdiv);
+  check_fDD ("fmull", &fmull, &fmul);
+  check_fDD ("fsubl", &fsubl, &fsub);
+
+  check_DDi ("ldexpl", &ldexpl, &ldexp);
+
+  check_DiD ("jnl", &jnl, &jn);
+  check_DiD ("ynl", &ynl, &yn);
+
+  check_iDD ("totalorderl", &totalorderl, &totalorder);
+  check_iDD ("totalordermagl", &totalordermagl, &totalordermag);
+
+  check_CCC ("cpowl", &cpowl, &cpow);
+}
+
+/* Wrappers for printf-style functions.  */
+
+static struct xmemstream stdout_replacement;
+static FILE *original_stdout;
+
+static void
+replace_stdout_start (void)
+{
+  TEST_VERIFY (stdout_replacement.buffer == NULL);
+  xopen_memstream (&stdout_replacement);
+  original_stdout = stdout;
+  stdout = stdout_replacement.out;
+}
+
+static void
+replace_stdout_stop (char **result)
+{
+  stdout = original_stdout;
+  xfclose_memstream (&stdout_replacement);
+  *result = stdout_replacement.buffer;
+  stdout_replacement = (struct xmemstream) { NULL };
+}
+
+static int
+printf_Dd (char **result, const char *format, ...)
+{
+  replace_stdout_start ();
+  va_list ap;
+  va_start (ap, format);
+  long double arg0 = va_arg (ap, long double);
+  double arg1 = va_arg (ap, double);
+  int ret = printf (format, arg0, arg1);
+  va_end (ap);
+  replace_stdout_stop (result);
+  return ret;
+}
+
+static int
+vprintf_Dd (char **result, const char *format, ...)
+{
+  replace_stdout_start ();
+  va_list ap;
+  va_start (ap, format);
+  int ret = vprintf (format, ap);
+  va_end (ap);
+  replace_stdout_stop (result);
+  return ret;
+}
+
+static int
+fprintf_Dd (char **result, const char *format, ...)
+{
+  replace_stdout_start ();
+  va_list ap;
+  va_start (ap, format);
+  long double arg0 = va_arg (ap, long double);
+  double arg1 = va_arg (ap, double);
+  int ret = fprintf (stdout, format, arg0, arg1);
+  va_end (ap);
+  replace_stdout_stop (result);
+  return ret;
+}
+
+static int
+vfprintf_Dd (char **result, const char *format, ...)
+{
+  replace_stdout_start ();
+  va_list ap;
+  va_start (ap, format);
+  int ret = vfprintf (stdout, format, ap);
+  va_end (ap);
+  replace_stdout_stop (result);
+  return ret;
+}
+
+static int
+sprintf_Dd (char **result, const char *format, ...)
+{
+  va_list ap;
+  va_start (ap, format);
+  long double arg0 = va_arg (ap, long double);
+  double arg1 = va_arg (ap, double);
+  char buf[128];
+  int ret = sprintf (buf, format, arg0, arg1);
+  va_end (ap);
+  *result = xstrdup (buf);
+  return ret;
+}
+
+static int
+vsprintf_Dd (char **result, const char *format, ...)
+{
+  va_list ap;
+  va_start (ap, format);
+  char buf[128];
+  int ret = vsprintf (buf, format, ap);
+  va_end (ap);
+  *result = xstrdup (buf);
+  return ret;
+}
+
+static int
+snprintf_Dd (char **result, const char *format, ...)
+{
+  va_list ap;
+  va_start (ap, format);
+  long double arg0 = va_arg (ap, long double);
+  double arg1 = va_arg (ap, double);
+  char buf[1024];
+  int ret = snprintf (buf, sizeof (buf), format, arg0, arg1);
+  va_end (ap);
+  *result = xstrdup (buf);
+  return ret;
+}
+
+static int
+vsnprintf_Dd (char **result, const char *format, ...)
+{
+  va_list ap;
+  va_start (ap, format);
+  char buf[1024];
+  int ret = vsnprintf (buf, sizeof (buf), format, ap);
+  va_end (ap);
+  *result = xstrdup (buf);
+  return ret;
+}
+
+static int
+asprintf_Dd (char **result, const char *format, ...)
+{
+  va_list ap;
+  va_start (ap, format);
+  long double arg0 = va_arg (ap, long double);
+  double arg1 = va_arg (ap, double);
+  int ret = asprintf (result, format, arg0, arg1);
+  va_end (ap);
+  return ret;
+}
+
+static int
+vasprintf_Dd (char **result, const char *format, ...)
+{
+  va_list ap;
+  va_start (ap, format);
+  int ret = vasprintf (result, format, ap);
+  va_end (ap);
+  return ret;
+}
+
+struct printf_wrapper
+{
+  char name[12];
+  int (*func) (char **, const char *, ...);
+};
+
+static struct printf_wrapper printf_wrappers[] =
+  {
+    { "printf", printf_Dd },
+    { "vprintf", vprintf_Dd },
+    { "fprintf", fprintf_Dd },
+    { "vfprintf", vfprintf_Dd },
+    { "sprintf", sprintf_Dd },
+    { "vsprintf", vsprintf_Dd },
+    { "snprintf", snprintf_Dd },
+    { "vsnprintf", vsnprintf_Dd },
+    { "asprintf", asprintf_Dd },
+    { "vasprintf", vasprintf_Dd },
+  };
+
+static void
+check_printf_Dd (const char *format_D, const char *format_d)
+{
+  for (int do_negative = 0; do_negative < 2; ++do_negative)
+    for (size_t i = 0; i < array_length (double_values); ++i)
+      {
+        double value = double_values[i];
+        if (do_negative)
+          value = -value;
+
+        /* Value to detect argument list corruption.  */
+        double sentinel = 1234.5;
+
+        char buf_d[1024];
+        int result_d
+          = snprintf (buf_d, sizeof (buf_d), format_d, value, sentinel);
+        TEST_VERIFY (result_d < sizeof (buf_d));
+
+        for (size_t j; j < array_length (printf_wrappers); ++j)
+          {
+            char *buf_D;
+            int result_D
+              = printf_wrappers[j].func (&buf_D, format_D, value, sentinel);
+            if (result_d != result_D || strcmp (buf_d, buf_D) != 0)
+              {
+                support_record_failure ();
+                char *quoted_format_D
+                  = support_quote_blob (format_D, strlen (format_D));
+                char *quoted_d = support_quote_blob (buf_d, strlen (buf_d));
+                char *quoted_D = support_quote_blob (buf_D, strlen (buf_D));
+                printf ("error: output mismatch for %s (\"%s\"):\n"
+                        "  expected: \"%s\" (return value %d)\n"
+                        "  actual:   \"%s\" (return value %d)\n",
+                        printf_wrappers[j].name, quoted_format_D,
+                        quoted_d, result_d, quoted_D, result_D);
+                free (quoted_D);
+                free (quoted_d);
+                free (quoted_format_D);
+              }
+            free (buf_D);
+          }
+      }
+}
+
+static void
+check_printf (void)
+{
+  check_printf_Dd ("%Lf %f", "%f %f");
+  check_printf_Dd ("%.30Lf %f", "%.30f %f");
+  check_printf_Dd ("%La %f", "%a %f");
+  check_printf_Dd ("%.30La %f", "%.30a %f");
+}
+
+static int
+do_test (void)
+{
+  init_complex_values ();
+
+  single_argument_functions ();
+  two_argument_functions ();
+  check_printf ();
+
+  /* Special cases.  */
+  check_DDDD ("fmal", &fmal, &fma);
+
+/* Still not tested:
+
+   asprintf_chk
+   canonicalize
+   dprintf
+   dprintf_chk
+   fprintf
+   fprintf_chk
+   frexp
+   fromfp
+   fromfpx
+   fscanf
+   fwprintf
+   fwprintf_chk
+   fwscanf
+   getpayload
+   ilogb
+   iovfscanf
+   isoc99_fscanf
+   isoc99_fwscanf
+   isoc99_scanf
+   isoc99_sscanf
+   isoc99_swscanf
+   isoc99_vfscanf
+   isoc99_vfwscanf
+   isoc99_vscanf
+   isoc99_vsscanf
+   isoc99_vswscanf
+   isoc99_vwscanf
+   isoc99_wscanf
+   lgamma_r
+   llogb
+   llrint
+   llround
+   lrint
+   lroundl
+   modfl
+   nan
+   nexttowardfl
+   nexttowardl
+   obstack_printf
+   obstack_printf_chk
+   obstack_vprintf
+   obstack_vprintf_chk
+   printf_chk
+   printf_fp
+   printf_size
+   qecvt
+   qecvt_r
+   qfcvt
+   qfcvt_r
+   qgcvt
+   remquo
+   rint
+   scalbln
+   scalbn
+   scanf
+   setpayload
+   setpayloadsig
+   sincos
+   snprintf_chk
+   sprintf_chk
+   sscanf
+   strfmon
+   strfmon_l
+   strfroml
+   strtold
+   strtold_l
+   strtoldint
+   swprintf
+   swprintf_chk
+   swscanf
+   syslog
+   syslog_chk
+   ufromfp
+   ufromfpx
+   vasprintf_chk
+   vdprintf
+   vdprintf_chk
+   vfprintf
+   vfprintf_chk
+   vfscanf
+   vfwprintf
+   vfwprintf_chk
+   vfwscanf
+   vprintf_chk
+   vscanf
+   vsnprintf_chk
+   vsprintf_chk
+   vsscanf
+   vswprintf
+   vswprintf_chk
+   vswscanf
+   vsyslog
+   vsyslog_chk
+   vwprintf
+   vwprintf_chk
+   vwscanf
+   wcstold
+   wcstold_l
+   wcstoldint
+   wprintf
+   wprintf_chk
+   wscanf
+*/
+
+  return 0;
+}
+
+#include <support/test-driver.c>