(root)/
glibc-2.38/
sysdeps/
ieee754/
ldbl-128ibm/
s_lrintl.c
       1  /* Round to long int long double floating-point values.
       2     IBM extended format long double version.
       3     Copyright (C) 2006-2023 Free Software Foundation, Inc.
       4     This file is part of the GNU C Library.
       5  
       6     The GNU C Library is free software; you can redistribute it and/or
       7     modify it under the terms of the GNU Lesser General Public
       8     License as published by the Free Software Foundation; either
       9     version 2.1 of the License, or (at your option) any later version.
      10  
      11     The GNU C Library is distributed in the hope that it will be useful,
      12     but WITHOUT ANY WARRANTY; without even the implied warranty of
      13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      14     Lesser General Public License for more details.
      15  
      16     You should have received a copy of the GNU Lesser General Public
      17     License along with the GNU C Library; if not, see
      18     <https://www.gnu.org/licenses/>.  */
      19  
      20  #include <math.h>
      21  #include <fenv.h>
      22  #include <math_private.h>
      23  #include <math_ldbl_opt.h>
      24  #include <float.h>
      25  #include <ieee754.h>
      26  
      27  
      28  long
      29  __lrintl (long double x)
      30  {
      31    double xh, xl;
      32    long res, hi, lo;
      33    int save_round;
      34  
      35    ldbl_unpack (x, &xh, &xl);
      36  
      37    /* Limit the range of values handled by the conversion to long.
      38       We do this because we aren't sure whether that conversion properly
      39       raises FE_INVALID.  */
      40    if (
      41  #if __LONG_MAX__ == 2147483647
      42        __builtin_expect
      43        ((__builtin_fabs (xh) <= (double) __LONG_MAX__ + 2), 1)
      44  #else
      45        __builtin_expect
      46        ((__builtin_fabs (xh) <= -(double) (-__LONG_MAX__ - 1)), 1)
      47  #endif
      48  #if !defined (FE_INVALID)
      49        || 1
      50  #endif
      51      )
      52      {
      53        save_round = fegetround ();
      54  
      55  #if __LONG_MAX__ == 2147483647
      56        long long llhi = (long long) xh;
      57        if (llhi != (long) llhi)
      58  	hi = llhi < 0 ? -__LONG_MAX__ - 1 : __LONG_MAX__;
      59        else
      60  	hi = llhi;
      61        xh -= hi;
      62  #else
      63        if (__glibc_unlikely ((xh == -(double) (-__LONG_MAX__ - 1))))
      64  	{
      65  	  /* When XH is 9223372036854775808.0, converting to long long will
      66  	     overflow, resulting in an invalid operation.  However, XL might
      67  	     be negative and of sufficient magnitude that the overall long
      68  	     double is in fact in range.  Avoid raising an exception.  In any
      69  	     case we need to convert this value specially, because
      70  	     the converted value is not exactly represented as a double
      71  	     thus subtracting HI from XH suffers rounding error.  */
      72  	  hi = __LONG_MAX__;
      73  	  xh = 1.0;
      74  	}
      75        else
      76  	{
      77  	  hi = (long) xh;
      78  	  xh -= hi;
      79  	}
      80  #endif
      81        ldbl_canonicalize (&xh, &xl);
      82  
      83        lo = (long) xh;
      84  
      85        /* Peg at max/min values, assuming that the above conversions do so.
      86           Strictly speaking, we can return anything for values that overflow,
      87           but this is more useful.  */
      88        res = (long int) ((unsigned long int) hi + (unsigned long int) lo);
      89  
      90        /* This is just sign(hi) == sign(lo) && sign(res) != sign(hi).  */
      91        if (__glibc_unlikely (((~(hi ^ lo) & (res ^ hi)) < 0)))
      92  	goto overflow;
      93  
      94        xh -= lo;
      95        ldbl_canonicalize (&xh, &xl);
      96  
      97        hi = res;
      98        switch (save_round)
      99  	{
     100  	case FE_TONEAREST:
     101  	  if (fabs (xh) < 0.5
     102  	      || (fabs (xh) == 0.5
     103  		  && ((xh > 0.0 && xl < 0.0)
     104  		      || (xh < 0.0 && xl > 0.0)
     105  		      || (xl == 0.0 && (res & 1) == 0))))
     106  	    return res;
     107  
     108  	  if (xh < 0.0)
     109  	    res -= 1UL;
     110  	  else
     111  	    res += 1UL;
     112  	  break;
     113  
     114  	case FE_TOWARDZERO:
     115  	  if (res > 0 && (xh < 0.0 || (xh == 0.0 && xl < 0.0)))
     116  	    res -= 1UL;
     117  	  else if (res < 0 && (xh > 0.0 || (xh == 0.0 && xl > 0.0)))
     118  	    res += 1UL;
     119  	  return res;
     120  	  break;
     121  
     122  	case FE_UPWARD:
     123  	  if (xh > 0.0 || (xh == 0.0 && xl > 0.0))
     124  	    res += 1UL;
     125  	  break;
     126  
     127  	case FE_DOWNWARD:
     128  	  if (xh < 0.0 || (xh == 0.0 && xl < 0.0))
     129  	    res -= 1UL;
     130  	  break;
     131  	}
     132  
     133        if (__glibc_unlikely (((~(hi ^ (res - hi)) & (res ^ hi)) < 0)))
     134  	goto overflow;
     135  
     136        return res;
     137      }
     138    else
     139      {
     140        if (xh > 0.0)
     141  	hi = __LONG_MAX__;
     142        else if (xh < 0.0)
     143  	hi = -__LONG_MAX__ - 1;
     144        else
     145  	/* Nan */
     146  	hi = 0;
     147      }
     148  
     149  overflow:
     150  #ifdef FE_INVALID
     151    feraiseexcept (FE_INVALID);
     152  #endif
     153    return hi;
     154  }
     155  
     156  long_double_symbol (libm, __lrintl, lrintl);