Back to index

glibc  2.9
s_nearbyintl.c
Go to the documentation of this file.
00001 /* Round to int long double floating-point values without raising inexact.
00002    IBM extended format long double version.
00003    Copyright (C) 2006, 2008 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.h>
00026 #include <math_ldbl_opt.h>
00027 #include <float.h>
00028 #include <ieee754.h>
00029 
00030 
00031 #ifdef __STDC__
00032 long double
00033 __nearbyintl (long double x)
00034 #else
00035 long double
00036 __nearbyintl (x)
00037      long double x;
00038 #endif
00039 {
00040   fenv_t env;
00041   static const long double TWO52 = 4503599627370496.0L;
00042   union ibm_extended_long_double u;
00043   u.d = x;
00044 
00045   if (fabs (u.dd[0]) < TWO52)
00046     {
00047       double high = u.dd[0];
00048       feholdexcept (&env);
00049       if (high > 0.0)
00050        {
00051          high += TWO52;
00052          high -= TWO52;
00053           if (high == -0.0) high = 0.0;
00054        }
00055       else if (high < 0.0)
00056        {
00057          high -= TWO52;
00058          high += TWO52;
00059           if (high == 0.0) high = -0.0;
00060        }
00061       u.dd[0] = high;
00062       u.dd[1] = 0.0;
00063       fesetenv (&env);
00064     }
00065   else if (fabs (u.dd[1]) < TWO52 && u.dd[1] != 0.0)
00066     {
00067       double high, low, tau;
00068       /* In this case we have to round the low double and handle any
00069          adjustment to the high double that may be caused by rounding
00070          (up).  This is complicated by the fact that the high double
00071          may already be rounded and the low double may have the
00072          opposite sign to compensate.  */
00073       feholdexcept (&env);
00074       if (u.dd[0] > 0.0)
00075        {
00076          if (u.dd[1] > 0.0)
00077            {
00078              /* If the high/low doubles are the same sign then simply
00079                 round the low double.  */
00080              high = u.dd[0];
00081              low = u.dd[1];
00082            }
00083          else if (u.dd[1] < 0.0)
00084            {
00085              /* Else the high double is pre rounded and we need to
00086                 adjust for that.  */
00087 
00088              tau = __nextafter (u.dd[0], 0.0);
00089              tau = (u.dd[0] - tau) * 2.0;
00090              high = u.dd[0] - tau;
00091              low = u.dd[1] + tau;
00092            }
00093          low += TWO52;
00094          low -= TWO52;
00095        }
00096       else if (u.dd[0] < 0.0)
00097        {
00098          if (u.dd[1] < 0.0)
00099            {
00100              /* If the high/low doubles are the same sign then simply
00101                 round the low double.  */
00102              high = u.dd[0];
00103              low = u.dd[1];
00104            }
00105          else if (u.dd[1] > 0.0)
00106            {
00107              /* Else the high double is pre rounded and we need to
00108                 adjust for that.  */
00109              tau = __nextafter (u.dd[0], 0.0);
00110              tau = (u.dd[0] - tau) * 2.0;
00111              high = u.dd[0] - tau;
00112              low = u.dd[1] + tau;
00113            }
00114          low = TWO52 - low;
00115          low = -(low - TWO52);
00116        }
00117       u.dd[0] = high + low;
00118       u.dd[1] = high - u.dd[0] + low;
00119       fesetenv (&env);
00120     }
00121 
00122   return u.d;
00123 }
00124 
00125 long_double_symbol (libm, __nearbyintl, nearbyintl);