1 /* Round to long int long double floating-point values.
2    IBM extended format long double version.
3    Copyright (C) 2006-2022 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
__lrintl(long double x)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);
157