1 /* Round double value to long int.
2    Copyright (C) 1997-2021 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-double.h>
25 #include <fix-fp-int-convert-overflow.h>
26 
27 /* For LP64, lround is an alias for llround.  */
28 #ifndef _LP64
29 
30 long int
__lround(double x)31 __lround (double x)
32 {
33   int32_t j0;
34   int64_t i0;
35   long int result;
36   int sign;
37 
38   EXTRACT_WORDS64 (i0, x);
39   j0 = ((i0 >> 52) & 0x7ff) - 0x3ff;
40   sign = i0 < 0 ? -1 : 1;
41   i0 &= UINT64_C(0xfffffffffffff);
42   i0 |= UINT64_C(0x10000000000000);
43 
44   if (j0 < (int32_t) (8 * sizeof (long int)) - 1)
45     {
46       if (j0 < 0)
47 	return j0 < -1 ? 0 : sign;
48       else if (j0 >= 52)
49 	result = i0 << (j0 - 52);
50       else
51 	{
52 	  i0 += UINT64_C(0x8000000000000) >> j0;
53 
54 	  result = i0 >> (52 - j0);
55 #ifdef FE_INVALID
56 	  if (sizeof (long int) == 4
57 	      && sign == 1
58 	      && result == LONG_MIN)
59 	    /* Rounding brought the value out of range.  */
60 	    feraiseexcept (FE_INVALID);
61 #endif
62 	}
63     }
64   else
65     {
66       /* The number is too large.  Unless it rounds to LONG_MIN,
67 	 FE_INVALID must be raised and the return value is
68 	 unspecified.  */
69 #ifdef FE_INVALID
70       if (FIX_DBL_LONG_CONVERT_OVERFLOW
71 	  && !(sign == -1
72 	       && (sizeof (long int) == 4
73 		   ? x > (double) LONG_MIN - 0.5
74 		   : x >= (double) LONG_MIN)))
75 	{
76 	  feraiseexcept (FE_INVALID);
77 	  return sign == 1 ? LONG_MAX : LONG_MIN;
78 	}
79       else if (!FIX_DBL_LONG_CONVERT_OVERFLOW
80 	  && sizeof (long int) == 4
81 	  && x <= (double) LONG_MIN - 0.5)
82 	{
83 	  /* If truncation produces LONG_MIN, the cast will not raise
84 	     the exception, but may raise "inexact".  */
85 	  feraiseexcept (FE_INVALID);
86 	  return LONG_MIN;
87 	}
88 #endif
89       return (long int) x;
90     }
91 
92   return sign * result;
93 }
94 
95 libm_alias_double (__lround, lround)
96 
97 #endif
98