diff options
Diffstat (limited to 'sysdeps/powerpc/powerpc32/fpu/s_llround.c')
-rw-r--r-- | sysdeps/powerpc/powerpc32/fpu/s_llround.c | 51 |
1 files changed, 49 insertions, 2 deletions
diff --git a/sysdeps/powerpc/powerpc32/fpu/s_llround.c b/sysdeps/powerpc/powerpc32/fpu/s_llround.c index 14d10e1e63..989d9b5763 100644 --- a/sysdeps/powerpc/powerpc32/fpu/s_llround.c +++ b/sysdeps/powerpc/powerpc32/fpu/s_llround.c @@ -18,10 +18,10 @@ #include <limits.h> #include <math.h> -#include <math_ldbl_opt.h> #include <math_private.h> #include <stdint.h> #include <libm-alias-double.h> +#include <math-barriers.h> /* Round to the nearest integer, with values exactly on a 0.5 boundary rounded away from zero, regardless of the current rounding mode. @@ -31,9 +31,53 @@ long long int __llround (double x) { +#ifdef _ARCH_PWR5X + x = round (x); + /* The barrier prevents compiler from optimizing it to llround when + compiled with -fno-math-errno */ + math_opt_barrier (x); + return x; +#else long long xr; if (HAVE_PPC_FCTIDZ) - xr = (long long) x; + { + /* IEEE 1003.1 lround function. IEEE specifies "round to the nearest + integer value, rounding halfway cases away from zero, regardless of + the current rounding mode." However PowerPC Architecture defines + "round to Nearest" as "Choose the best approximation. In case of a + tie, choose the one that is even (least significant bit o).". + So we can't use the PowerPC "round to Nearest" mode. Instead we set + "round toward Zero" mode and round by adding +-0.5 before rounding + to the integer value. + + It is necessary to detect when x is (+-)0x1.fffffffffffffp-2 + because adding +-0.5 in this case will cause an erroneous shift, + carry and round. We simply return 0 if 0.5 > x > -0.5. Likewise + if x is and odd number between +-(2^52 and 2^53-1) a shift and + carry will erroneously round if biased with +-0.5. Therefore if x + is greater/less than +-2^52 we don't need to bias the number with + +-0.5. */ + double ax = fabs (x); + + if (ax < 0.5) + return 0; + + if (ax < 0x1p+52) + { + /* Test whether an integer to avoid spurious "inexact". */ + double t = ax + 0x1p+52; + t = t - 0x1p+52; + if (ax != t) + { + ax = ax + 0.5; + if (x < 0.0) + ax = -fabs (ax); + x = ax; + } + } + + return x; + } else { /* Avoid incorrect exceptions from libgcc conversions (as of GCC @@ -80,5 +124,8 @@ __llround (double x) xr -= (long long) ((unsigned long long) xr - 1) < 0; } return xr; +#endif } +#ifndef __llround libm_alias_double (__llround, llround) +#endif |