Back to index

glibc  2.9
s_llrintl.c
Go to the documentation of this file.
00001 /* Round to long long 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 #include <math.h>
00022 #include <fenv_libc.h>
00023 #include <math_ldbl_opt.h>
00024 #include <float.h>
00025 #include <ieee754.h>
00026 
00027 
00028 #ifdef __STDC__
00029 long long
00030 __llrintl (long double x)
00031 #else
00032 long long
00033 __llrintl (x)
00034      long double x;
00035 #endif
00036 {
00037   double xh, xl;
00038   long long res, hi, lo;
00039   int save_round;
00040 
00041   ldbl_unpack (x, &xh, &xl);
00042 
00043   /* Limit the range of values handled by the conversion to long long.
00044      We do this because we aren't sure whether that conversion properly
00045      raises FE_INVALID.  */
00046   if (__builtin_expect
00047       ((__builtin_fabs (xh) <= -(double) (-__LONG_LONG_MAX__ - 1)), 1)
00048 #if !defined (FE_INVALID)
00049       || 1
00050 #endif
00051     )
00052     {
00053       save_round = fegetround ();
00054 
00055       if (__builtin_expect ((xh == -(double) (-__LONG_LONG_MAX__ - 1)), 0))
00056        {
00057          /* When XH is 9223372036854775808.0, converting to long long will
00058             overflow, resulting in an invalid operation.  However, XL might
00059             be negative and of sufficient magnitude that the overall long
00060             double is in fact in range.  Avoid raising an exception.  In any
00061             case we need to convert this value specially, because
00062             the converted value is not exactly represented as a double
00063             thus subtracting HI from XH suffers rounding error.  */
00064          hi = __LONG_LONG_MAX__;
00065          xh = 1.0;
00066        }
00067       else
00068        {
00069          hi = (long long) xh;
00070          xh -= hi;
00071        }
00072       ldbl_canonicalize (&xh, &xl);
00073 
00074       lo = (long long) xh;
00075 
00076       /* Peg at max/min values, assuming that the above conversions do so.
00077          Strictly speaking, we can return anything for values that overflow,
00078          but this is more useful.  */
00079       res = hi + lo;
00080 
00081       /* This is just sign(hi) == sign(lo) && sign(res) != sign(hi).  */
00082       if (__builtin_expect (((~(hi ^ lo) & (res ^ hi)) < 0), 0))
00083        goto overflow;
00084 
00085       xh -= lo;
00086       ldbl_canonicalize (&xh, &xl);
00087 
00088       hi = res;
00089       switch (save_round)
00090        {
00091        case FE_TONEAREST:
00092          if (fabs (xh) < 0.5
00093              || (fabs (xh) == 0.5
00094                 && ((xh > 0.0 && xl < 0.0)
00095                     || (xh < 0.0 && xl > 0.0)
00096                     || (xl == 0.0 && (res & 1) == 0))))
00097            return res;
00098 
00099          if (xh < 0.0)
00100            res -= 1;
00101          else
00102            res += 1;
00103          break;
00104 
00105        case FE_TOWARDZERO:
00106          if (res > 0 && (xh < 0.0 || (xh == 0.0 && xl < 0.0)))
00107            res -= 1;
00108          else if (res < 0 && (xh > 0.0 || (xh == 0.0 && xl > 0.0)))
00109            res += 1;
00110          return res;
00111          break;
00112 
00113        case FE_UPWARD:
00114          if (xh > 0.0 || (xh == 0.0 && xl > 0.0))
00115            res += 1;
00116          break;
00117 
00118        case FE_DOWNWARD:
00119          if (xh < 0.0 || (xh == 0.0 && xl < 0.0))
00120            res -= 1;
00121          break;
00122        }
00123 
00124       if (__builtin_expect (((~(hi ^ (res - hi)) & (res ^ hi)) < 0), 0))
00125        goto overflow;
00126 
00127       return res;
00128     }
00129   else
00130     {
00131       if (xh > 0.0)
00132        hi = __LONG_LONG_MAX__;
00133       else if (xh < 0.0)
00134        hi = -__LONG_LONG_MAX__ - 1;
00135       else
00136        /* Nan */
00137        hi = 0;
00138     }
00139 
00140 overflow:
00141 #ifdef FE_INVALID
00142   feraiseexcept (FE_INVALID);
00143 #endif
00144   return hi;
00145 }
00146 
00147 long_double_symbol (libm, __llrintl, llrintl);