1 /* Round to integer type.  ldbl-128ibm version.
2    Copyright (C) 2016-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 <errno.h>
20 #include <fenv.h>
21 #include <math.h>
22 #include <math_private.h>
23 #include <stdbool.h>
24 #include <stdint.h>
25 
26 #define BIAS 0x3ff
27 #define MANT_DIG 53
28 
29 #if UNSIGNED
30 # define RET_TYPE uintmax_t
31 #else
32 # define RET_TYPE intmax_t
33 #endif
34 
35 #include <fromfp.h>
36 
37 RET_TYPE
FUNC(long double x,int round,unsigned int width)38 FUNC (long double x, int round, unsigned int width)
39 {
40   double hi, lo;
41   if (width > INTMAX_WIDTH)
42     width = INTMAX_WIDTH;
43   uint64_t hx, lx;
44   ldbl_unpack (x, &hi, &lo);
45   EXTRACT_WORDS64 (hx, hi);
46   EXTRACT_WORDS64 (lx, lo);
47   bool negative = (hx & 0x8000000000000000ULL) != 0;
48   bool lo_negative = (lx & 0x8000000000000000ULL) != 0;
49   if (width == 0)
50     return fromfp_domain_error (negative, width);
51   hx &= 0x7fffffffffffffffULL;
52   lx &= 0x7fffffffffffffffULL;
53   if ((hx | lx) == 0)
54     return 0;
55   int hi_exponent = hx >> (MANT_DIG - 1);
56   hi_exponent -= BIAS;
57   int exponent = hi_exponent;
58   hx &= ((1ULL << (MANT_DIG - 1)) - 1);
59   if (hx == 0 && lx != 0 && lo_negative != negative)
60     exponent--;
61   int max_exponent = fromfp_max_exponent (negative, width);
62   if (exponent > max_exponent)
63     return fromfp_domain_error (negative, width);
64   int lo_exponent = lx >> (MANT_DIG - 1);
65   lo_exponent -= BIAS;
66 
67   /* Convert the high part to integer.  */
68   hx |= 1ULL << (MANT_DIG - 1);
69   uintmax_t uret;
70   bool half_bit, more_bits;
71   if (hi_exponent >= MANT_DIG - 1)
72     {
73       uret = hx;
74       uret <<= hi_exponent - (MANT_DIG - 1);
75       half_bit = false;
76       more_bits = false;
77     }
78   else if (hi_exponent >= -1)
79     {
80       uint64_t h = 1ULL << (MANT_DIG - 2 - hi_exponent);
81       half_bit = (hx & h) != 0;
82       more_bits = (hx & (h - 1)) != 0;
83       uret = hx >> (MANT_DIG - 1 - hi_exponent);
84     }
85   else
86     {
87       uret = 0;
88       half_bit = false;
89       more_bits = true;
90     }
91 
92   /* Likewise, the low part.  */
93   if (lx != 0)
94     {
95       uintmax_t lo_uret;
96       bool lo_half_bit, lo_more_bits;
97       lx &= ((1ULL << (MANT_DIG - 1)) - 1);
98       lx |= 1ULL << (MANT_DIG - 1);
99       /* The high part exponent is at most 64, so the low part
100 	 exponent is at most 11.  */
101       if (lo_exponent >= -1)
102 	{
103 	  uint64_t h = 1ULL << (MANT_DIG - 2 - lo_exponent);
104 	  lo_half_bit = (lx & h) != 0;
105 	  lo_more_bits = (lx & (h - 1)) != 0;
106 	  lo_uret = lx >> (MANT_DIG - 1 - lo_exponent);
107 	}
108       else
109 	{
110 	  lo_uret = 0;
111 	  lo_half_bit = false;
112 	  lo_more_bits = true;
113 	}
114       if (lo_negative == negative)
115 	{
116 	  uret += lo_uret;
117 	  half_bit |= lo_half_bit;
118 	  more_bits |= lo_more_bits;
119 	}
120       else
121 	{
122 	  uret -= lo_uret;
123 	  if (lo_half_bit)
124 	    {
125 	      uret--;
126 	      half_bit = true;
127 	    }
128 	  if (lo_more_bits && !more_bits)
129 	    {
130 	      if (half_bit)
131 		{
132 		  half_bit = false;
133 		  more_bits = true;
134 		}
135 	      else
136 		{
137 		  uret--;
138 		  half_bit = true;
139 		  more_bits = true;
140 		}
141 	    }
142 	}
143     }
144 
145   return fromfp_round_and_return (negative, uret, half_bit, more_bits, round,
146 				  exponent, max_exponent, width);
147 }
148