xref: /netbsd-src/external/gpl3/gcc.old/dist/libgcc/config/libbid/bid_from_int.c (revision 8feb0f0b7eaff0608f8350bbfa3098827b4bb91b)
1 /* Copyright (C) 2007-2020 Free Software Foundation, Inc.
2 
3 This file is part of GCC.
4 
5 GCC is free software; you can redistribute it and/or modify it under
6 the terms of the GNU General Public License as published by the Free
7 Software Foundation; either version 3, or (at your option) any later
8 version.
9 
10 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
11 WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13 for more details.
14 
15 Under Section 7 of GPL version 3, you are granted additional
16 permissions described in the GCC Runtime Library Exception, version
17 3.1, as published by the Free Software Foundation.
18 
19 You should have received a copy of the GNU General Public License and
20 a copy of the GCC Runtime Library Exception along with this program;
21 see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
22 <http://www.gnu.org/licenses/>.  */
23 
24 #include "bid_internal.h"
25 
26 /*****************************************************************************
27  *  BID64_round_integral_exact
28  ****************************************************************************/
29 
30 #if DECIMAL_CALL_BY_REFERENCE
31 void
bid64_from_int32(UINT64 * pres,int * px _EXC_MASKS_PARAM _EXC_INFO_PARAM)32 bid64_from_int32 (UINT64 * pres,
33 		  int *px _EXC_MASKS_PARAM _EXC_INFO_PARAM) {
34   int x = *px;
35 #else
36 UINT64
37 bid64_from_int32 (int x _EXC_MASKS_PARAM _EXC_INFO_PARAM) {
38 #endif
39   UINT64 res;
40 
41   // if integer is negative, put the absolute value
42   // in the lowest 32bits of the result
43   if ((x & SIGNMASK32) == SIGNMASK32) {
44     // negative int32
45     x = ~x + 1;	// 2's complement of x
46     res = (unsigned int) x | 0xb1c0000000000000ull;
47     // (exp << 53)) = biased exp. is 0
48   } else {	// positive int32
49     res = x | 0x31c0000000000000ull;	// (exp << 53)) = biased exp. is 0
50   }
51   BID_RETURN (res);
52 }
53 
54 #if DECIMAL_CALL_BY_REFERENCE
55 void
56 bid64_from_uint32 (UINT64 * pres, unsigned int *px
57 		   _EXC_MASKS_PARAM _EXC_INFO_PARAM) {
58   unsigned int x = *px;
59 #else
60 UINT64
61 bid64_from_uint32 (unsigned int x _EXC_MASKS_PARAM _EXC_INFO_PARAM) {
62 #endif
63   UINT64 res;
64 
65   res = x | 0x31c0000000000000ull;	// (exp << 53)) = biased exp. is 0
66   BID_RETURN (res);
67 }
68 
69 #if DECIMAL_CALL_BY_REFERENCE
70 void
71 bid64_from_int64 (UINT64 * pres, SINT64 * px
72 		  _RND_MODE_PARAM _EXC_FLAGS_PARAM _EXC_MASKS_PARAM
73 		  _EXC_INFO_PARAM) {
74   SINT64 x = *px;
75 #if !DECIMAL_GLOBAL_ROUNDING
76   unsigned int rnd_mode = *prnd_mode;
77 #endif
78 #else
79 UINT64
80 bid64_from_int64 (SINT64 x
81 		  _RND_MODE_PARAM _EXC_FLAGS_PARAM _EXC_MASKS_PARAM
82 		  _EXC_INFO_PARAM) {
83 #endif
84 
85   UINT64 res;
86   UINT64 x_sign, C;
87   unsigned int q, ind;
88   int incr_exp = 0;
89   int is_midpoint_lt_even = 0, is_midpoint_gt_even = 0;
90   int is_inexact_lt_midpoint = 0, is_inexact_gt_midpoint = 0;
91 
92   x_sign = x & 0x8000000000000000ull;
93   // if the integer is negative, use the absolute value
94   if (x_sign)
95     C = ~((UINT64) x) + 1;
96   else
97     C = x;
98   if (C <= BID64_SIG_MAX) {	// |C| <= 10^16-1 and the result is exact
99     if (C < 0x0020000000000000ull) {	// C < 2^53
100       res = x_sign | 0x31c0000000000000ull | C;
101     } else {	// C >= 2^53
102       res =
103 	x_sign | 0x6c70000000000000ull | (C & 0x0007ffffffffffffull);
104     }
105   } else {	// |C| >= 10^16 and the result may be inexact
106     // the smallest |C| is 10^16 which has 17 decimal digits
107     // the largest |C| is 0x8000000000000000 = 9223372036854775808 w/ 19 digits
108     if (C < 0x16345785d8a0000ull) {	// x < 10^17
109       q = 17;
110       ind = 1;	// number of digits to remove for q = 17
111     } else if (C < 0xde0b6b3a7640000ull) {	// C < 10^18
112       q = 18;
113       ind = 2;	// number of digits to remove for q = 18
114     } else {	// C < 10^19
115       q = 19;
116       ind = 3;	// number of digits to remove for q = 19
117     }
118     // overflow and underflow are not possible
119     // Note: performace can be improved by inlining this call
120     round64_2_18 (	// will work for 19 digits too if C fits in 64 bits
121 		   q, ind, C, &res, &incr_exp,
122 		   &is_midpoint_lt_even, &is_midpoint_gt_even,
123 		   &is_inexact_lt_midpoint, &is_inexact_gt_midpoint);
124     if (incr_exp)
125       ind++;
126     // set the inexact flag
127     if (is_inexact_lt_midpoint || is_inexact_gt_midpoint ||
128 	is_midpoint_lt_even || is_midpoint_gt_even)
129       *pfpsf |= INEXACT_EXCEPTION;
130     // general correction from RN to RA, RM, RP, RZ; result uses ind for exp
131     if (rnd_mode != ROUNDING_TO_NEAREST) {
132       if ((!x_sign
133 	   && ((rnd_mode == ROUNDING_UP && is_inexact_lt_midpoint)
134 	       ||
135 	       ((rnd_mode == ROUNDING_TIES_AWAY
136 		 || rnd_mode == ROUNDING_UP) && is_midpoint_gt_even)))
137 	  || (x_sign
138 	      && ((rnd_mode == ROUNDING_DOWN && is_inexact_lt_midpoint)
139 		  ||
140 		  ((rnd_mode == ROUNDING_TIES_AWAY
141 		    || rnd_mode == ROUNDING_DOWN)
142 		   && is_midpoint_gt_even)))) {
143 	res = res + 1;
144 	if (res == 0x002386f26fc10000ull) {	// res = 10^16 => rounding overflow
145 	  res = 0x00038d7ea4c68000ull;	// 10^15
146 	  ind = ind + 1;
147 	}
148       } else if ((is_midpoint_lt_even || is_inexact_gt_midpoint) &&
149 		 ((x_sign && (rnd_mode == ROUNDING_UP ||
150 			      rnd_mode == ROUNDING_TO_ZERO)) ||
151 		  (!x_sign && (rnd_mode == ROUNDING_DOWN ||
152 			       rnd_mode == ROUNDING_TO_ZERO)))) {
153 	res = res - 1;
154 	// check if we crossed into the lower decade
155 	if (res == 0x00038d7ea4c67fffull) {	// 10^15 - 1
156 	  res = 0x002386f26fc0ffffull;	// 10^16 - 1
157 	  ind = ind - 1;
158 	}
159       } else {
160 	;	// exact, the result is already correct
161       }
162     }
163     if (res < 0x0020000000000000ull) {	// res < 2^53
164       res = x_sign | (((UINT64) ind + 398) << 53) | res;
165     } else {	// res >= 2^53
166       res =
167 	x_sign | 0x6000000000000000ull | (((UINT64) ind + 398) << 51) |
168 	(res & 0x0007ffffffffffffull);
169     }
170   }
171   BID_RETURN (res);
172 }
173 
174 #if DECIMAL_CALL_BY_REFERENCE
175 void
176 bid64_from_uint64 (UINT64 * pres, UINT64 * px
177 		   _RND_MODE_PARAM _EXC_FLAGS_PARAM _EXC_MASKS_PARAM
178 		   _EXC_INFO_PARAM) {
179   UINT64 x = *px;
180 #if !DECIMAL_GLOBAL_ROUNDING
181   unsigned int rnd_mode = *prnd_mode;
182 #endif
183 #else
184 UINT64
185 bid64_from_uint64 (UINT64 x
186 		   _RND_MODE_PARAM _EXC_FLAGS_PARAM _EXC_MASKS_PARAM
187 		   _EXC_INFO_PARAM) {
188 #endif
189 
190   UINT64 res;
191   UINT128 x128, res128;
192   unsigned int q, ind;
193   int incr_exp = 0;
194   int is_midpoint_lt_even = 0, is_midpoint_gt_even = 0;
195   int is_inexact_lt_midpoint = 0, is_inexact_gt_midpoint = 0;
196 
197   if (x <= BID64_SIG_MAX) {	// x <= 10^16-1 and the result is exact
198     if (x < 0x0020000000000000ull) {	// x < 2^53
199       res = 0x31c0000000000000ull | x;
200     } else {	// x >= 2^53
201       res = 0x6c70000000000000ull | (x & 0x0007ffffffffffffull);
202     }
203   } else {	// x >= 10^16 and the result may be inexact
204     // the smallest x is 10^16 which has 17 decimal digits
205     // the largest x is 0xffffffffffffffff = 18446744073709551615 w/ 20 digits
206     if (x < 0x16345785d8a0000ull) {	// x < 10^17
207       q = 17;
208       ind = 1;	// number of digits to remove for q = 17
209     } else if (x < 0xde0b6b3a7640000ull) {	// x < 10^18
210       q = 18;
211       ind = 2;	// number of digits to remove for q = 18
212     } else if (x < 0x8ac7230489e80000ull) {	// x < 10^19
213       q = 19;
214       ind = 3;	// number of digits to remove for q = 19
215     } else {	// x < 10^20
216       q = 20;
217       ind = 4;	// number of digits to remove for q = 20
218     }
219     // overflow and underflow are not possible
220     // Note: performace can be improved by inlining this call
221     if (q <= 19) {
222       round64_2_18 (	// will work for 20 digits too if x fits in 64 bits
223 		     q, ind, x, &res, &incr_exp,
224 		     &is_midpoint_lt_even, &is_midpoint_gt_even,
225 		     &is_inexact_lt_midpoint, &is_inexact_gt_midpoint);
226     } else {	// q = 20
227       x128.w[1] = 0x0;
228       x128.w[0] = x;
229       round128_19_38 (q, ind, x128, &res128, &incr_exp,
230 		      &is_midpoint_lt_even, &is_midpoint_gt_even,
231 		      &is_inexact_lt_midpoint, &is_inexact_gt_midpoint);
232       res = res128.w[0];	// res.w[1] is 0
233     }
234     if (incr_exp)
235       ind++;
236     // set the inexact flag
237     if (is_inexact_lt_midpoint || is_inexact_gt_midpoint ||
238 	is_midpoint_lt_even || is_midpoint_gt_even)
239       *pfpsf |= INEXACT_EXCEPTION;
240     // general correction from RN to RA, RM, RP, RZ; result uses ind for exp
241     if (rnd_mode != ROUNDING_TO_NEAREST) {
242       if ((rnd_mode == ROUNDING_UP && is_inexact_lt_midpoint) ||
243 	  ((rnd_mode == ROUNDING_TIES_AWAY || rnd_mode == ROUNDING_UP)
244 	   && is_midpoint_gt_even)) {
245 	res = res + 1;
246 	if (res == 0x002386f26fc10000ull) {	// res = 10^16 => rounding overflow
247 	  res = 0x00038d7ea4c68000ull;	// 10^15
248 	  ind = ind + 1;
249 	}
250       } else if ((is_midpoint_lt_even || is_inexact_gt_midpoint) &&
251 		 (rnd_mode == ROUNDING_DOWN ||
252 		  rnd_mode == ROUNDING_TO_ZERO)) {
253 	res = res - 1;
254 	// check if we crossed into the lower decade
255 	if (res == 0x00038d7ea4c67fffull) {	// 10^15 - 1
256 	  res = 0x002386f26fc0ffffull;	// 10^16 - 1
257 	  ind = ind - 1;
258 	}
259       } else {
260 	;	// exact, the result is already correct
261       }
262     }
263     if (res < 0x0020000000000000ull) {	// res < 2^53
264       res = (((UINT64) ind + 398) << 53) | res;
265     } else {	// res >= 2^53
266       res = 0x6000000000000000ull | (((UINT64) ind + 398) << 51) |
267 	(res & 0x0007ffffffffffffull);
268     }
269   }
270   BID_RETURN (res);
271 }
272 
273 #if DECIMAL_CALL_BY_REFERENCE
274 void
275 bid128_from_int32 (UINT128 * pres,
276 		   int *px _EXC_MASKS_PARAM _EXC_INFO_PARAM) {
277   int x = *px;
278 #else
279 UINT128
280 bid128_from_int32 (int x _EXC_MASKS_PARAM _EXC_INFO_PARAM) {
281 #endif
282   UINT128 res;
283 
284   // if integer is negative, use the absolute value
285   if ((x & SIGNMASK32) == SIGNMASK32) {
286     res.w[HIGH_128W] = 0xb040000000000000ull;
287     res.w[LOW_128W] = ~((unsigned int) x) + 1;	// 2's complement of x
288   } else {
289     res.w[HIGH_128W] = 0x3040000000000000ull;
290     res.w[LOW_128W] = (unsigned int) x;
291   }
292   BID_RETURN (res);
293 }
294 
295 #if DECIMAL_CALL_BY_REFERENCE
296 void
297 bid128_from_uint32 (UINT128 * pres, unsigned int *px
298 		    _EXC_MASKS_PARAM _EXC_INFO_PARAM) {
299   unsigned int x = *px;
300 #else
301 UINT128
302 bid128_from_uint32 (unsigned int x _EXC_MASKS_PARAM _EXC_INFO_PARAM) {
303 #endif
304   UINT128 res;
305 
306   res.w[HIGH_128W] = 0x3040000000000000ull;
307   res.w[LOW_128W] = x;
308   BID_RETURN (res);
309 }
310 
311 #if DECIMAL_CALL_BY_REFERENCE
312 void
313 bid128_from_int64 (UINT128 * pres, SINT64 * px
314 		   _EXC_MASKS_PARAM _EXC_INFO_PARAM) {
315   SINT64 x = *px;
316 #else
317 UINT128
318 bid128_from_int64 (SINT64 x _EXC_MASKS_PARAM _EXC_INFO_PARAM) {
319 #endif
320 
321   UINT128 res;
322 
323   // if integer is negative, use the absolute value
324   if ((x & SIGNMASK64) == SIGNMASK64) {
325     res.w[HIGH_128W] = 0xb040000000000000ull;
326     res.w[LOW_128W] = ~x + 1;	// 2's complement of x
327   } else {
328     res.w[HIGH_128W] = 0x3040000000000000ull;
329     res.w[LOW_128W] = x;
330   }
331   BID_RETURN (res);
332 }
333 
334 #if DECIMAL_CALL_BY_REFERENCE
335 void
336 bid128_from_uint64 (UINT128 * pres, UINT64 * px
337 		    _EXC_MASKS_PARAM _EXC_INFO_PARAM) {
338   UINT64 x = *px;
339 #else
340 UINT128
341 bid128_from_uint64 (UINT64 x _EXC_MASKS_PARAM _EXC_INFO_PARAM) {
342 #endif
343 
344   UINT128 res;
345 
346   res.w[HIGH_128W] = 0x3040000000000000ull;
347   res.w[LOW_128W] = x;
348   BID_RETURN (res);
349 }
350