@@ -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; \
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)