Back to index

glibc  2.9
s_rintl.c
Go to the documentation of this file.
00001 /* Round to int long double floating-point values.
00002    IBM extended format long double version.
00003    Copyright (C) 2006 Free Software Foundation, Inc.
00004    This file is part of the GNU C Library.
00005 
00006    The GNU C Library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Lesser General Public
00008    License as published by the Free Software Foundation; either
00009    version 2.1 of the License, or (at your option) any later version.
00010 
00011    The GNU C Library is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014    Lesser General Public License for more details.
00015 
00016    You should have received a copy of the GNU Lesser General Public
00017    License along with the GNU C Library; if not, write to the Free
00018    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00019    02111-1307 USA.  */
00020 
00021 /* This has been coded in assembler because GCC makes such a mess of it
00022    when it's coded in C.  */
00023 
00024 #include <math.h>
00025 #include <fenv_libc.h>
00026 #include <math_ldbl_opt.h>
00027 #include <float.h>
00028 #include <ieee754.h>
00029 
00030 
00031 #ifdef __STDC__
00032 long double
00033 __rintl (long double x)
00034 #else
00035 long double
00036 __rintl (x)
00037      long double x;
00038 #endif
00039 {
00040   double xh, xl, hi, lo;
00041 
00042   ldbl_unpack (x, &xh, &xl);
00043 
00044   /* Return Inf, Nan, +/-0 unchanged.  */
00045   if (__builtin_expect (xh != 0.0
00046                      && __builtin_isless (__builtin_fabs (xh),
00047                                         __builtin_inf ()), 1))
00048     {
00049       double orig_xh;
00050       int save_round = fegetround ();
00051 
00052       /* Long double arithmetic, including the canonicalisation below,
00053         only works in round-to-nearest mode.  */
00054       fesetround (FE_TONEAREST);
00055 
00056       /* Convert the high double to integer.  */
00057       orig_xh = xh;
00058       hi = ldbl_nearbyint (xh);
00059 
00060       /* Subtract integral high part from the value.  If the low double
00061         happens to be exactly 0.5 or -0.5, you might think that this
00062         subtraction could result in an incorrect conversion.  For
00063         instance, subtracting an odd number would cause this function
00064         to round in the wrong direction.  However, if we have a
00065         canonical long double with the low double 0.5 or -0.5, then the
00066         high double must be even.  */
00067       xh -= hi;
00068       ldbl_canonicalize (&xh, &xl);
00069 
00070       /* Now convert the low double, adjusted for any remainder from the
00071         high double.  */
00072       lo = ldbl_nearbyint (xh);
00073 
00074       xh -= lo;
00075       ldbl_canonicalize (&xh, &xl);
00076 
00077       switch (save_round)
00078        {
00079        case FE_TONEAREST:
00080          if (xl > 0.0 && xh == 0.5)
00081            lo += 1.0;
00082          else if (xl < 0.0 && -xh == 0.5)
00083            lo -= 1.0;
00084          break;
00085 
00086        case FE_TOWARDZERO:
00087          if (orig_xh < 0.0)
00088            goto do_up;
00089          /* Fall thru */
00090 
00091        case FE_DOWNWARD:
00092          if (xh < 0.0 || (xh == 0.0 && xl < 0.0))
00093            lo -= 1.0;
00094          break;
00095 
00096        case FE_UPWARD:
00097        do_up:
00098          if (xh > 0.0 || (xh == 0.0 && xl > 0.0))
00099            lo += 1.0;
00100          break;
00101        }
00102 
00103       /* Ensure the final value is canonical.  In certain cases,
00104          rounding causes hi,lo calculated so far to be non-canonical.  */
00105       xh = hi;
00106       xl = lo;
00107       ldbl_canonicalize (&xh, &xl);
00108 
00109       /* Ensure we return -0 rather than +0 when appropriate.  */
00110       if (orig_xh < 0.0)
00111        xh = -__builtin_fabs (xh);
00112 
00113       fesetround (save_round);
00114     }
00115 
00116   return ldbl_pack (xh, xl);
00117 }
00118 
00119 long_double_symbol (libm, __rintl, rintl);