mirror of
git://sourceware.org/git/glibc.git
synced 2024-11-27 03:41:23 +08:00
d0d286d32d
The dbl-64, ldbl-96 and ldbl-128 implementations of lrint and llrint fail to produce "invalid" exceptions in cases where the rounded result overflows the target type, but truncating the floating-point argument to the next integer towards zero does not overflow it (so in particular casts do not produce such exceptions). (This issue cannot arise for float, or for double with 64-bit target type, or for ldbl-96 with 64-bit target type and negative arguments, because of insufficient precision in the floating-point type for arguments with the relevant property to exist. It also obviously cannot arise in FE_TOWARDZERO mode.) This patch fixes these problems by inserting checks for the special cases that can occur in each implementation, and explicitly raising FE_INVALID (and avoiding the cast if it might raise spurious FE_INEXACT, while raising FE_INEXACT explicitly in the cases where it is needed; unlike lround and llround, FE_INEXACT is required, not optional, for these functions for a within-range inexact result). The fixes are conditional on FE_INVALID or FE_INEXACT being defined. If any future architecture supports one but not both of those exceptions, the code will fail to compile and need fixing to handle that case (this seemed better than conditioning on both macros being defined, resulting in code that would compile but quietly miss exceptions on such a system). Tested for x86_64, x86 and mips64. Tested the ldbl-96 changes (only relevant for ia64, it appears) on x86_64 by removing the x86_64 versions of lrintl / llrintl. [BZ #19094] * sysdeps/ieee754/dbl-64/s_lrint.c: Include <fenv.h> and <limits.h>. (__lrint) [FE_INVALID || FE_INEXACT]: Force FE_INVALID exception when result overflows but exception would not result from cast. * sysdeps/ieee754/ldbl-128/s_llrintl.c: Include <fenv.h> and <limits.h>. (__llrintl) [FE_INVALID || FE_INEXACT]: Force FE_INVALID exception when result overflows but exception would not result from cast. * sysdeps/ieee754/ldbl-128/s_lrintl.c: Include <fenv.h> and <limits.h>. (__lrintl) [FE_INVALID || FE_INEXACT]: Force FE_INVALID exception when result overflows but exception would not result from cast. * sysdeps/ieee754/ldbl-96/s_llrintl.c: Include <fenv.h> and <limits.h>. (__llrintl) [FE_INVALID || FE_INEXACT]: Force FE_INVALID exception when result overflows but exception would not result from cast. * sysdeps/ieee754/ldbl-96/s_lrintl.c: Include <fenv.h> and <limits.h>. (__lrintl) [FE_INVALID || FE_INEXACT]: Force FE_INVALID exception when result overflows but exception would not result from cast. * math/libm-test.inc (lrint_test_data): Add more tests. (llrint_test_data): Likewise.
131 lines
3.6 KiB
C
131 lines
3.6 KiB
C
/* Round argument to nearest integral value according to current rounding
|
|
direction.
|
|
Copyright (C) 1997-2015 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>
|
|
|
|
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;
|
|
}
|
|
#endif
|
|
return (long int) x;
|
|
}
|
|
|
|
return sx ? -result : result;
|
|
}
|
|
|
|
weak_alias (__lrintl, lrintl)
|