[2/4] math: signal underflow for narrowing results that are tiny before rounding

Message ID 20260803235509.3532030-3-mattst88@gmail.com (mailing list archive)
State Dropped
Headers
Series Fix underflow signalling for narrowing operations on Alpha |

Checks

Context Check Description
redhat-pt-bot/TryBot-apply_patch success Patch applied to master at the time it was sent
linaro-tcwg-bot/tcwg_glibc_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_glibc_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_glibc_check--master-arm success Test passed
linaro-tcwg-bot/tcwg_glibc_check--master-aarch64 success Test passed

Commit Message

Matt Turner Aug. 3, 2026, 11:55 p.m. UTC
  A narrowing arithmetic function rounds the exact result once to the
narrower type, so it underflows whenever that result is both tiny and
inexact.  Two cases were not being signaled.

A result that rounds up to exactly the smallest normal value of the
narrower type is not tiny any more once rounded, so architectures
determining tininess after rounding do not raise underflow for it even
though the exact result was tiny.  Whether underflow is due is decided
by rounding the round-to-odd value with an unbounded exponent range:
dividing by the smallest normal is exact and moves it into the normal
range, so the conversion that follows rounds only the significand.

A subnormal result is tiny by inspection, and an inexact narrowing to
one underflows.  This is normally signaled by the narrowing conversion
itself, but is lost where that conversion is carried out in more than
one step: an intermediate type wide enough to keep the double rounding
harmless for the value can still round to a number the narrower type
represents exactly, leaving the final step exact.  This happens on
alpha, where the compiler converts long double to float via double.

Both raises are redundant on architectures that already signal these,
and harmless there.  The comparisons use the quiet relational macros
because the result is a NaN whenever an argument was, and the signaling
relational operators would raise a spurious invalid exception for it.

The macros take the smallest normal value of the narrower type as a new
argument, as only the callers know that type.
---
 math/math-narrow.h                       | 130 ++++++++++++++++++-----
 sysdeps/i386/fpu/s_f32xdivf64.c          |   3 +-
 sysdeps/i386/fpu/s_f32xmulf64.c          |   3 +-
 sysdeps/ieee754/dbl-64/s_fadd.c          |   3 +-
 sysdeps/ieee754/dbl-64/s_fdiv.c          |   4 +-
 sysdeps/ieee754/dbl-64/s_ffma.c          |   4 +-
 sysdeps/ieee754/dbl-64/s_fmul.c          |   4 +-
 sysdeps/ieee754/dbl-64/s_fsub.c          |   3 +-
 sysdeps/ieee754/ldbl-128/s_daddl.c       |   4 +-
 sysdeps/ieee754/ldbl-128/s_ddivl.c       |   4 +-
 sysdeps/ieee754/ldbl-128/s_dfmal.c       |   4 +-
 sysdeps/ieee754/ldbl-128/s_dmull.c       |   4 +-
 sysdeps/ieee754/ldbl-128/s_dsubl.c       |   4 +-
 sysdeps/ieee754/ldbl-128/s_f64xaddf128.c |   4 +-
 sysdeps/ieee754/ldbl-128/s_f64xdivf128.c |   5 +-
 sysdeps/ieee754/ldbl-128/s_f64xfmaf128.c |   5 +-
 sysdeps/ieee754/ldbl-128/s_f64xmulf128.c |   5 +-
 sysdeps/ieee754/ldbl-128/s_f64xsubf128.c |   4 +-
 sysdeps/ieee754/ldbl-128/s_faddl.c       |   4 +-
 sysdeps/ieee754/ldbl-128/s_fdivl.c       |   4 +-
 sysdeps/ieee754/ldbl-128/s_ffmal.c       |   4 +-
 sysdeps/ieee754/ldbl-128/s_fmull.c       |   4 +-
 sysdeps/ieee754/ldbl-128/s_fsubl.c       |   4 +-
 sysdeps/ieee754/ldbl-96/s_daddl.c        |   4 +-
 sysdeps/ieee754/ldbl-96/s_ddivl.c        |   4 +-
 sysdeps/ieee754/ldbl-96/s_dfmal.c        |   4 +-
 sysdeps/ieee754/ldbl-96/s_dmull.c        |   4 +-
 sysdeps/ieee754/ldbl-96/s_dsubl.c        |   4 +-
 sysdeps/ieee754/ldbl-96/s_faddl.c        |   4 +-
 sysdeps/ieee754/ldbl-96/s_fdivl.c        |   4 +-
 sysdeps/ieee754/ldbl-96/s_ffmal.c        |   4 +-
 sysdeps/ieee754/ldbl-96/s_fmull.c        |   4 +-
 sysdeps/ieee754/ldbl-96/s_fsubl.c        |   4 +-
 sysdeps/x86/fpu/s_ffma.c                 |   4 +-
 34 files changed, 172 insertions(+), 89 deletions(-)
  

Comments

Joseph Myers Aug. 4, 2026, 12:25 a.m. UTC | #1
On Mon, 3 Aug 2026, Matt Turner wrote:

> A result that rounds up to exactly the smallest normal value of the
> narrower type is not tiny any more once rounded, so architectures
> determining tininess after rounding do not raise underflow for it even
> though the exact result was tiny.  Whether underflow is due is decided
> by rounding the round-to-odd value with an unbounded exponent range:
> dividing by the smallest normal is exact and moves it into the normal
> range, so the conversion that follows rounds only the significand.

Are you claiming a bug on systems that implement IEEE 
tininess-after-rounding semantics correctly?  If so, please give a 
detailed example with the exact function inputs and value the current 
logic ends up rounding.  I'd expect round-to-odd to work correctly for 
tininess-after-rounding (since the at-least-two-bits-wider value being 
narrowed should produce the correct results both for narrowing to the 
target format, and for the narrowing with target precision but infinite 
exponent range that's used to determine underflow) if the hardware 
implements the right semantics.

Or are you working around a hardware bug that misses the right exceptions?  
If so, I'd expect the changes to be conditional so that no extra code is 
generated on architectures without this bug.

> A subnormal result is tiny by inspection, and an inexact narrowing to
> one underflows.  This is normally signaled by the narrowing conversion
> itself, but is lost where that conversion is carried out in more than
> one step: an intermediate type wide enough to keep the double rounding
> harmless for the value can still round to a number the narrower type
> represents exactly, leaving the final step exact.  This happens on
> alpha, where the compiler converts long double to float via double.

This sound like it's working around a compiler bug and so should 
definitely be conditional to only apply with architectures and compilers 
where it's needed.
  
Matt Turner Aug. 4, 2026, 2:10 a.m. UTC | #2
On Tue, 4 Aug 2026, Joseph Myers wrote:

> Are you claiming a bug on systems that implement IEEE
> tininess-after-rounding semantics correctly?

No.  You are right that round-to-odd already gets this right there, and I
should have checked that before writing it up as something general.  On
x86_64, with neither this patch nor the fmaf one applied:

  fdiv (-0x4p-128, 0x1.000002p+0) in FE_DOWNWARD
    -> -0x1p-126, underflow raised
  fmaf (-0x8p-152, 0x8.8p-4, -0x3.fffff8p-128)
    -> -0x1p-126, underflow raised

So both patches are working around Alpha and neither is needed elsewhere.

The detailed example you asked for.  For fdiv (-0x4p-128, 0x1.000002p+0)
in FE_DOWNWARD the exact quotient is -0x1.fffffc0000080p-127, below
FLT_MIN.  Rounded to 24 bits with an unbounded exponent range it is
-0x1.fffffep-127, still below FLT_MIN, so the result is tiny after
rounding and underflow is due.  Alpha delivers -0x1p-126 and raises
nothing.  It decides tininess from the delivered result, and in the
bounded format the spacing below FLT_MIN is 2^-149 rather than the 2^-150
of the binade the unbounded rounding lands in, so the value reaches
FLT_MIN exactly and looks normal.  That is Alpha deviating from the
standard, not round-to-odd mishandling anything.

> This sound like it's working around a compiler bug

It is, and a different one.  GCC lowers a long double to float conversion
on Alpha as quad to double to float.  The trunctfsf2 expander sets a
sticky bit at fraction bit 48 so that the second rounding has no tie to
break, but the conversion to DFmode rounds as well and its carry can
propagate through bit 48 and clear it.  I have a fix for that and will
send it to gcc-patches.

Please drop this patch and 3/4.  I will make the workaround conditional on
a macro that generic code defines to do nothing, so only Alpha pays for
it, and resend.  1/4 and 4/4 are Alpha specific already and do not depend
on these.

Thanks for catching this.

Matt
  

Patch

diff --git ./math/math-narrow.h ./math/math-narrow.h
index bb319cc2ca..51a43cb92b 100644
--- ./math/math-narrow.h
+++ ./math/math-narrow.h
@@ -62,6 +62,57 @@ 
     u.d;								\
   })
 
+/* Raise the underflow exception for a narrowing of the round-to-odd
+   value W to RET that is tiny and inexact but whose underflow the
+   architecture did not signal.  A narrowing operation rounds the exact
+   result once to the narrower type, so it underflows whenever that
+   result is both tiny and inexact.  MIN_NORMAL is the smallest positive
+   normal value of the narrower type.  Two cases need help here, and on
+   architectures that already signal them the extra raise is redundant
+   but harmless.
+
+   A subnormal RET is tiny by inspection, so an inexact narrowing to one
+   underflows.  This is normally signaled by the narrowing conversion,
+   but is lost where that conversion is performed in more than one step:
+   an intermediate type wide enough to make the double rounding harmless
+   for the value can still round to a number the narrower type
+   represents exactly, leaving the final step exact.
+
+   A RET of exactly MIN_NORMAL is not tiny after rounding, so
+   architectures detecting tininess after rounding do not signal it even
+   though the exact result was tiny.  Rounding W with an unbounded
+   exponent range decides whether it is tiny under that rule too.
+
+   The comparisons use the quiet relational macros: RET is a NaN
+   whenever an argument was, and the signaling relational operators
+   would raise a spurious invalid exception for it.  */
+#ifdef FE_UNDERFLOW
+# define CHECK_NARROW_TINY(RET, W, MIN_NORMAL)				\
+  do									\
+    {									\
+      if (isless (RET, MIN_NORMAL) && isgreater (RET, -(MIN_NORMAL)))	\
+	{								\
+	  if ((W) != (RET))						\
+	    __feraiseexcept (FE_UNDERFLOW);				\
+	}								\
+      else if (((RET) == (MIN_NORMAL) || (RET) == -(MIN_NORMAL))	\
+	       && isless (W, MIN_NORMAL) && isgreater (W, -(MIN_NORMAL))) \
+	{								\
+	  /* Dividing by MIN_NORMAL is exact and moves W into the	\
+	     normal range, so the conversion below rounds only the	\
+	     significand.  Underflow is due only when the value is	\
+	     still tiny afterwards; when it rounds up to exactly	\
+	     MIN_NORMAL it is not tiny after rounding.  */		\
+	  __typeof (RET) __scaled = (__typeof (RET)) ((W) / (MIN_NORMAL)); \
+	  if (__scaled > -1 && __scaled < 1)				\
+	    __feraiseexcept (FE_UNDERFLOW);				\
+	}								\
+    }									\
+  while (0)
+#else
+# define CHECK_NARROW_TINY(RET, W, MIN_NORMAL) do { } while (0)
+#endif
+
 /* Check for error conditions from a narrowing add function returning
    RET with arguments X and Y and set errno as needed.  Overflow and
    underflow can occur for finite arguments and a domain error for
@@ -85,9 +136,11 @@ 
   while (0)
 
 /* Implement narrowing add using round-to-odd.  The arguments are X
-   and Y, the return type is TYPE and UNION, MANTISSA and SUFFIX are
-   as for ROUND_TO_ODD.  */
-#define NARROW_ADD_ROUND_TO_ODD(X, Y, TYPE, UNION, SUFFIX, MANTISSA)	\
+   and Y, the return type is TYPE, MIN_NORMAL is the smallest positive
+   normal value of TYPE and UNION, MANTISSA and SUFFIX are as for
+   ROUND_TO_ODD.  */
+#define NARROW_ADD_ROUND_TO_ODD(X, Y, TYPE, MIN_NORMAL, UNION, SUFFIX,	\
+				MANTISSA)				\
   do									\
     {									\
       TYPE ret;								\
@@ -97,8 +150,13 @@ 
       if ((X) == -(Y))							\
 	ret = (TYPE) ((X) + (Y));					\
       else								\
-	ret = (TYPE) ROUND_TO_ODD (math_opt_barrier (X) + (Y),		\
-				   UNION, SUFFIX, MANTISSA, false);	\
+	{								\
+	  __typeof ((X) + (Y)) tmp					\
+	    = ROUND_TO_ODD (math_opt_barrier (X) + (Y),			\
+			    UNION, SUFFIX, MANTISSA, false);		\
+	  ret = (TYPE) tmp;						\
+	  CHECK_NARROW_TINY (ret, tmp, MIN_NORMAL);			\
+	}								\
 									\
       CHECK_NARROW_ADD (ret, (X), (Y));					\
       return ret;							\
@@ -143,9 +201,11 @@ 
   while (0)
 
 /* Implement narrowing subtract using round-to-odd.  The arguments are
-   X and Y, the return type is TYPE and UNION, MANTISSA and SUFFIX are
-   as for ROUND_TO_ODD.  */
-#define NARROW_SUB_ROUND_TO_ODD(X, Y, TYPE, UNION, SUFFIX, MANTISSA)	\
+   X and Y, the return type is TYPE, MIN_NORMAL is the smallest positive
+   normal value of TYPE and UNION, MANTISSA and SUFFIX are as for
+   ROUND_TO_ODD.  */
+#define NARROW_SUB_ROUND_TO_ODD(X, Y, TYPE, MIN_NORMAL, UNION, SUFFIX,	\
+				MANTISSA)				\
   do									\
     {									\
       TYPE ret;								\
@@ -155,8 +215,13 @@ 
       if ((X) == (Y))							\
 	ret = (TYPE) ((X) - (Y));					\
       else								\
-	ret = (TYPE) ROUND_TO_ODD (math_opt_barrier (X) - (Y),		\
-				   UNION, SUFFIX, MANTISSA, false);	\
+	{								\
+	  __typeof ((X) - (Y)) tmp					\
+	    = ROUND_TO_ODD (math_opt_barrier (X) - (Y),			\
+			    UNION, SUFFIX, MANTISSA, false);		\
+	  ret = (TYPE) tmp;						\
+	  CHECK_NARROW_TINY (ret, tmp, MIN_NORMAL);			\
+	}								\
 									\
       CHECK_NARROW_SUB (ret, (X), (Y));					\
       return ret;							\
@@ -201,17 +266,21 @@ 
   while (0)
 
 /* Implement narrowing multiply using round-to-odd.  The arguments are
-   X and Y, the return type is TYPE and UNION, MANTISSA, SUFFIX and
-   CLEAR_UNDERFLOW are as for ROUND_TO_ODD.  */
-#define NARROW_MUL_ROUND_TO_ODD(X, Y, TYPE, UNION, SUFFIX, MANTISSA,	\
-				CLEAR_UNDERFLOW)			\
+   X and Y, the return type is TYPE, MIN_NORMAL is the smallest positive
+   normal value of TYPE and UNION, MANTISSA, SUFFIX and CLEAR_UNDERFLOW
+   are as for ROUND_TO_ODD.  */
+#define NARROW_MUL_ROUND_TO_ODD(X, Y, TYPE, MIN_NORMAL, UNION, SUFFIX,	\
+				MANTISSA, CLEAR_UNDERFLOW)		\
   do									\
     {									\
       TYPE ret;								\
+      __typeof ((X) * (Y)) tmp;						\
 									\
-      ret = (TYPE) ROUND_TO_ODD (math_opt_barrier (X) * (Y),		\
-				 UNION, SUFFIX, MANTISSA,		\
-				 CLEAR_UNDERFLOW);			\
+      tmp = ROUND_TO_ODD (math_opt_barrier (X) * (Y),			\
+			  UNION, SUFFIX, MANTISSA,			\
+			  CLEAR_UNDERFLOW);				\
+      ret = (TYPE) tmp;							\
+      CHECK_NARROW_TINY (ret, tmp, MIN_NORMAL);				\
 									\
       CHECK_NARROW_MUL (ret, (X), (Y));					\
       return ret;							\
@@ -256,17 +325,21 @@ 
   while (0)
 
 /* Implement narrowing divide using round-to-odd.  The arguments are X
-   and Y, the return type is TYPE and UNION, MANTISSA, SUFFIX and
-   CLEAR_UNDERFLOW are as for ROUND_TO_ODD.  */
-#define NARROW_DIV_ROUND_TO_ODD(X, Y, TYPE, UNION, SUFFIX, MANTISSA,	\
-				CLEAR_UNDERFLOW)			\
+   and Y, the return type is TYPE, MIN_NORMAL is the smallest positive
+   normal value of TYPE and UNION, MANTISSA, SUFFIX and CLEAR_UNDERFLOW
+   are as for ROUND_TO_ODD.  */
+#define NARROW_DIV_ROUND_TO_ODD(X, Y, TYPE, MIN_NORMAL, UNION, SUFFIX,	\
+				MANTISSA, CLEAR_UNDERFLOW)		\
   do									\
     {									\
       TYPE ret;								\
+      __typeof ((X) / (Y)) tmp;						\
 									\
-      ret = (TYPE) ROUND_TO_ODD (math_opt_barrier (X) / (Y),		\
-				 UNION, SUFFIX, MANTISSA,		\
-				 CLEAR_UNDERFLOW);			\
+      tmp = ROUND_TO_ODD (math_opt_barrier (X) / (Y),			\
+			  UNION, SUFFIX, MANTISSA,			\
+			  CLEAR_UNDERFLOW);				\
+      ret = (TYPE) tmp;							\
+      CHECK_NARROW_TINY (ret, tmp, MIN_NORMAL);				\
 									\
       CHECK_NARROW_DIV (ret, (X), (Y));					\
       return ret;							\
@@ -356,8 +429,8 @@ 
 /* Implement narrowing fused multiply-add using round-to-odd.  The
    arguments are X, Y and Z, the return type is TYPE and UNION,
    MANTISSA, SUFFIX and CLEAR_UNDERFLOW are as for ROUND_TO_ODD.  */
-#define NARROW_FMA_ROUND_TO_ODD(X, Y, Z, TYPE, UNION, SUFFIX, MANTISSA, \
-				CLEAR_UNDERFLOW)			\
+#define NARROW_FMA_ROUND_TO_ODD(X, Y, Z, TYPE, MIN_NORMAL, UNION,	\
+				SUFFIX, MANTISSA, CLEAR_UNDERFLOW)	\
   do									\
     {									\
       typeof (X) tmp;							\
@@ -371,7 +444,10 @@ 
       if (tmp == 0)							\
 	ret = (TYPE) (math_opt_barrier (X) * (Y) + (Z));		\
       else								\
-	ret = (TYPE) tmp;						\
+	{								\
+	  ret = (TYPE) tmp;						\
+	  CHECK_NARROW_TINY (ret, tmp, MIN_NORMAL);			\
+	}								\
 									\
       CHECK_NARROW_FMA (ret, (X), (Y), (Z));				\
       return ret;							\
diff --git ./sysdeps/i386/fpu/s_f32xdivf64.c ./sysdeps/i386/fpu/s_f32xdivf64.c
index 9090844404..00e9a51676 100644
--- ./sysdeps/i386/fpu/s_f32xdivf64.c
+++ ./sysdeps/i386/fpu/s_f32xdivf64.c
@@ -24,6 +24,7 @@  __f32xdivf64 (_Float64 x, _Float64 y)
 {
   /* To avoid double rounding, use round-to-odd on long double.  */
   NARROW_DIV_ROUND_TO_ODD ((long double) x, (long double) y, double,
-			   union ieee854_long_double, l, mantissa1, false);
+			   __DBL_MIN__, union ieee854_long_double, l,
+			   mantissa1, false);
 }
 libm_alias_float32x_float64 (div)
diff --git ./sysdeps/i386/fpu/s_f32xmulf64.c ./sysdeps/i386/fpu/s_f32xmulf64.c
index 536ddd4d78..fc96096782 100644
--- ./sysdeps/i386/fpu/s_f32xmulf64.c
+++ ./sysdeps/i386/fpu/s_f32xmulf64.c
@@ -24,6 +24,7 @@  __f32xmulf64 (_Float64 x, _Float64 y)
 {
   /* To avoid double rounding, use round-to-odd on long double.  */
   NARROW_MUL_ROUND_TO_ODD ((long double) x, (long double) y, double,
-			   union ieee854_long_double, l, mantissa1, false);
+			   __DBL_MIN__, union ieee854_long_double, l,
+			   mantissa1, false);
 }
 libm_alias_float32x_float64 (mul)
diff --git ./sysdeps/ieee754/dbl-64/s_fadd.c ./sysdeps/ieee754/dbl-64/s_fadd.c
index 22bf2766df..7091ae1539 100644
--- ./sysdeps/ieee754/dbl-64/s_fadd.c
+++ ./sysdeps/ieee754/dbl-64/s_fadd.c
@@ -29,6 +29,7 @@ 
 float
 __fadd (double x, double y)
 {
-  NARROW_ADD_ROUND_TO_ODD (x, y, float, union ieee754_double, , mantissa1);
+  NARROW_ADD_ROUND_TO_ODD (x, y, float, __FLT_MIN__, union ieee754_double, ,
+			   mantissa1);
 }
 libm_alias_float_double (add)
diff --git ./sysdeps/ieee754/dbl-64/s_fdiv.c ./sysdeps/ieee754/dbl-64/s_fdiv.c
index 918e1722ca..5de0f1f37c 100644
--- ./sysdeps/ieee754/dbl-64/s_fdiv.c
+++ ./sysdeps/ieee754/dbl-64/s_fdiv.c
@@ -29,7 +29,7 @@ 
 float
 __fdiv (double x, double y)
 {
-  NARROW_DIV_ROUND_TO_ODD (x, y, float, union ieee754_double, , mantissa1,
-			   false);
+  NARROW_DIV_ROUND_TO_ODD (x, y, float, __FLT_MIN__, union ieee754_double, ,
+			   mantissa1, false);
 }
 libm_alias_float_double (div)
diff --git ./sysdeps/ieee754/dbl-64/s_ffma.c ./sysdeps/ieee754/dbl-64/s_ffma.c
index 14db126cfd..f341fa88cd 100644
--- ./sysdeps/ieee754/dbl-64/s_ffma.c
+++ ./sysdeps/ieee754/dbl-64/s_ffma.c
@@ -29,7 +29,7 @@ 
 float
 __ffma (double x, double y, double z)
 {
-  NARROW_FMA_ROUND_TO_ODD (x, y, z, float, union ieee754_double, , mantissa1,
-			   false);
+  NARROW_FMA_ROUND_TO_ODD (x, y, z, float, __FLT_MIN__, union ieee754_double,
+			   , mantissa1, false);
 }
 libm_alias_float_double (fma)
diff --git ./sysdeps/ieee754/dbl-64/s_fmul.c ./sysdeps/ieee754/dbl-64/s_fmul.c
index 2fdc7c5f22..528096bc83 100644
--- ./sysdeps/ieee754/dbl-64/s_fmul.c
+++ ./sysdeps/ieee754/dbl-64/s_fmul.c
@@ -29,7 +29,7 @@ 
 float
 __fmul (double x, double y)
 {
-  NARROW_MUL_ROUND_TO_ODD (x, y, float, union ieee754_double, , mantissa1,
-			   false);
+  NARROW_MUL_ROUND_TO_ODD (x, y, float, __FLT_MIN__, union ieee754_double, ,
+			   mantissa1, false);
 }
 libm_alias_float_double (mul)
diff --git ./sysdeps/ieee754/dbl-64/s_fsub.c ./sysdeps/ieee754/dbl-64/s_fsub.c
index 79ccfaef9d..9008e7cbf4 100644
--- ./sysdeps/ieee754/dbl-64/s_fsub.c
+++ ./sysdeps/ieee754/dbl-64/s_fsub.c
@@ -29,6 +29,7 @@ 
 float
 __fsub (double x, double y)
 {
-  NARROW_SUB_ROUND_TO_ODD (x, y, float, union ieee754_double, , mantissa1);
+  NARROW_SUB_ROUND_TO_ODD (x, y, float, __FLT_MIN__, union ieee754_double, ,
+			   mantissa1);
 }
 libm_alias_float_double (sub)
diff --git ./sysdeps/ieee754/ldbl-128/s_daddl.c ./sysdeps/ieee754/ldbl-128/s_daddl.c
index 17a76321c0..97d1073b11 100644
--- ./sysdeps/ieee754/ldbl-128/s_daddl.c
+++ ./sysdeps/ieee754/ldbl-128/s_daddl.c
@@ -31,7 +31,7 @@ 
 double
 __daddl (_Float128 x, _Float128 y)
 {
-  NARROW_ADD_ROUND_TO_ODD (x, y, double, union ieee854_long_double, l,
-			   mantissa3);
+  NARROW_ADD_ROUND_TO_ODD (x, y, double, __DBL_MIN__,
+			   union ieee854_long_double, l, mantissa3);
 }
 libm_alias_double_ldouble (add)
diff --git ./sysdeps/ieee754/ldbl-128/s_ddivl.c ./sysdeps/ieee754/ldbl-128/s_ddivl.c
index be17f2d3db..a30fcb959c 100644
--- ./sysdeps/ieee754/ldbl-128/s_ddivl.c
+++ ./sysdeps/ieee754/ldbl-128/s_ddivl.c
@@ -31,7 +31,7 @@ 
 double
 __ddivl (_Float128 x, _Float128 y)
 {
-  NARROW_DIV_ROUND_TO_ODD (x, y, double, union ieee854_long_double, l,
-			   mantissa3, false);
+  NARROW_DIV_ROUND_TO_ODD (x, y, double, __DBL_MIN__,
+			   union ieee854_long_double, l, mantissa3, false);
 }
 libm_alias_double_ldouble (div)
diff --git ./sysdeps/ieee754/ldbl-128/s_dfmal.c ./sysdeps/ieee754/ldbl-128/s_dfmal.c
index a6784e6fc1..beacfa2c73 100644
--- ./sysdeps/ieee754/ldbl-128/s_dfmal.c
+++ ./sysdeps/ieee754/ldbl-128/s_dfmal.c
@@ -32,7 +32,7 @@ 
 double
 __dfmal (_Float128 x, _Float128 y, _Float128 z)
 {
-  NARROW_FMA_ROUND_TO_ODD (x, y, z, double, union ieee854_long_double, l,
-			   mantissa3, false);
+  NARROW_FMA_ROUND_TO_ODD (x, y, z, double, __DBL_MIN__,
+			   union ieee854_long_double, l, mantissa3, false);
 }
 libm_alias_double_ldouble (fma)
diff --git ./sysdeps/ieee754/ldbl-128/s_dmull.c ./sysdeps/ieee754/ldbl-128/s_dmull.c
index 5e5f2ee6d1..60ab752095 100644
--- ./sysdeps/ieee754/ldbl-128/s_dmull.c
+++ ./sysdeps/ieee754/ldbl-128/s_dmull.c
@@ -31,7 +31,7 @@ 
 double
 __dmull (_Float128 x, _Float128 y)
 {
-  NARROW_MUL_ROUND_TO_ODD (x, y, double, union ieee854_long_double, l,
-			   mantissa3, false);
+  NARROW_MUL_ROUND_TO_ODD (x, y, double, __DBL_MIN__,
+			   union ieee854_long_double, l, mantissa3, false);
 }
 libm_alias_double_ldouble (mul)
diff --git ./sysdeps/ieee754/ldbl-128/s_dsubl.c ./sysdeps/ieee754/ldbl-128/s_dsubl.c
index a2e51664bc..b0583ef518 100644
--- ./sysdeps/ieee754/ldbl-128/s_dsubl.c
+++ ./sysdeps/ieee754/ldbl-128/s_dsubl.c
@@ -31,7 +31,7 @@ 
 double
 __dsubl (_Float128 x, _Float128 y)
 {
-  NARROW_SUB_ROUND_TO_ODD (x, y, double, union ieee854_long_double, l,
-			   mantissa3);
+  NARROW_SUB_ROUND_TO_ODD (x, y, double, __DBL_MIN__,
+			   union ieee854_long_double, l, mantissa3);
 }
 libm_alias_double_ldouble (sub)
diff --git ./sysdeps/ieee754/ldbl-128/s_f64xaddf128.c ./sysdeps/ieee754/ldbl-128/s_f64xaddf128.c
index 8a53f146fd..bf2d0ccf5f 100644
--- ./sysdeps/ieee754/ldbl-128/s_f64xaddf128.c
+++ ./sysdeps/ieee754/ldbl-128/s_f64xaddf128.c
@@ -29,8 +29,8 @@  _Float64x
 __f64xaddf128 (_Float128 x, _Float128 y)
 {
 #if __HAVE_FLOAT64X_LONG_DOUBLE && __HAVE_DISTINCT_FLOAT128
-  NARROW_ADD_ROUND_TO_ODD (x, y, _Float64x, union ieee854_long_double, l,
-			   mantissa3);
+  NARROW_ADD_ROUND_TO_ODD (x, y, _Float64x, __FLT64X_MIN__,
+			   union ieee854_long_double, l, mantissa3);
 #else
   NARROW_ADD_TRIVIAL (x, y, _Float64x);
 #endif
diff --git ./sysdeps/ieee754/ldbl-128/s_f64xdivf128.c ./sysdeps/ieee754/ldbl-128/s_f64xdivf128.c
index 51573c8adf..af594f566e 100644
--- ./sysdeps/ieee754/ldbl-128/s_f64xdivf128.c
+++ ./sysdeps/ieee754/ldbl-128/s_f64xdivf128.c
@@ -30,8 +30,9 @@  _Float64x
 __f64xdivf128 (_Float128 x, _Float128 y)
 {
 #if __HAVE_FLOAT64X_LONG_DOUBLE && __HAVE_DISTINCT_FLOAT128
-  NARROW_DIV_ROUND_TO_ODD (x, y, _Float64x, union ieee854_long_double, l,
-			   mantissa3, TININESS_AFTER_ROUNDING);
+  NARROW_DIV_ROUND_TO_ODD (x, y, _Float64x, __FLT64X_MIN__,
+			   union ieee854_long_double, l, mantissa3,
+			   TININESS_AFTER_ROUNDING);
 #else
   NARROW_DIV_TRIVIAL (x, y, _Float64x);
 #endif
diff --git ./sysdeps/ieee754/ldbl-128/s_f64xfmaf128.c ./sysdeps/ieee754/ldbl-128/s_f64xfmaf128.c
index 0f7d5592e0..af0436263c 100644
--- ./sysdeps/ieee754/ldbl-128/s_f64xfmaf128.c
+++ ./sysdeps/ieee754/ldbl-128/s_f64xfmaf128.c
@@ -30,8 +30,9 @@ 
 _Float64x
 __f64xfmaf128 (_Float128 x, _Float128 y, _Float128 z)
 {
-  NARROW_FMA_ROUND_TO_ODD (x, y, z, _Float64x, union ieee854_long_double, l,
-			   mantissa3, TININESS_AFTER_ROUNDING);
+  NARROW_FMA_ROUND_TO_ODD (x, y, z, _Float64x, __FLT64X_MIN__,
+			   union ieee854_long_double, l, mantissa3,
+			   TININESS_AFTER_ROUNDING);
 }
 libm_alias_float64x_float128 (fma)
 #else
diff --git ./sysdeps/ieee754/ldbl-128/s_f64xmulf128.c ./sysdeps/ieee754/ldbl-128/s_f64xmulf128.c
index 6a59e04bb1..d4e3322d25 100644
--- ./sysdeps/ieee754/ldbl-128/s_f64xmulf128.c
+++ ./sysdeps/ieee754/ldbl-128/s_f64xmulf128.c
@@ -30,8 +30,9 @@  _Float64x
 __f64xmulf128 (_Float128 x, _Float128 y)
 {
 #if __HAVE_FLOAT64X_LONG_DOUBLE && __HAVE_DISTINCT_FLOAT128
-  NARROW_MUL_ROUND_TO_ODD (x, y, _Float64x, union ieee854_long_double, l,
-			   mantissa3, TININESS_AFTER_ROUNDING);
+  NARROW_MUL_ROUND_TO_ODD (x, y, _Float64x, __FLT64X_MIN__,
+			   union ieee854_long_double, l, mantissa3,
+			   TININESS_AFTER_ROUNDING);
 #else
   NARROW_MUL_TRIVIAL (x, y, _Float64x);
 #endif
diff --git ./sysdeps/ieee754/ldbl-128/s_f64xsubf128.c ./sysdeps/ieee754/ldbl-128/s_f64xsubf128.c
index 05311608c8..dffb7ac7d8 100644
--- ./sysdeps/ieee754/ldbl-128/s_f64xsubf128.c
+++ ./sysdeps/ieee754/ldbl-128/s_f64xsubf128.c
@@ -29,8 +29,8 @@  _Float64x
 __f64xsubf128 (_Float128 x, _Float128 y)
 {
 #if __HAVE_FLOAT64X_LONG_DOUBLE && __HAVE_DISTINCT_FLOAT128
-  NARROW_SUB_ROUND_TO_ODD (x, y, _Float64x, union ieee854_long_double, l,
-			   mantissa3);
+  NARROW_SUB_ROUND_TO_ODD (x, y, _Float64x, __FLT64X_MIN__,
+			   union ieee854_long_double, l, mantissa3);
 #else
   NARROW_SUB_TRIVIAL (x, y, _Float64x);
 #endif
diff --git ./sysdeps/ieee754/ldbl-128/s_faddl.c ./sysdeps/ieee754/ldbl-128/s_faddl.c
index 3b74da68dc..41d7a7846b 100644
--- ./sysdeps/ieee754/ldbl-128/s_faddl.c
+++ ./sysdeps/ieee754/ldbl-128/s_faddl.c
@@ -27,7 +27,7 @@ 
 float
 __faddl (_Float128 x, _Float128 y)
 {
-  NARROW_ADD_ROUND_TO_ODD (x, y, float, union ieee854_long_double, l,
-			   mantissa3);
+  NARROW_ADD_ROUND_TO_ODD (x, y, float, __FLT_MIN__,
+			   union ieee854_long_double, l, mantissa3);
 }
 libm_alias_float_ldouble (add)
diff --git ./sysdeps/ieee754/ldbl-128/s_fdivl.c ./sysdeps/ieee754/ldbl-128/s_fdivl.c
index 3d10e4eec0..2d7389e02c 100644
--- ./sysdeps/ieee754/ldbl-128/s_fdivl.c
+++ ./sysdeps/ieee754/ldbl-128/s_fdivl.c
@@ -27,7 +27,7 @@ 
 float
 __fdivl (_Float128 x, _Float128 y)
 {
-  NARROW_DIV_ROUND_TO_ODD (x, y, float, union ieee854_long_double, l,
-			   mantissa3, false);
+  NARROW_DIV_ROUND_TO_ODD (x, y, float, __FLT_MIN__,
+			   union ieee854_long_double, l, mantissa3, false);
 }
 libm_alias_float_ldouble (div)
diff --git ./sysdeps/ieee754/ldbl-128/s_ffmal.c ./sysdeps/ieee754/ldbl-128/s_ffmal.c
index 711baf893e..92be4e736c 100644
--- ./sysdeps/ieee754/ldbl-128/s_ffmal.c
+++ ./sysdeps/ieee754/ldbl-128/s_ffmal.c
@@ -28,7 +28,7 @@ 
 float
 __ffmal (_Float128 x, _Float128 y, _Float128 z)
 {
-  NARROW_FMA_ROUND_TO_ODD (x, y, z, float, union ieee854_long_double, l,
-			   mantissa3, false);
+  NARROW_FMA_ROUND_TO_ODD (x, y, z, float, __FLT_MIN__,
+			   union ieee854_long_double, l, mantissa3, false);
 }
 libm_alias_float_ldouble (fma)
diff --git ./sysdeps/ieee754/ldbl-128/s_fmull.c ./sysdeps/ieee754/ldbl-128/s_fmull.c
index 2534d6636b..a13fe149db 100644
--- ./sysdeps/ieee754/ldbl-128/s_fmull.c
+++ ./sysdeps/ieee754/ldbl-128/s_fmull.c
@@ -27,7 +27,7 @@ 
 float
 __fmull (_Float128 x, _Float128 y)
 {
-  NARROW_MUL_ROUND_TO_ODD (x, y, float, union ieee854_long_double, l,
-			   mantissa3, false);
+  NARROW_MUL_ROUND_TO_ODD (x, y, float, __FLT_MIN__,
+			   union ieee854_long_double, l, mantissa3, false);
 }
 libm_alias_float_ldouble (mul)
diff --git ./sysdeps/ieee754/ldbl-128/s_fsubl.c ./sysdeps/ieee754/ldbl-128/s_fsubl.c
index 57b7bb3f9f..38c5ce8d9b 100644
--- ./sysdeps/ieee754/ldbl-128/s_fsubl.c
+++ ./sysdeps/ieee754/ldbl-128/s_fsubl.c
@@ -27,7 +27,7 @@ 
 float
 __fsubl (_Float128 x, _Float128 y)
 {
-  NARROW_SUB_ROUND_TO_ODD (x, y, float, union ieee854_long_double, l,
-			   mantissa3);
+  NARROW_SUB_ROUND_TO_ODD (x, y, float, __FLT_MIN__,
+			   union ieee854_long_double, l, mantissa3);
 }
 libm_alias_float_ldouble (sub)
diff --git ./sysdeps/ieee754/ldbl-96/s_daddl.c ./sysdeps/ieee754/ldbl-96/s_daddl.c
index ffdede0807..846509d819 100644
--- ./sysdeps/ieee754/ldbl-96/s_daddl.c
+++ ./sysdeps/ieee754/ldbl-96/s_daddl.c
@@ -27,7 +27,7 @@ 
 double
 __daddl (long double x, long double y)
 {
-  NARROW_ADD_ROUND_TO_ODD (x, y, double, union ieee854_long_double, l,
-			   mantissa1);
+  NARROW_ADD_ROUND_TO_ODD (x, y, double, __DBL_MIN__,
+			   union ieee854_long_double, l, mantissa1);
 }
 libm_alias_double_ldouble (add)
diff --git ./sysdeps/ieee754/ldbl-96/s_ddivl.c ./sysdeps/ieee754/ldbl-96/s_ddivl.c
index 1bbea6fae7..fb811704e5 100644
--- ./sysdeps/ieee754/ldbl-96/s_ddivl.c
+++ ./sysdeps/ieee754/ldbl-96/s_ddivl.c
@@ -27,7 +27,7 @@ 
 double
 __ddivl (long double x, long double y)
 {
-  NARROW_DIV_ROUND_TO_ODD (x, y, double, union ieee854_long_double, l,
-			   mantissa1, false);
+  NARROW_DIV_ROUND_TO_ODD (x, y, double, __DBL_MIN__,
+			   union ieee854_long_double, l, mantissa1, false);
 }
 libm_alias_double_ldouble (div)
diff --git ./sysdeps/ieee754/ldbl-96/s_dfmal.c ./sysdeps/ieee754/ldbl-96/s_dfmal.c
index 332ea895ec..b2fb24eb06 100644
--- ./sysdeps/ieee754/ldbl-96/s_dfmal.c
+++ ./sysdeps/ieee754/ldbl-96/s_dfmal.c
@@ -28,7 +28,7 @@ 
 double
 __dfmal (long double x, long double y, long double z)
 {
-  NARROW_FMA_ROUND_TO_ODD (x, y, z, double, union ieee854_long_double, l,
-			   mantissa1, false);
+  NARROW_FMA_ROUND_TO_ODD (x, y, z, double, __DBL_MIN__,
+			   union ieee854_long_double, l, mantissa1, false);
 }
 libm_alias_double_ldouble (fma)
diff --git ./sysdeps/ieee754/ldbl-96/s_dmull.c ./sysdeps/ieee754/ldbl-96/s_dmull.c
index faeede2f0e..b023bded5d 100644
--- ./sysdeps/ieee754/ldbl-96/s_dmull.c
+++ ./sysdeps/ieee754/ldbl-96/s_dmull.c
@@ -27,7 +27,7 @@ 
 double
 __dmull (long double x, long double y)
 {
-  NARROW_MUL_ROUND_TO_ODD (x, y, double, union ieee854_long_double, l,
-			   mantissa1, false);
+  NARROW_MUL_ROUND_TO_ODD (x, y, double, __DBL_MIN__,
+			   union ieee854_long_double, l, mantissa1, false);
 }
 libm_alias_double_ldouble (mul)
diff --git ./sysdeps/ieee754/ldbl-96/s_dsubl.c ./sysdeps/ieee754/ldbl-96/s_dsubl.c
index 7ac31434dd..594512810d 100644
--- ./sysdeps/ieee754/ldbl-96/s_dsubl.c
+++ ./sysdeps/ieee754/ldbl-96/s_dsubl.c
@@ -27,7 +27,7 @@ 
 double
 __dsubl (long double x, long double y)
 {
-  NARROW_SUB_ROUND_TO_ODD (x, y, double, union ieee854_long_double, l,
-			   mantissa1);
+  NARROW_SUB_ROUND_TO_ODD (x, y, double, __DBL_MIN__,
+			   union ieee854_long_double, l, mantissa1);
 }
 libm_alias_double_ldouble (sub)
diff --git ./sysdeps/ieee754/ldbl-96/s_faddl.c ./sysdeps/ieee754/ldbl-96/s_faddl.c
index 46d4a035a3..fae922bb1b 100644
--- ./sysdeps/ieee754/ldbl-96/s_faddl.c
+++ ./sysdeps/ieee754/ldbl-96/s_faddl.c
@@ -25,7 +25,7 @@ 
 float
 __faddl (long double x, long double y)
 {
-  NARROW_ADD_ROUND_TO_ODD (x, y, float, union ieee854_long_double, l,
-			   mantissa1);
+  NARROW_ADD_ROUND_TO_ODD (x, y, float, __FLT_MIN__,
+			   union ieee854_long_double, l, mantissa1);
 }
 libm_alias_float_ldouble (add)
diff --git ./sysdeps/ieee754/ldbl-96/s_fdivl.c ./sysdeps/ieee754/ldbl-96/s_fdivl.c
index a819df6d81..0cddc4f745 100644
--- ./sysdeps/ieee754/ldbl-96/s_fdivl.c
+++ ./sysdeps/ieee754/ldbl-96/s_fdivl.c
@@ -25,7 +25,7 @@ 
 float
 __fdivl (long double x, long double y)
 {
-  NARROW_DIV_ROUND_TO_ODD (x, y, float, union ieee854_long_double, l,
-			   mantissa1, false);
+  NARROW_DIV_ROUND_TO_ODD (x, y, float, __FLT_MIN__,
+			   union ieee854_long_double, l, mantissa1, false);
 }
 libm_alias_float_ldouble (div)
diff --git ./sysdeps/ieee754/ldbl-96/s_ffmal.c ./sysdeps/ieee754/ldbl-96/s_ffmal.c
index 5f158b43b0..92ab91455e 100644
--- ./sysdeps/ieee754/ldbl-96/s_ffmal.c
+++ ./sysdeps/ieee754/ldbl-96/s_ffmal.c
@@ -26,7 +26,7 @@ 
 float
 __ffmal (long double x, long double y, long double z)
 {
-  NARROW_FMA_ROUND_TO_ODD (x, y, z, float, union ieee854_long_double, l,
-			   mantissa1, false);
+  NARROW_FMA_ROUND_TO_ODD (x, y, z, float, __FLT_MIN__,
+			   union ieee854_long_double, l, mantissa1, false);
 }
 libm_alias_float_ldouble (fma)
diff --git ./sysdeps/ieee754/ldbl-96/s_fmull.c ./sysdeps/ieee754/ldbl-96/s_fmull.c
index cabe804070..103d819b24 100644
--- ./sysdeps/ieee754/ldbl-96/s_fmull.c
+++ ./sysdeps/ieee754/ldbl-96/s_fmull.c
@@ -25,7 +25,7 @@ 
 float
 __fmull (long double x, long double y)
 {
-  NARROW_MUL_ROUND_TO_ODD (x, y, float, union ieee854_long_double, l,
-			   mantissa1, false);
+  NARROW_MUL_ROUND_TO_ODD (x, y, float, __FLT_MIN__,
+			   union ieee854_long_double, l, mantissa1, false);
 }
 libm_alias_float_ldouble (mul)
diff --git ./sysdeps/ieee754/ldbl-96/s_fsubl.c ./sysdeps/ieee754/ldbl-96/s_fsubl.c
index 0d2de90d5a..5827d02ff2 100644
--- ./sysdeps/ieee754/ldbl-96/s_fsubl.c
+++ ./sysdeps/ieee754/ldbl-96/s_fsubl.c
@@ -25,7 +25,7 @@ 
 float
 __fsubl (long double x, long double y)
 {
-  NARROW_SUB_ROUND_TO_ODD (x, y, float, union ieee854_long_double, l,
-			   mantissa1);
+  NARROW_SUB_ROUND_TO_ODD (x, y, float, __FLT_MIN__,
+			   union ieee854_long_double, l, mantissa1);
 }
 libm_alias_float_ldouble (sub)
diff --git ./sysdeps/x86/fpu/s_ffma.c ./sysdeps/x86/fpu/s_ffma.c
index 329a9d7469..099f36971b 100644
--- ./sysdeps/x86/fpu/s_ffma.c
+++ ./sysdeps/x86/fpu/s_ffma.c
@@ -44,7 +44,7 @@ 
 float
 __ffma (double x, double y, double z)
 {
-  NARROW_FMA_ROUND_TO_ODD (x, y, z, float, union ieee754_double, , mantissa1,
-			   false);
+  NARROW_FMA_ROUND_TO_ODD (x, y, z, float, __FLT_MIN__, union ieee754_double,
+			   , mantissa1, false);
 }
 libm_alias_float_double (fma)