1 /* Round long double value to long int.
2    Copyright (C) 1997-2022 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <https://www.gnu.org/licenses/>.  */
18 
19 #include <fenv.h>
20 #include <limits.h>
21 #include <math.h>
22 
23 #include <math_private.h>
24 #include <libm-alias-ldouble.h>
25 
26 
27 long int
__lroundl(long double x)28 __lroundl (long double x)
29 {
30   int32_t j0;
31   uint32_t se, i1, i0;
32   long int result;
33   int sign;
34 
35   GET_LDOUBLE_WORDS (se, i0, i1, x);
36   j0 = (se & 0x7fff) - 0x3fff;
37   sign = (se & 0x8000) != 0 ? -1 : 1;
38 
39   if (j0 < 31)
40     {
41       if (j0 < 0)
42 	return j0 < -1 ? 0 : sign;
43       else
44 	{
45 	  uint32_t j = i0 + (0x40000000 >> j0);
46 	  if (j < i0)
47 	    {
48 	      j >>= 1;
49 	      j |= 0x80000000;
50 	      ++j0;
51 	    }
52 
53 	  result = j >> (31 - j0);
54 #ifdef FE_INVALID
55 	  if (sizeof (long int) == 4
56 	      && sign == 1
57 	      && result == LONG_MIN)
58 	    /* Rounding brought the value out of range.  */
59 	    feraiseexcept (FE_INVALID);
60 #endif
61 	}
62     }
63   else if (j0 < (int32_t) (8 * sizeof (long int)) - 1)
64     {
65       if (j0 >= 63)
66 	result = ((long int) i0 << (j0 - 31)) | (i1 << (j0 - 63));
67       else
68 	{
69 	  uint32_t j = i1 + (0x80000000 >> (j0 - 31));
70 	  unsigned long int ures = i0;
71 
72 	  if (j < i1)
73 	    ++ures;
74 
75 	  if (j0 == 31)
76 	    result = ures;
77 	  else
78 	    {
79 	      result = (ures << (j0 - 31)) | (j >> (63 - j0));
80 #ifdef FE_INVALID
81 	      if (sizeof (long int) == 8
82 		  && sign == 1
83 		  && result == LONG_MIN)
84 		/* Rounding brought the value out of range.  */
85 		feraiseexcept (FE_INVALID);
86 #endif
87 	    }
88 	}
89     }
90   else
91     {
92       /* The number is too large.  Unless it rounds to LONG_MIN,
93 	 FE_INVALID must be raised and the return value is
94 	 unspecified.  */
95 #ifdef FE_INVALID
96       if (sizeof (long int) == 4
97 	  && x <= (long double) LONG_MIN - 0.5L)
98 	{
99 	  /* If truncation produces LONG_MIN, the cast will not raise
100 	     the exception, but may raise "inexact".  */
101 	  feraiseexcept (FE_INVALID);
102 	  return LONG_MIN;
103 	}
104 #endif
105       return (long int) x;
106     }
107 
108   return sign * result;
109 }
110 
111 libm_alias_ldouble (__lround, lround)
112