about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog83
-rw-r--r--NEWS5
-rw-r--r--math/Makefile6
-rwxr-xr-xmath/gen-tgmath-tests.py184
-rw-r--r--math/tgmath.h167
5 files changed, 425 insertions, 20 deletions
diff --git a/ChangeLog b/ChangeLog
index 2f8b8761ca..ee96090964 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,86 @@
+2019-08-21  Joseph Myers  <joseph@codesourcery.com>
+
+	* math/tgmath.h [__HAVE_FLOAT128X]: Give error.
+	[(__HAVE_FLOAT64X && !__HAVE_FLOAT128)
+	|| (__HAVE_FLOAT128 && !__HAVE_FLOAT64X)]: Likewise.
+	(__TGMATH_2_NARROW_F): Likewise.
+	(__TGMATH_2_NARROW_D): New macro.
+	(__TGMATH_2_NARROW_F16): Likewise.
+	(__TGMATH_2_NARROW_F32): Likewise.
+	(__TGMATH_2_NARROW_F64): Likewise.
+	(__TGMATH_2_NARROW_F32X): Likewise.
+	(__TGMATH_2_NARROW_F64X): Likewise.
+	[__HAVE_BUILTIN_TGMATH] (__TGMATH_NARROW_FUNCS_F): Likewise.
+	[__HAVE_BUILTIN_TGMATH] (__TGMATH_NARROW_FUNCS_F16): Likewise.
+	[__HAVE_BUILTIN_TGMATH] (__TGMATH_NARROW_FUNCS_F32): Likewise.
+	[__HAVE_BUILTIN_TGMATH] (__TGMATH_NARROW_FUNCS_F64): Likewise.
+	[__HAVE_BUILTIN_TGMATH] (__TGMATH_NARROW_FUNCS_F32X): Likewise.
+	[__GLIBC_USE (IEC_60559_BFP_EXT_C2X)] (fadd): Likewise.
+	[__GLIBC_USE (IEC_60559_BFP_EXT_C2X)] (dadd): Likewise.
+	[__GLIBC_USE (IEC_60559_BFP_EXT_C2X)] (fdiv): Likewise.
+	[__GLIBC_USE (IEC_60559_BFP_EXT_C2X)] (ddiv): Likewise.
+	[__GLIBC_USE (IEC_60559_BFP_EXT_C2X)] (fmul): Likewise.
+	[__GLIBC_USE (IEC_60559_BFP_EXT_C2X)] (dmul): Likewise.
+	[__GLIBC_USE (IEC_60559_BFP_EXT_C2X)] (fsub): Likewise.
+	[__GLIBC_USE (IEC_60559_BFP_EXT_C2X)] (dsub): Likewise.
+	[__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT16] (f16add):
+	Likewise.
+	[__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT16] (f16div):
+	Likewise.
+	[__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT16] (f16mul):
+	Likewise.
+	[__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT16] (f16sub):
+	Likewise.
+	[__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT32] (f32add):
+	Likewise.
+	[__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT32] (f32div):
+	Likewise.
+	[__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT32] (f32mul):
+	Likewise.
+	[__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT32] (f32sub):
+	Likewise.
+	[__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT64
+	&& (__HAVE_FLOAT64X || __HAVE_FLOAT128)] (f64add): Likewise.
+	[__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT64
+	&& (__HAVE_FLOAT64X || __HAVE_FLOAT128)] (f64div): Likewise.
+	[__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT64
+	&& (__HAVE_FLOAT64X || __HAVE_FLOAT128)] (f64mul): Likewise.
+	[__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT64
+	&& (__HAVE_FLOAT64X || __HAVE_FLOAT128)] (f64sub): Likewise.
+	[__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT32X] (f32xadd):
+	Likewise.
+	[__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT32X] (f32xdiv):
+	Likewise.
+	[__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT32X] (f32xmul):
+	Likewise.
+	[__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT32X] (f32xsub):
+	Likewise.
+	[__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT64X
+	&& (__HAVE_FLOAT128X || __HAVE_FLOAT128)] (f64xadd): Likewise.
+	[__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT64X
+	&& (__HAVE_FLOAT128X || __HAVE_FLOAT128)] (f64xdiv): Likewise.
+	[__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT64X
+	&& (__HAVE_FLOAT128X || __HAVE_FLOAT128)] (f64xmul): Likewise.
+	[__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT64X
+	&& (__HAVE_FLOAT128X || __HAVE_FLOAT128)] (f64xsub): Likewise.
+	* math/gen-tgmath-tests.py (Type): Add members
+	non_standard_real_argument_types_list, long_double_type,
+	complex_float64_type and float32x_ext_type.
+	(Type.__init__): Set the new members.
+	(Type.floating_type): Add new argument floatn.
+	(Type.real_floating_type): Likewise.
+	(Type.can_combine_types): Likewise.
+	(Type.combine_types): Likewise.
+	(Type.init_types): Create internal Float32x_ext type.
+	(Tests.__init__): Define Float32x_ext in generated C code.
+	(Tests.add_tests): Handle narrowing functions.
+	(Tests.add_all_tests): Likewise.
+	(Tests.tests_text): Allow variation in mant_dig for narrowing
+	functions with compilers before GCC 8.
+	* math/Makefile (tgmath3-narrow-types): New variable.
+	(tgmath3-narrow-macros): Likewise.
+	(tgmath3-macros): Add $(tgmath3-narrow-macros).
+
 2019-08-20  Andreas Schwab  <schwab@suse.de>
 
 	* sysdeps/i386/fpu/libm-test-ulps: Update.
diff --git a/NEWS b/NEWS
index 045720b3fb..a64b89986a 100644
--- a/NEWS
+++ b/NEWS
@@ -16,6 +16,11 @@ Major new features:
   liable to change.  Features from C2X are also enabled by _GNU_SOURCE, or
   by compiling with "gcc -std=gnu2x".
 
+* The <math.h> functions that round their results to a narrower type now
+  have corresponding type-generic macros in <tgmath.h>, as defined in TS
+  18661-1:2014 and TS 18661-3:2015 as amended by the resolution of
+  Clarification Request 13 to TS 18661-3.
+
 Deprecated and removed features, and other changes affecting compatibility:
 
 * The totalorder and totalordermag functions, and the corresponding
diff --git a/math/Makefile b/math/Makefile
index 15c864c98b..247e31d7b9 100644
--- a/math/Makefile
+++ b/math/Makefile
@@ -368,6 +368,10 @@ $(foreach t,$(libm-tests-base),\
 $(foreach t,$(libm-tests-base),\
 	    $(objpfx)$(t)-compat_totalordermag.o): $(objpfx)libm-test-totalordermag.c
 
+# _Float128x omitted as not supported by gen-tgmath-tests.py.
+tgmath3-narrow-types = f d f16 f32 f64 f128 f32x f64x
+tgmath3-narrow-macros = $(foreach t,$(tgmath3-narrow-types), \
+				    $(foreach f,$(libm-narrow-fns),$(t)$(f)))
 tgmath3-macros = atan2 cbrt ceil copysign erf erfc exp2 expm1 fdim floor \
 		 fma fmax fmin fmod frexp hypot ilogb ldexp lgamma llrint \
 		 llround log10 log1p log2 logb lrint lround nearbyint \
@@ -375,7 +379,7 @@ tgmath3-macros = atan2 cbrt ceil copysign erf erfc exp2 expm1 fdim floor \
 		 scalbln tgamma trunc acos asin atan acosh asinh atanh cos \
 		 sin tan cosh sinh tanh exp log pow sqrt fabs carg cimag conj \
 		 cproj creal roundeven nextup nextdown fminmag fmaxmag llogb \
-		 fromfp fromfpx ufromfp ufromfpx scalb
+		 fromfp fromfpx ufromfp ufromfpx scalb $(tgmath3-narrow-macros)
 tgmath3-macro-tests = $(addprefix test-tgmath3-,$(tgmath3-macros))
 tests += $(tgmath3-macro-tests)
 generated += $(addsuffix .c,$(tgmath3-macro-tests))
diff --git a/math/gen-tgmath-tests.py b/math/gen-tgmath-tests.py
index 2a7d94e737..1c098440f4 100755
--- a/math/gen-tgmath-tests.py
+++ b/math/gen-tgmath-tests.py
@@ -69,17 +69,27 @@ class Type(object):
     # Real argument types that correspond to a standard floating type
     # (float, double or long double; not _FloatN or _FloatNx).
     standard_real_argument_types_list = []
+    # Real argument types other than float, double and long double
+    # (i.e., those that are valid as arguments to narrowing macros
+    # returning _FloatN or _FloatNx).
+    non_standard_real_argument_types_list = []
     # The real floating types by their order properties (which are
     # tuples giving the positions in both the possible orders above).
     real_types_order = {}
     # The type double.
     double_type = None
+    # The type long double.
+    long_double_type = None
     # The type _Complex double.
     complex_double_type = None
     # The type _Float64.
     float64_type = None
+    # The type _Complex _Float64.
+    complex_float64_type = None
     # The type _Float64x.
     float64x_type = None
+    # The type _Float64x if available, otherwise _Float64.
+    float32x_ext_type = None
 
     def __init__(self, name, suffix=None, mant_dig=None, condition='1',
                  order=None, integer=False, complex=False, real_type=None):
@@ -109,16 +119,24 @@ class Type(object):
                 Type.real_argument_types_list.append(self)
                 if not self.name.startswith('_Float'):
                     Type.standard_real_argument_types_list.append(self)
+                if self.name not in ('float', 'double', 'long double'):
+                    Type.non_standard_real_argument_types_list.append(self)
         if self.order is not None:
             Type.real_types_order[self.order] = self
         if self.name == 'double':
             Type.double_type = self
+        if self.name == 'long double':
+            Type.long_double_type = self
         if self.name == '_Complex double':
             Type.complex_double_type = self
         if self.name == '_Float64':
             Type.float64_type = self
+        if self.name == '_Complex _Float64':
+            Type.complex_float64_type = self
         if self.name == '_Float64x':
             Type.float64x_type = self
+        if self.name == 'Float32x_ext':
+            Type.float32x_ext_type = self
 
     @staticmethod
     def create_type(name, suffix=None, mant_dig=None, condition='1', order=None,
@@ -142,18 +160,23 @@ class Type(object):
         if complex_type is not None:
             complex_type.register_type(internal)
 
-    def floating_type(self):
+    def floating_type(self, floatn):
         """Return the corresponding floating type."""
         if self.integer:
-            return (Type.complex_double_type
-                    if self.complex
-                    else Type.double_type)
+            if floatn:
+                return (Type.complex_float64_type
+                        if self.complex
+                        else Type.float64_type)
+            else:
+                return (Type.complex_double_type
+                        if self.complex
+                        else Type.double_type)
         else:
             return self
 
-    def real_floating_type(self):
+    def real_floating_type(self, floatn):
         """Return the corresponding real floating type."""
-        return self.real_type.floating_type()
+        return self.real_type.floating_type(floatn)
 
     def __str__(self):
         """Return string representation of a type."""
@@ -212,15 +235,21 @@ class Type(object):
                          complex_name='complex_long_double_Float64x',
                          condition='defined HUGE_VAL_F64X', order=(7, 7),
                          internal=True)
+        # An internal type for the argument type used by f32x*
+        # narrowing macros (_Float64x if available, otherwise
+        # _Float64).
+        Type.create_type('Float32x_ext', None, 'FLT32X_EXT_MANT_DIG',
+                         complex_name='complex_Float32x_ext',
+                         condition='1', internal=True)
 
     @staticmethod
-    def can_combine_types(types):
+    def can_combine_types(types, floatn):
         """Return a C preprocessor conditional for whether the given list of
         types can be used together as type-generic macro arguments."""
         have_long_double = False
         have_float128 = False
         for t in types:
-            t = t.real_floating_type()
+            t = t.real_floating_type(floatn)
             if t.name == 'long double':
                 have_long_double = True
             if t.name == '_Float128' or t.name == '_Float64x':
@@ -233,14 +262,14 @@ class Type(object):
         return '1'
 
     @staticmethod
-    def combine_types(types):
+    def combine_types(types, floatn):
         """Return the result of combining a set of types."""
         have_complex = False
         combined = None
         for t in types:
             if t.complex:
                 have_complex = True
-            t = t.real_floating_type()
+            t = t.real_floating_type(floatn)
             if combined is None:
                 combined = t
             else:
@@ -316,6 +345,7 @@ class Tests(object):
                             '    const char *func_name;\n'
                             '    const char *test_name;\n'
                             '    int mant_dig;\n'
+                            '    int narrow_mant_dig;\n'
                             '  };\n'
                             'int num_pass, num_fail;\n'
                             'volatile int called_mant_dig;\n'
@@ -345,8 +375,18 @@ class Tests(object):
                          '# endif\n')
         float64x_text = if_cond_text([Type.float64x_type.condition],
                                      float64x_text)
+        float32x_ext_text = ('#ifdef HUGE_VAL_F64X\n'
+                             'typedef _Float64x Float32x_ext;\n'
+                             'typedef __CFLOAT64X complex_Float32x_ext;\n'
+                             '# define FLT32X_EXT_MANT_DIG FLT64X_MANT_DIG\n'
+                             '#else\n'
+                             'typedef _Float64 Float32x_ext;\n'
+                             'typedef __CFLOAT64 complex_Float32x_ext;\n'
+                             '# define FLT32X_EXT_MANT_DIG FLT64_MANT_DIG\n'
+                             '#endif\n')
         self.header_list.append(float64_text)
         self.header_list.append(float64x_text)
+        self.header_list.append(float32x_ext_text)
         self.types_seen = set()
         for t in Type.all_types_list:
             self.add_type_var(t.name, t.condition)
@@ -377,6 +417,8 @@ class Tests(object):
             return
         have_complex = False
         func = macro
+        narrowing = False
+        narrowing_std = False
         if ret == 'c' or 'c' in args:
             # Complex-only.
             have_complex = True
@@ -387,6 +429,49 @@ class Tests(object):
             have_complex = True
             if complex_func == None:
                 complex_func = 'c%s' % func
+        # For narrowing macros, compute narrow_args, the list of
+        # argument types for which there is an actual corresponding
+        # function.  If none of those types exist, or the return type
+        # does not exist, then the macro is not defined and no tests
+        # of it can be run.
+        if ret == 'float':
+            narrowing = True
+            narrowing_std = True
+            narrow_cond = '1'
+            narrow_args = [Type.double_type, Type.long_double_type]
+            narrow_fallback = Type.double_type
+        elif ret == 'double':
+            narrowing = True
+            narrowing_std = True
+            narrow_cond = '1'
+            narrow_args = [Type.long_double_type]
+            narrow_fallback = Type.long_double_type
+        elif ret.startswith('_Float'):
+            narrowing = True
+            narrow_args = []
+            nret_type = None
+            narrow_fallback = None
+            for order, real_type in sorted(Type.real_types_order.items()):
+                if real_type.name == ret:
+                    nret_type = real_type
+                elif nret_type and real_type.name.startswith('_Float'):
+                    narrow_args.append(real_type)
+                    if (narrow_fallback is None
+                        and ret.endswith('x') == real_type.name.endswith('x')):
+                        narrow_fallback = real_type
+            if narrow_args:
+                narrow_cond = ('(%s && (%s))'
+                               % (nret_type.condition,
+                                  ' || '.join(t.condition
+                                              for t in narrow_args)))
+                if narrow_fallback is None:
+                    narrow_fallback = narrow_args[0]
+                if ret == '_Float32x':
+                    narrow_fallback = Type.float32x_ext_type
+            else:
+                # No possible argument types, even conditionally.
+                narrow_cond = '0'
+        narrowing_nonstd = narrowing and not narrowing_std
         types = [ret] + args
         for t in types:
             if t != 'c' and t != 'g' and t != 'r' and t != 's':
@@ -400,6 +485,8 @@ class Tests(object):
                 continue
             if ret == 's' and t.name.startswith('_Float'):
                 continue
+            if narrowing and t not in narrow_args:
+                continue
             if ret == 'c':
                 ret_name = t.complex_type.name
             elif ret == 'g':
@@ -432,23 +519,56 @@ class Tests(object):
                           '}\n' % (ret_name, dummy_func_name,
                                    t.real_type.suffix, ', '.join(arg_list),
                                    t.real_type.mant_dig, dummy_func_name))
-            dummy_func = if_cond_text([t.condition], dummy_func)
+            if narrowing:
+                dummy_cond = [narrow_cond, t.condition]
+            else:
+                dummy_cond = [t.condition]
+            dummy_func = if_cond_text(dummy_cond, dummy_func)
             self.test_text_list.append(dummy_func)
         arg_types = []
         for t in args:
             if t == 'g' or t == 'c':
                 arg_types.append(Type.argument_types_list)
             elif t == 'r':
-                arg_types.append(Type.real_argument_types_list)
+                if narrowing_std:
+                    arg_types.append(Type.standard_real_argument_types_list)
+                elif narrowing:
+                    arg_types.append(
+                        Type.non_standard_real_argument_types_list)
+                else:
+                    arg_types.append(Type.real_argument_types_list)
             elif t == 's':
                 arg_types.append(Type.standard_real_argument_types_list)
         arg_types_product = list_product(arg_types)
         test_num = 0
         for this_args in arg_types_product:
-            comb_type = Type.combine_types(this_args)
-            can_comb = Type.can_combine_types(this_args)
+            comb_type = Type.combine_types(this_args, narrowing_nonstd)
+            if narrowing:
+                # As long as there are no integer arguments, and as
+                # long as the chosen argument type is as wide as all
+                # the floating-point arguments passed, the semantics
+                # of the macro call do not depend on the exact
+                # function chosen.  In particular, for f32x functions
+                # when _Float64x exists, the chosen type should differ
+                # for _Float32x and _Float64 arguments, but it is not
+                # always possible to distinguish those types before
+                # GCC 7 and the implementation does not attempt to do
+                # so before GCC 8.
+                narrow_mant_dig = comb_type.real_type.mant_dig
+                for arg_type in this_args:
+                    if arg_type.integer:
+                        narrow_mant_dig = 0
+            else:
+                narrow_mant_dig = 0
+            if (narrowing
+                and comb_type not in narrow_args
+                and narrow_fallback is not None):
+                comb_type = narrow_fallback
+            can_comb = Type.can_combine_types(this_args, narrowing_nonstd)
             all_conds = [t.condition for t in this_args]
             all_conds.append(can_comb)
+            if narrowing:
+                all_conds.append(narrow_cond)
             any_complex = func == None
             for t in this_args:
                 if t.complex:
@@ -459,8 +579,9 @@ class Tests(object):
             test_func_name = 'test_%s_%d' % (macro, test_num)
             test_num += 1
             mant_dig = comb_type.real_type.mant_dig
-            test_text = '%s, "%s", "%s", %s' % (test_func_name, func_name,
-                                                test_name, mant_dig)
+            test_text = '%s, "%s", "%s", %s, %s' % (test_func_name, func_name,
+                                                    test_name, mant_dig,
+                                                    narrow_mant_dig)
             test_text = '    { %s },\n' % test_text
             test_text = if_cond_text(all_conds, test_text)
             self.test_array_list.append(test_text)
@@ -575,9 +696,16 @@ class Tests(object):
         self.add_tests('fromfpx', 'intmax_t', ['r', 'int', 'unsigned int'])
         self.add_tests('ufromfp', 'uintmax_t', ['r', 'int', 'unsigned int'])
         self.add_tests('ufromfpx', 'uintmax_t', ['r', 'int', 'unsigned int'])
-        # The functions that round their result to a narrower type,
-        # and the associated type-generic macros, are not yet
-        # supported by this script or by glibc.
+        for fn in ('add', 'div', 'mul', 'sub'):
+            for ret, prefix in (('float', 'f'),
+                                ('double', 'd'),
+                                ('_Float16', 'f16'),
+                                ('_Float32', 'f32'),
+                                ('_Float64', 'f64'),
+                                ('_Float128', 'f128'),
+                                ('_Float32x', 'f32x'),
+                                ('_Float64x', 'f64x')):
+                self.add_tests(prefix + fn, ret, ['r', 'r'])
         # Miscellaneous functions.
         self.add_tests('scalb', 's', ['s', 's'])
 
@@ -602,6 +730,24 @@ class Tests(object):
                        '          && strcmp (called_func_name,\n'
                        '                     tests[i].func_name) == 0)\n'
                        '        num_pass++;\n'
+                       '#if !__GNUC_PREREQ (8, 0)\n'
+                       '      else if (tests[i].narrow_mant_dig > 0\n'
+                       '               && (called_mant_dig\n'
+                       '                   >= tests[i].narrow_mant_dig)\n'
+                       '               && strcmp (called_func_name,\n'
+                       '                          tests[i].func_name) == 0)\n'
+                       '        {\n'
+                       '          num_pass++;\n'
+                       '          printf ("Test %zu (%s):\\n"\n'
+                       '                  "  Expected: %s precision %d\\n"\n'
+                       '                  "  Actual: %s precision %d\\n"\n'
+                       '                  "  (OK with old GCC)\\n\\n",\n'
+                       '                  i, tests[i].test_name,\n'
+                       '                  tests[i].func_name,\n'
+                       '                  tests[i].mant_dig,\n'
+                       '                  called_func_name, called_mant_dig);\n'
+                       '        }\n'
+                       '#endif\n'
                        '      else\n'
                        '        {\n'
                        '          num_fail++;\n'
diff --git a/math/tgmath.h b/math/tgmath.h
index d9dfca459b..0c58cc9326 100644
--- a/math/tgmath.h
+++ b/math/tgmath.h
@@ -43,6 +43,25 @@
 
 #if __GNUC_PREREQ (2, 7)
 
+/* Certain cases of narrowing macros only need to call a single
+   function so cannot use __builtin_tgmath and do not need any
+   complicated logic.  */
+# if __HAVE_FLOAT128X
+#  error "Unsupported _Float128x type for <tgmath.h>."
+# endif
+# if ((__HAVE_FLOAT64X && !__HAVE_FLOAT128)		\
+      || (__HAVE_FLOAT128 && !__HAVE_FLOAT64X))
+#  error "Unsupported combination of types for <tgmath.h>."
+# endif
+# define __TGMATH_2_NARROW_D(F, X, Y)		\
+  (F ## l (X, Y))
+# define __TGMATH_2_NARROW_F64X(F, X, Y)	\
+  (F ## f128 (X, Y))
+# if !__HAVE_FLOAT128
+#  define __TGMATH_2_NARROW_F32X(F, X, Y)	\
+  (F ## f64 (X, Y))
+# endif
+
 # if __HAVE_BUILTIN_TGMATH
 
 #  if __HAVE_FLOAT16 && __GLIBC_USE (IEC_60559_TYPES_EXT)
@@ -94,6 +113,33 @@
 #  define __TGMATH_2C(F, C, X, Y) __builtin_tgmath (__TGMATH_RCFUNCS (F, C) \
 						    (X), (Y))
 
+#  define __TGMATH_NARROW_FUNCS_F(X) X, X ## l,
+#  define __TGMATH_NARROW_FUNCS_F16(X)				\
+    __TG_F32_ARG (X) __TG_F64_ARG (X) __TG_F128_ARG (X)		\
+    __TG_F32X_ARG (X) __TG_F64X_ARG (X) __TG_F128X_ARG (X)
+#  define __TGMATH_NARROW_FUNCS_F32(X)				\
+    __TG_F64_ARG (X) __TG_F128_ARG (X)				\
+    __TG_F32X_ARG (X) __TG_F64X_ARG (X) __TG_F128X_ARG (X)
+#  define __TGMATH_NARROW_FUNCS_F64(X)		\
+    __TG_F128_ARG (X)				\
+    __TG_F64X_ARG (X) __TG_F128X_ARG (X)
+#  define __TGMATH_NARROW_FUNCS_F32X(X)		\
+    __TG_F64X_ARG (X) __TG_F128X_ARG (X)	\
+    __TG_F64_ARG (X) __TG_F128_ARG (X)
+
+#  define __TGMATH_2_NARROW_F(F, X, Y)				\
+  __builtin_tgmath (__TGMATH_NARROW_FUNCS_F (F) (X), (Y))
+#  define __TGMATH_2_NARROW_F16(F, X, Y)			\
+  __builtin_tgmath (__TGMATH_NARROW_FUNCS_F16 (F) (X), (Y))
+#  define __TGMATH_2_NARROW_F32(F, X, Y)			\
+  __builtin_tgmath (__TGMATH_NARROW_FUNCS_F32 (F) (X), (Y))
+#  define __TGMATH_2_NARROW_F64(F, X, Y)			\
+  __builtin_tgmath (__TGMATH_NARROW_FUNCS_F64 (F) (X), (Y))
+#  if __HAVE_FLOAT128
+#   define __TGMATH_2_NARROW_F32X(F, X, Y)			\
+  __builtin_tgmath (__TGMATH_NARROW_FUNCS_F32X (F) (X), (Y))
+#  endif
+
 # else /* !__HAVE_BUILTIN_TGMATH.  */
 
 #  ifdef __NO_LONG_DOUBLE_MATH
@@ -493,6 +539,65 @@
 			: (__typeof ((__tgmath_complex_type (Val1)) 0	      \
 				   + (__tgmath_complex_type (Val2)) 0))	      \
 			  Cfct##f (Val1, Val2))))
+
+#  define __TGMATH_2_NARROW_F(F, X, Y)					\
+  (__extension__ (sizeof ((__tgmath_real_type (X)) 0			\
+			  + (__tgmath_real_type (Y)) 0) > sizeof (double) \
+		  ? F ## l (X, Y)					\
+		  : F (X, Y)))
+/* In most cases, these narrowing macro definitions based on sizeof
+   ensure that the function called has the right argument format, as
+   for other <tgmath.h> macros for compilers before GCC 8, but may not
+   have exactly the argument type (among the types with that format)
+   specified in the standard logic.
+
+   In the case of macros for _Float32x return type, when _Float64x
+   exists, _Float64 arguments should result in the *f64 function being
+   called while _Float32x arguments should result in the *f64x
+   function being called.  These cases cannot be distinguished using
+   sizeof (or at all if the types are typedefs rather than different
+   types).  However, for these functions it is OK (does not affect the
+   final result) to call a function with any argument format at least
+   as wide as all the floating-point arguments, unless that affects
+   rounding of integer arguments.  Integer arguments are considered to
+   have type _Float64, so the *f64 functions are preferred for f32x*
+   macros when no argument has a wider floating-point type.  */
+#  if __HAVE_FLOAT64X_LONG_DOUBLE && __HAVE_DISTINCT_FLOAT128
+#   define __TGMATH_2_NARROW_F32(F, X, Y)				\
+  (__extension__ (sizeof ((__tgmath_real_type (X)) 0			\
+			  + (__tgmath_real_type (Y)) 0) > sizeof (_Float64) \
+		  ? __TGMATH_F128 ((X) + (Y), F, (X, Y))		\
+		  F ## f64x (X, Y)					\
+		  : F ## f64 (X, Y)))
+#   define __TGMATH_2_NARROW_F64(F, X, Y)				\
+  (__extension__ (sizeof ((__tgmath_real_type (X)) 0			\
+			  + (__tgmath_real_type (Y)) 0) > sizeof (_Float64) \
+		  ? __TGMATH_F128 ((X) + (Y), F, (X, Y))		\
+		  F ## f64x (X, Y)					\
+		  : F ## f128 (X, Y)))
+#   define __TGMATH_2_NARROW_F32X(F, X, Y)				\
+  (__extension__ (sizeof ((__tgmath_real_type (X)) 0			\
+			  + (__tgmath_real_type (Y)) 0) > sizeof (_Float64) \
+		  ? __TGMATH_F128 ((X) + (Y), F, (X, Y))		\
+		  F ## f64x (X, Y)					\
+		  : F ## f64 (X, Y)))
+#  elif __HAVE_FLOAT128
+#   define __TGMATH_2_NARROW_F32(F, X, Y)				\
+  (__extension__ (sizeof ((__tgmath_real_type (X)) 0			\
+			  + (__tgmath_real_type (Y)) 0) > sizeof (_Float64) \
+		  ? F ## f128 (X, Y)					\
+		  : F ## f64 (X, Y)))
+#   define __TGMATH_2_NARROW_F64(F, X, Y)	\
+  (F ## f128 (X, Y))
+#   define __TGMATH_2_NARROW_F32X(F, X, Y)				\
+  (__extension__ (sizeof ((__tgmath_real_type (X)) 0			\
+			  + (__tgmath_real_type (Y)) 0) > sizeof (_Float32x) \
+		  ? F ## f64x (X, Y)					\
+		  : F ## f64 (X, Y)))
+#  else
+#   define __TGMATH_2_NARROW_F32(F, X, Y)	\
+  (F ## f64 (X, Y))
+#  endif
 # endif /* !__HAVE_BUILTIN_TGMATH.  */
 #else
 # error "Unsupported compiler; you cannot use <tgmath.h>"
@@ -739,4 +844,66 @@
 /* Real part of Z.  */
 #define creal(Val) __TGMATH_UNARY_REAL_IMAG_RET_REAL_SAME (Val, creal)
 
+
+/* Narrowing functions.  */
+
+#if __GLIBC_USE (IEC_60559_BFP_EXT_C2X)
+
+/* Add.  */
+# define fadd(Val1, Val2) __TGMATH_2_NARROW_F (fadd, Val1, Val2)
+# define dadd(Val1, Val2) __TGMATH_2_NARROW_D (dadd, Val1, Val2)
+
+/* Divide.  */
+# define fdiv(Val1, Val2) __TGMATH_2_NARROW_F (fdiv, Val1, Val2)
+# define ddiv(Val1, Val2) __TGMATH_2_NARROW_D (ddiv, Val1, Val2)
+
+/* Multiply.  */
+# define fmul(Val1, Val2) __TGMATH_2_NARROW_F (fmul, Val1, Val2)
+# define dmul(Val1, Val2) __TGMATH_2_NARROW_D (dmul, Val1, Val2)
+
+/* Subtract.  */
+# define fsub(Val1, Val2) __TGMATH_2_NARROW_F (fsub, Val1, Val2)
+# define dsub(Val1, Val2) __TGMATH_2_NARROW_D (dsub, Val1, Val2)
+
+#endif
+
+#if __GLIBC_USE (IEC_60559_TYPES_EXT)
+
+# if __HAVE_FLOAT16
+#  define f16add(Val1, Val2) __TGMATH_2_NARROW_F16 (f16add, Val1, Val2)
+#  define f16div(Val1, Val2) __TGMATH_2_NARROW_F16 (f16div, Val1, Val2)
+#  define f16mul(Val1, Val2) __TGMATH_2_NARROW_F16 (f16mul, Val1, Val2)
+#  define f16sub(Val1, Val2) __TGMATH_2_NARROW_F16 (f16sub, Val1, Val2)
+# endif
+
+# if __HAVE_FLOAT32
+#  define f32add(Val1, Val2) __TGMATH_2_NARROW_F32 (f32add, Val1, Val2)
+#  define f32div(Val1, Val2) __TGMATH_2_NARROW_F32 (f32div, Val1, Val2)
+#  define f32mul(Val1, Val2) __TGMATH_2_NARROW_F32 (f32mul, Val1, Val2)
+#  define f32sub(Val1, Val2) __TGMATH_2_NARROW_F32 (f32sub, Val1, Val2)
+# endif
+
+# if __HAVE_FLOAT64 && (__HAVE_FLOAT64X || __HAVE_FLOAT128)
+#  define f64add(Val1, Val2) __TGMATH_2_NARROW_F64 (f64add, Val1, Val2)
+#  define f64div(Val1, Val2) __TGMATH_2_NARROW_F64 (f64div, Val1, Val2)
+#  define f64mul(Val1, Val2) __TGMATH_2_NARROW_F64 (f64mul, Val1, Val2)
+#  define f64sub(Val1, Val2) __TGMATH_2_NARROW_F64 (f64sub, Val1, Val2)
+# endif
+
+# if __HAVE_FLOAT32X
+#  define f32xadd(Val1, Val2) __TGMATH_2_NARROW_F32X (f32xadd, Val1, Val2)
+#  define f32xdiv(Val1, Val2) __TGMATH_2_NARROW_F32X (f32xdiv, Val1, Val2)
+#  define f32xmul(Val1, Val2) __TGMATH_2_NARROW_F32X (f32xmul, Val1, Val2)
+#  define f32xsub(Val1, Val2) __TGMATH_2_NARROW_F32X (f32xsub, Val1, Val2)
+# endif
+
+# if __HAVE_FLOAT64X && (__HAVE_FLOAT128X || __HAVE_FLOAT128)
+#  define f64xadd(Val1, Val2) __TGMATH_2_NARROW_F64X (f64xadd, Val1, Val2)
+#  define f64xdiv(Val1, Val2) __TGMATH_2_NARROW_F64X (f64xdiv, Val1, Val2)
+#  define f64xmul(Val1, Val2) __TGMATH_2_NARROW_F64X (f64xmul, Val1, Val2)
+#  define f64xsub(Val1, Val2) __TGMATH_2_NARROW_F64X (f64xsub, Val1, Val2)
+# endif
+
+#endif
+
 #endif /* tgmath.h */