glibc/sysdeps/ieee754/ldbl-128/s_lrintl.c
Stefan Liebler c4d17461e0 S/390: Do not raise inexact exception in lrint/lround. [BZ #19486]
I get some math test-failures on s390 for float/double/ldouble for
various lrint/lround functions like:
lrint (0x1p64): Exception "Inexact" set
lrint (-0x1p64): Exception "Inexact" set
lround (0x1p64): Exception "Inexact" set
lround (-0x1p64): Exception "Inexact" set
...

GCC emits "convert to fixed" instructions for casting floating point
values to integer values. These instructions raise invalid and inexact
exceptions if the floating point value exceeds the integer type ranges.

This patch enables the various FIX_DBL_LONG_CONVERT_OVERFLOW macros in
order to avoid a cast from floating point to integer type and raise the
invalid exception with feraiseexcept.
The ldbl-128 rint/round functions are now using the same logic.

ChangeLog:

	[BZ #19486]
	* sysdeps/s390/fix-fp-int-convert-overflow.h: New File.
	* sysdeps/generic/fix-fp-int-convert-overflow.h
	(FIX_LDBL_LONG_CONVERT_OVERFLOW,
	FIX_LDBL_LLONG_CONVERT_OVERFLOW): New define.
	* sysdeps/arm/fix-fp-int-convert-overflow.h: Likewise.
	* sysdeps/mips/mips32/fpu/fix-fp-int-convert-overflow.h:
	Likewise.
	* sysdeps/ieee754/ldbl-128/s_lrintl.c (__lrintl):
	Avoid conversions to long int where inexact exceptions
	could be raised.
	* sysdeps/ieee754/ldbl-128/s_lroundl.c (__lroundl):
	Likewise.
	* sysdeps/ieee754/ldbl-128/s_llrintl.c (__llrintl):
	Avoid conversions to long long int where inexact exceptions
	could be raised.
	* sysdeps/ieee754/ldbl-128/s_llroundl.c (__llroundl):
	Likewise.
2016-01-18 12:48:06 +01:00

138 lines
3.8 KiB
C

/* Round argument to nearest integral value according to current rounding
direction.
Copyright (C) 1997-2016 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997 and
Jakub Jelinek <jj@ultra.linux.cz>, 1999.
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 <fenv.h>
#include <limits.h>
#include <math.h>
#include <math_private.h>
#include <fix-fp-int-convert-overflow.h>
static const long double two112[2] =
{
5.19229685853482762853049632922009600E+33L, /* 0x406F000000000000, 0 */
-5.19229685853482762853049632922009600E+33L /* 0xC06F000000000000, 0 */
};
long int
__lrintl (long double x)
{
int32_t j0;
u_int64_t i0,i1;
long double w;
long double t;
long int result;
int sx;
GET_LDOUBLE_WORDS64 (i0, i1, x);
j0 = ((i0 >> 48) & 0x7fff) - 0x3fff;
sx = i0 >> 63;
i0 &= 0x0000ffffffffffffLL;
i0 |= 0x0001000000000000LL;
if (j0 < (int32_t) (8 * sizeof (long int)) - 1)
{
if (j0 < 48)
{
#if defined FE_INVALID || defined FE_INEXACT
/* X < LONG_MAX + 1 implied by J0 < 31. */
if (sizeof (long int) == 4
&& x > (long double) LONG_MAX)
{
/* In the event of overflow we must raise the "invalid"
exception, but not "inexact". */
t = __nearbyintl (x);
feraiseexcept (t == LONG_MAX ? FE_INEXACT : FE_INVALID);
}
else
#endif
{
w = two112[sx] + x;
t = w - two112[sx];
}
GET_LDOUBLE_WORDS64 (i0, i1, t);
j0 = ((i0 >> 48) & 0x7fff) - 0x3fff;
i0 &= 0x0000ffffffffffffLL;
i0 |= 0x0001000000000000LL;
result = (j0 < 0 ? 0 : i0 >> (48 - j0));
}
else if (j0 >= 112)
result = ((long int) i0 << (j0 - 48)) | (i1 << (j0 - 112));
else
{
#if defined FE_INVALID || defined FE_INEXACT
/* X < LONG_MAX + 1 implied by J0 < 63. */
if (sizeof (long int) == 8
&& x > (long double) LONG_MAX)
{
/* In the event of overflow we must raise the "invalid"
exception, but not "inexact". */
t = __nearbyintl (x);
feraiseexcept (t == LONG_MAX ? FE_INEXACT : FE_INVALID);
}
else
#endif
{
w = two112[sx] + x;
t = w - two112[sx];
}
GET_LDOUBLE_WORDS64 (i0, i1, t);
j0 = ((i0 >> 48) & 0x7fff) - 0x3fff;
i0 &= 0x0000ffffffffffffLL;
i0 |= 0x0001000000000000LL;
if (j0 == 48)
result = (long int) i0;
else
result = ((long int) i0 << (j0 - 48)) | (i1 >> (112 - j0));
}
}
else
{
/* The number is too large. Unless it rounds to LONG_MIN,
FE_INVALID must be raised and the return value is
unspecified. */
#if defined FE_INVALID || defined FE_INEXACT
if (x < (long double) LONG_MIN
&& x > (long double) LONG_MIN - 1.0L)
{
/* If truncation produces LONG_MIN, the cast will not raise
the exception, but may raise "inexact". */
t = __nearbyintl (x);
feraiseexcept (t == LONG_MIN ? FE_INEXACT : FE_INVALID);
return LONG_MIN;
}
else if (FIX_LDBL_LONG_CONVERT_OVERFLOW && x != (long double) LONG_MIN)
{
feraiseexcept (FE_INVALID);
return sx == 0 ? LONG_MAX : LONG_MIN;
}
#endif
return (long int) x;
}
return sx ? -result : result;
}
weak_alias (__lrintl, lrintl)