diff options
author | Roland McGrath <roland@gnu.org> | 2006-03-16 23:17:32 +0000 |
---|---|---|
committer | Roland McGrath <roland@gnu.org> | 2006-03-16 23:17:32 +0000 |
commit | 830fce0415390c4a7326fe91ad678fd987a1c53f (patch) | |
tree | 839393ec850233231d49b8cc3cfd02aece5aa675 /sysdeps/ieee754/ldbl-128ibm/s_llroundl.c | |
parent | 27c0e0d8630de8efbbd633ffb425c85515a5e89d (diff) | |
download | glibc-830fce0415390c4a7326fe91ad678fd987a1c53f.tar.gz |
[BZ #2466]
* math/gen-libm-test.pl (parse_args): Take function name for pretty
output as an argument.
(generate_testfile): Pass it the name given in the START macro.
[BZ #2466]
* math/libm-test.inc (llrint_test, llround_test): Fix last change to
protect large-precision cases with [LDBL_MANT_DIG > 100].
(llrint_test_tonearest, llrint_test_towardzero): Likewise.
(llrint_test_downward, llrint_test_upward): Likewise.
2006-03-15 Steven Munroe <sjmunroe@us.ibm.com>
Alan Modra <amodra@bigpond.net.au>
[BZ #2466]
* math/libm-test.inc (llrint_test, llround_test) [TEST_LDOUBLE]:
Add new test values.
(llrint_test_tonearest, llrint_test_towardzero, llrint_test_downward,
llrint_test_upward): New functions.
(main): Call them.
* sysdeps/ieee754/ldbl-128ibm/s_llrintl.c (__llrintl): Handle
rounding that spans doubles in IBM long double format.
* sysdeps/ieee754/ldbl-128ibm/s_llroundl.c (__llroundl): Likewise.
* sysdeps/powerpc/powerpc64/fpu/s_llrintl.S: Removed.
* sysdeps/powerpc/powerpc64/fpu/s_llroundl.S: Removed.
* sysdeps/powerpc/powerpc64/fpu/s_lrintl.S: Removed.
* sysdeps/powerpc/powerpc64/fpu/s_lroundl.S: Removed.
2006-03-16 Roland McGrath <roland@redhat.com>
Diffstat (limited to 'sysdeps/ieee754/ldbl-128ibm/s_llroundl.c')
-rw-r--r-- | sysdeps/ieee754/ldbl-128ibm/s_llroundl.c | 158 |
1 files changed, 80 insertions, 78 deletions
diff --git a/sysdeps/ieee754/ldbl-128ibm/s_llroundl.c b/sysdeps/ieee754/ldbl-128ibm/s_llroundl.c index 567e7ecc07..103529d5a1 100644 --- a/sysdeps/ieee754/ldbl-128ibm/s_llroundl.c +++ b/sysdeps/ieee754/ldbl-128ibm/s_llroundl.c @@ -22,12 +22,11 @@ when it's coded in C. */ #include <math.h> -#include <fenv.h> +#include <fenv_libc.h> #include <math_ldbl_opt.h> #include <float.h> #include <ieee754.h> - #ifdef __STDC__ long long __llroundl (long double x) @@ -37,92 +36,95 @@ __llroundl (x) long double x; #endif { - static const double TWO52 = 4503599627370496.0; - static const double HALF = 0.5; - int mode = fegetround(); - union ibm_extended_long_double u; - long long result = __LONG_LONG_MAX__; - - u.d = x; - - if (fabs (u.dd[0]) < TWO52) - { - fesetround(FE_TOWARDZERO); - if (u.dd[0] > 0.0) + double xh, xl; + long long res, hi, lo; + + ldbl_unpack (x, &xh, &xl); + + /* Limit the range of values handled by the conversion to long long. + We do this because we aren't sure whether that conversion properly + raises FE_INVALID. */ + if (__builtin_expect + ((__builtin_fabs (xh) <= -(double) (-__LONG_LONG_MAX__ - 1)), 1) +#if !defined (FE_INVALID) + || 1 +#endif + ) + { + if (__builtin_expect ((xh == -(double) (-__LONG_LONG_MAX__ - 1)), 0)) { - u.dd[0] += HALF; - u.dd[0] += TWO52; - u.dd[0] -= TWO52; + /* When XH is 9223372036854775808.0, converting to long long will + overflow, resulting in an invalid operation. However, XL might + be negative and of sufficient magnitude that the overall long + double is in fact in range. Avoid raising an exception. In any + case we need to convert this value specially, because + the converted value is not exactly represented as a double + thus subtracting HI from XH suffers rounding error. */ + hi = __LONG_LONG_MAX__; + xh = 1.0; } - else if (u.dd[0] < 0.0) + else { - u.dd[0] = TWO52 - (u.dd[0] - HALF); - u.dd[0] = -(u.dd[0] - TWO52); + hi = (long long) xh; + xh -= hi; } - fesetround(mode); - result = u.dd[0]; - } - else if (fabs (u.dd[1]) < TWO52 && u.dd[1] != 0.0) - { - double high, low; - long long lowint; - /* In this case we have to round the low double and handle any - adjustment to the high double that may be caused by rounding - (up). This is complicated by the fact that the high double - may already be rounded and the low double may have the - opposite sign to compensate. */ - if (u.dd[0] > 0.0) + ldbl_canonicalize (&xh, &xl); + + lo = (long long) xh; + + /* Peg at max/min values, assuming that the above conversions do so. + Strictly speaking, we can return anything for values that overflow, + but this is more useful. */ + res = hi + lo; + + /* This is just sign(hi) == sign(lo) && sign(res) != sign(hi). */ + if (__builtin_expect (((~(hi ^ lo) & (res ^ hi)) < 0), 0)) + goto overflow; + + xh -= lo; + ldbl_canonicalize (&xh, &xl); + + hi = res; + if (xh > 0.5) + { + res += 1; + } + else if (xh == 0.5) { - if (u.dd[1] > 0.0) - { - /* If the high/low doubles are the same sign then simply - round the low double. */ - high = u.dd[0]; - low = u.dd[1]; - } - else if (u.dd[1] < 0.0) - { - /* Else the high double is pre rounded and we need to - adjust for that. */ - high = nextafter (u.dd[0], 0.0); - low = u.dd[1] + (u.dd[0] - high); - } - fesetround(FE_TOWARDZERO); - low += HALF; - low += TWO52; - low -= TWO52; + if (xl > 0.0 || (xl == 0.0 && res >= 0)) + res += 1; } - else if (u.dd[0] < 0.0) + else if (-xh > 0.5) { - if (u.dd[1] < 0.0) - { - /* If the high/low doubles are the same sign then simply - round the low double. */ - high = u.dd[0]; - low = u.dd[1]; - } - else if (u.dd[1] > 0.0) - { - /* Else the high double is pre rounded and we need to - adjust for that. */ - high = nextafter (u.dd[0], 0.0); - low = u.dd[1] + (u.dd[0] - high); - } - fesetround(FE_TOWARDZERO); - low -= HALF; - low = TWO52 - low; - low = -(low - TWO52); + res -= 1; } - fesetround(mode); - lowint = low; - result = high; - result += lowint; + else if (-xh == 0.5) + { + if (xl < 0.0 || (xl == 0.0 && res <= 0)) + res -= 1; + } + + if (__builtin_expect (((~(hi ^ (res - hi)) & (res ^ hi)) < 0), 0)) + goto overflow; + + return res; } else - { - result = u.dd[0]; - } - return result; + { + if (xh > 0.0) + hi = __LONG_LONG_MAX__; + else if (xh < 0.0) + hi = -__LONG_LONG_MAX__ - 1; + else + /* Nan */ + hi = 0; + } + +overflow: +#ifdef FE_INVALID + feraiseexcept (FE_INVALID); +#endif + return hi; } long_double_symbol (libm, __llroundl, llroundl); |