xref: /openbsd-src/gnu/gcc/gcc/config/dfp-bit.c (revision 404b540a9034ac75a6199ad1a32d1bbc7a0d4210)
1 /* This is a software decimal floating point library.
2    Copyright (C) 2005, 2006 Free Software Foundation, Inc.
3 
4 This file is part of GCC.
5 
6 GCC is free software; you can redistribute it and/or modify it under
7 the terms of the GNU General Public License as published by the Free
8 Software Foundation; either version 2, or (at your option) any later
9 version.
10 
11 In addition to the permissions in the GNU General Public License, the
12 Free Software Foundation gives you unlimited permission to link the
13 compiled version of this file into combinations with other programs,
14 and to distribute those combinations without any restriction coming
15 from the use of this file.  (The General Public License restrictions
16 do apply in other respects; for example, they cover modification of
17 the file, and distribution when not linked into a combine
18 executable.)
19 
20 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
21 WARRANTY; without even the implied warranty of MERCHANTABILITY or
22 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
23 for more details.
24 
25 You should have received a copy of the GNU General Public License
26 along with GCC; see the file COPYING.  If not, write to the Free
27 Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
28 02110-1301, USA.  */
29 
30 /* This implements IEEE 754R decimal floating point arithmetic, but
31    does not provide a mechanism for setting the rounding mode, or for
32    generating or handling exceptions.  Conversions between decimal
33    floating point types and other types depend on C library functions.
34 
35    Contributed by Ben Elliston  <bje@au.ibm.com>.  */
36 
37 /* The intended way to use this file is to make two copies, add `#define '
38    to one copy, then compile both copies and add them to libgcc.a.  */
39 
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <limits.h>
44 
45 #include "config/dfp-bit.h"
46 
47 /* Forward declarations.  */
48 #if WIDTH == 32 || WIDTH_TO == 32
49 void __host_to_ieee_32 (_Decimal32 in, decimal32 *out);
50 void __ieee_to_host_32 (decimal32 in, _Decimal32 *out);
51 #endif
52 #if WIDTH == 64 || WIDTH_TO == 64
53 void __host_to_ieee_64 (_Decimal64 in, decimal64 *out);
54 void __ieee_to_host_64 (decimal64 in, _Decimal64 *out);
55 #endif
56 #if WIDTH == 128 || WIDTH_TO == 128
57 void __host_to_ieee_128 (_Decimal128 in, decimal128 *out);
58 void __ieee_to_host_128 (decimal128 in, _Decimal128 *out);
59 #endif
60 
61 /* A pointer to a unary decNumber operation.  */
62 typedef decNumber* (*dfp_unary_func)
63      (decNumber *, decNumber *, decContext *);
64 
65 /* A pointer to a binary decNumber operation.  */
66 typedef decNumber* (*dfp_binary_func)
67      (decNumber *, decNumber *, decNumber *, decContext *);
68 
69 extern unsigned long __dec_byte_swap (unsigned long);
70 
71 /* Unary operations.  */
72 
73 static inline DFP_C_TYPE
dfp_unary_op(dfp_unary_func op,DFP_C_TYPE arg)74 dfp_unary_op (dfp_unary_func op, DFP_C_TYPE arg)
75 {
76   DFP_C_TYPE result;
77   decContext context;
78   decNumber arg1, res;
79   IEEE_TYPE a, encoded_result;
80 
81   HOST_TO_IEEE (arg, &a);
82 
83   decContextDefault (&context, CONTEXT_INIT);
84   context.round = CONTEXT_ROUND;
85 
86   TO_INTERNAL (&a, &arg1);
87 
88   /* Perform the operation.  */
89   op (&res, &arg1, &context);
90 
91   if (CONTEXT_TRAPS && CONTEXT_ERRORS (context))
92     DFP_RAISE (0);
93 
94   TO_ENCODED (&encoded_result, &res, &context);
95   IEEE_TO_HOST (encoded_result, &result);
96   return result;
97 }
98 
99 /* Binary operations.  */
100 
101 static inline DFP_C_TYPE
dfp_binary_op(dfp_binary_func op,DFP_C_TYPE arg_a,DFP_C_TYPE arg_b)102 dfp_binary_op (dfp_binary_func op, DFP_C_TYPE arg_a, DFP_C_TYPE arg_b)
103 {
104   DFP_C_TYPE result;
105   decContext context;
106   decNumber arg1, arg2, res;
107   IEEE_TYPE a, b, encoded_result;
108 
109   HOST_TO_IEEE (arg_a, &a);
110   HOST_TO_IEEE (arg_b, &b);
111 
112   decContextDefault (&context, CONTEXT_INIT);
113   context.round = CONTEXT_ROUND;
114 
115   TO_INTERNAL (&a, &arg1);
116   TO_INTERNAL (&b, &arg2);
117 
118   /* Perform the operation.  */
119   op (&res, &arg1, &arg2, &context);
120 
121   if (CONTEXT_TRAPS && CONTEXT_ERRORS (context))
122     DFP_RAISE (0);
123 
124   TO_ENCODED (&encoded_result, &res, &context);
125   IEEE_TO_HOST (encoded_result, &result);
126   return result;
127 }
128 
129 /* Comparison operations.  */
130 
131 static inline int
dfp_compare_op(dfp_binary_func op,DFP_C_TYPE arg_a,DFP_C_TYPE arg_b)132 dfp_compare_op (dfp_binary_func op, DFP_C_TYPE arg_a, DFP_C_TYPE arg_b)
133 {
134   IEEE_TYPE a, b;
135   decContext context;
136   decNumber arg1, arg2, res;
137   int result;
138 
139   HOST_TO_IEEE (arg_a, &a);
140   HOST_TO_IEEE (arg_b, &b);
141 
142   decContextDefault (&context, CONTEXT_INIT);
143   context.round = CONTEXT_ROUND;
144 
145   TO_INTERNAL (&a, &arg1);
146   TO_INTERNAL (&b, &arg2);
147 
148   /* Perform the comparison.  */
149   op (&res, &arg1, &arg2, &context);
150 
151   if (CONTEXT_TRAPS && CONTEXT_ERRORS (context))
152     DFP_RAISE (0);
153 
154   if (decNumberIsNegative (&res))
155     result = -1;
156   else if (decNumberIsZero (&res))
157     result = 0;
158   else
159     result = 1;
160 
161   return result;
162 }
163 
164 
165 #if defined(L_conv_sd)
166 void
__host_to_ieee_32(_Decimal32 in,decimal32 * out)167 __host_to_ieee_32 (_Decimal32 in, decimal32 *out)
168 {
169   uint32_t t;
170 
171   if (!LIBGCC2_FLOAT_WORDS_BIG_ENDIAN)
172     {
173       memcpy (&t, &in, 4);
174       t = __dec_byte_swap (t);
175       memcpy (out, &t, 4);
176     }
177   else
178     memcpy (out, &in, 4);
179 }
180 
181 void
__ieee_to_host_32(decimal32 in,_Decimal32 * out)182 __ieee_to_host_32 (decimal32 in, _Decimal32 *out)
183 {
184   uint32_t t;
185 
186   if (!LIBGCC2_FLOAT_WORDS_BIG_ENDIAN)
187     {
188       memcpy (&t, &in, 4);
189       t = __dec_byte_swap (t);
190       memcpy (out, &t, 4);
191     }
192   else
193     memcpy (out, &in, 4);
194 }
195 #endif /* L_conv_sd */
196 
197 #if defined(L_conv_dd)
198 static void
__swap64(char * src,char * dst)199 __swap64 (char *src, char *dst)
200 {
201   uint32_t t1, t2;
202 
203   if (!LIBGCC2_FLOAT_WORDS_BIG_ENDIAN)
204     {
205       memcpy (&t1, src, 4);
206       memcpy (&t2, src + 4, 4);
207       t1 = __dec_byte_swap (t1);
208       t2 = __dec_byte_swap (t2);
209       memcpy (dst, &t2, 4);
210       memcpy (dst + 4, &t1, 4);
211     }
212   else
213     memcpy (dst, src, 8);
214 }
215 
216 void
__host_to_ieee_64(_Decimal64 in,decimal64 * out)217 __host_to_ieee_64 (_Decimal64 in, decimal64 *out)
218 {
219   __swap64 ((char *) &in, (char *) out);
220 }
221 
222 void
__ieee_to_host_64(decimal64 in,_Decimal64 * out)223 __ieee_to_host_64 (decimal64 in, _Decimal64 *out)
224 {
225   __swap64 ((char *) &in, (char *) out);
226 }
227 #endif /* L_conv_dd */
228 
229 #if defined(L_conv_td)
230 static void
__swap128(char * src,char * dst)231 __swap128 (char *src, char *dst)
232 {
233   uint32_t t1, t2, t3, t4;
234 
235   if (!LIBGCC2_FLOAT_WORDS_BIG_ENDIAN)
236     {
237       memcpy (&t1, src, 4);
238       memcpy (&t2, src + 4, 4);
239       memcpy (&t3, src + 8, 4);
240       memcpy (&t4, src + 12, 4);
241       t1 = __dec_byte_swap (t1);
242       t2 = __dec_byte_swap (t2);
243       t3 = __dec_byte_swap (t3);
244       t4 = __dec_byte_swap (t4);
245       memcpy (dst, &t4, 4);
246       memcpy (dst + 4, &t3, 4);
247       memcpy (dst + 8, &t2, 4);
248       memcpy (dst + 12, &t1, 4);
249     }
250   else
251     memcpy (dst, src, 16);
252 }
253 
254 void
__host_to_ieee_128(_Decimal128 in,decimal128 * out)255 __host_to_ieee_128 (_Decimal128 in, decimal128 *out)
256 {
257   __swap128 ((char *) &in, (char *) out);
258 }
259 
260 void
__ieee_to_host_128(decimal128 in,_Decimal128 * out)261 __ieee_to_host_128 (decimal128 in, _Decimal128 *out)
262 {
263   __swap128 ((char *) &in, (char *) out);
264 }
265 #endif /* L_conv_td */
266 
267 #if defined(L_addsub_sd) || defined(L_addsub_dd) || defined(L_addsub_td)
268 DFP_C_TYPE
DFP_ADD(DFP_C_TYPE arg_a,DFP_C_TYPE arg_b)269 DFP_ADD (DFP_C_TYPE arg_a, DFP_C_TYPE arg_b)
270 {
271   return dfp_binary_op (decNumberAdd, arg_a, arg_b);
272 }
273 
274 DFP_C_TYPE
DFP_SUB(DFP_C_TYPE arg_a,DFP_C_TYPE arg_b)275 DFP_SUB (DFP_C_TYPE arg_a, DFP_C_TYPE arg_b)
276 {
277   return dfp_binary_op (decNumberSubtract, arg_a, arg_b);
278 }
279 #endif /* L_addsub */
280 
281 #if defined(L_mul_sd) || defined(L_mul_dd) || defined(L_mul_td)
282 DFP_C_TYPE
DFP_MULTIPLY(DFP_C_TYPE arg_a,DFP_C_TYPE arg_b)283 DFP_MULTIPLY (DFP_C_TYPE arg_a, DFP_C_TYPE arg_b)
284 {
285   return dfp_binary_op (decNumberMultiply, arg_a, arg_b);
286 }
287 #endif /* L_mul */
288 
289 #if defined(L_div_sd) || defined(L_div_dd) || defined(L_div_td)
290 DFP_C_TYPE
DFP_DIVIDE(DFP_C_TYPE arg_a,DFP_C_TYPE arg_b)291 DFP_DIVIDE (DFP_C_TYPE arg_a, DFP_C_TYPE arg_b)
292 {
293   return dfp_binary_op (decNumberDivide, arg_a, arg_b);
294 }
295 #endif /* L_div */
296 
297 #if defined (L_eq_sd) || defined (L_eq_dd) || defined (L_eq_td)
298 CMPtype
DFP_EQ(DFP_C_TYPE arg_a,DFP_C_TYPE arg_b)299 DFP_EQ (DFP_C_TYPE arg_a, DFP_C_TYPE arg_b)
300 {
301   int stat;
302   stat = dfp_compare_op (decNumberCompare, arg_a, arg_b);
303   /* For EQ return zero for true, nonzero for false.  */
304   return stat != 0;
305 }
306 #endif /* L_eq */
307 
308 #if defined (L_ne_sd) || defined (L_ne_dd) || defined (L_ne_td)
309 CMPtype
DFP_NE(DFP_C_TYPE arg_a,DFP_C_TYPE arg_b)310 DFP_NE (DFP_C_TYPE arg_a, DFP_C_TYPE arg_b)
311 {
312   int stat;
313   stat = dfp_compare_op (decNumberCompare, arg_a, arg_b);
314   /* For NE return nonzero for true, zero for false.  */
315   return stat != 0;
316 }
317 #endif /* L_ne */
318 
319 #if defined (L_lt_sd) || defined (L_lt_dd) || defined (L_lt_td)
320 CMPtype
DFP_LT(DFP_C_TYPE arg_a,DFP_C_TYPE arg_b)321 DFP_LT (DFP_C_TYPE arg_a, DFP_C_TYPE arg_b)
322 {
323   int stat;
324   stat = dfp_compare_op (decNumberCompare, arg_a, arg_b);
325   /* For LT return -1 (<0) for true, 1 for false.  */
326   return (stat == -1) ? -1 : 1;
327 }
328 #endif /* L_lt */
329 
330 #if defined (L_gt_sd) || defined (L_gt_dd) || defined (L_gt_td)
331 CMPtype
DFP_GT(DFP_C_TYPE arg_a,DFP_C_TYPE arg_b)332 DFP_GT (DFP_C_TYPE arg_a, DFP_C_TYPE arg_b)
333 {
334   int stat;
335   stat = dfp_compare_op (decNumberCompare, arg_a, arg_b);
336   /* For GT return 1 (>0) for true, -1 for false.  */
337   return (stat == 1) ? 1 : -1;
338 }
339 #endif
340 
341 #if defined (L_le_sd) || defined (L_le_dd) || defined (L_le_td)
342 CMPtype
DFP_LE(DFP_C_TYPE arg_a,DFP_C_TYPE arg_b)343 DFP_LE (DFP_C_TYPE arg_a, DFP_C_TYPE arg_b)
344 {
345   int stat;
346   stat = dfp_compare_op (decNumberCompare, arg_a, arg_b);
347   /* For LE return 0 (<= 0) for true, 1 for false.  */
348   return stat == 1;
349 }
350 #endif /* L_le */
351 
352 #if defined (L_ge_sd) || defined (L_ge_dd) || defined (L_ge_td)
353 CMPtype
DFP_GE(DFP_C_TYPE arg_a,DFP_C_TYPE arg_b)354 DFP_GE (DFP_C_TYPE arg_a, DFP_C_TYPE arg_b)
355 {
356   int stat;
357   stat = dfp_compare_op (decNumberCompare, arg_a, arg_b);
358   /* For GE return 1 (>=0) for true, -1 for false.  */
359   return (stat != -1) ? 1 : -1;
360 }
361 #endif /* L_ge */
362 
363 #define BUFMAX 128
364 
365 #if defined (L_sd_to_dd) || defined (L_sd_to_td) || defined (L_dd_to_sd) \
366  || defined (L_dd_to_td) || defined (L_td_to_sd) || defined (L_td_to_dd)
367 DFP_C_TYPE_TO
DFP_TO_DFP(DFP_C_TYPE f_from)368 DFP_TO_DFP (DFP_C_TYPE f_from)
369 {
370   DFP_C_TYPE_TO f_to;
371   IEEE_TYPE s_from;
372   IEEE_TYPE_TO s_to;
373   decNumber d;
374   decContext context;
375 
376   decContextDefault (&context, CONTEXT_INIT);
377   context.round = CONTEXT_ROUND;
378 
379   HOST_TO_IEEE (f_from, &s_from);
380   TO_INTERNAL (&s_from, &d);
381   TO_ENCODED_TO (&s_to, &d, &context);
382   if (CONTEXT_TRAPS && (context.status & DEC_Inexact) != 0)
383     DFP_RAISE (DEC_Inexact);
384 
385   IEEE_TO_HOST_TO (s_to, &f_to);
386   return f_to;
387 }
388 #endif
389 
390 #if defined (L_sd_to_si) || defined (L_dd_to_si) || defined (L_td_to_si) \
391   || defined (L_sd_to_di) || defined (L_dd_to_di) || defined (L_td_to_di) \
392   || defined (L_sd_to_usi) || defined (L_dd_to_usi) || defined (L_td_to_usi) \
393   || defined (L_sd_to_udi) || defined (L_dd_to_udi) || defined (L_td_to_udi)
394 INT_TYPE
DFP_TO_INT(DFP_C_TYPE x)395 DFP_TO_INT (DFP_C_TYPE x)
396 {
397   /* decNumber's decimal* types have the same format as C's _Decimal*
398      types, but they have different calling conventions.  */
399 
400   IEEE_TYPE s;
401   char buf[BUFMAX];
402   char *pos;
403   decNumber qval, n1, n2;
404   decContext context;
405 
406   decContextDefault (&context, CONTEXT_INIT);
407   /* Need non-default rounding mode here.  */
408   context.round = DEC_ROUND_DOWN;
409 
410   HOST_TO_IEEE (x, &s);
411   TO_INTERNAL (&s, &n1);
412   /* Rescale if the exponent is less than zero.  */
413   decNumberToIntegralValue (&n2, &n1, &context);
414   /* Get a value to use for the quantize call.  */
415   decNumberFromString (&qval, (char *) "1.0", &context);
416   /* Force the exponent to zero.  */
417   decNumberQuantize (&n1, &n2, &qval, &context);
418   /* This is based on text in N1107 section 5.1; it might turn out to be
419      undefined behavior instead.  */
420   if (context.status & DEC_Invalid_operation)
421     {
422 #if defined (L_sd_to_si) || defined (L_dd_to_si) || defined (L_td_to_si)
423       if (decNumberIsNegative(&n2))
424         return INT_MIN;
425       else
426         return INT_MAX;
427 #elif defined (L_sd_to_di) || defined (L_dd_to_di) || defined (L_td_to_di)
428       if (decNumberIsNegative(&n2))
429         /* Find a defined constant that will work here.  */
430         return (-9223372036854775807LL - 1LL);
431       else
432         /* Find a defined constant that will work here.  */
433         return 9223372036854775807LL;
434 #elif defined (L_sd_to_usi) || defined (L_dd_to_usi) || defined (L_td_to_usi)
435       return UINT_MAX;
436 #elif defined (L_sd_to_udi) || defined (L_dd_to_udi) || defined (L_td_to_udi)
437         /* Find a defined constant that will work here.  */
438       return 18446744073709551615ULL;
439 #endif
440     }
441   /* Get a string, which at this point will not include an exponent.  */
442   decNumberToString (&n1, buf);
443   /* Ignore the fractional part.  */
444   pos = strchr (buf, '.');
445   if (pos)
446     *pos = 0;
447   /* Use a C library function to convert to the integral type.  */
448   return STR_TO_INT (buf, NULL, 10);
449 }
450 #endif
451 
452 #if defined (L_si_to_sd) || defined (L_si_to_dd) || defined (L_si_to_td) \
453   || defined (L_di_to_sd) || defined (L_di_to_dd) || defined (L_di_to_td) \
454   || defined (L_usi_to_sd) || defined (L_usi_to_dd) || defined (L_usi_to_td) \
455   || defined (L_udi_to_sd) || defined (L_udi_to_dd) || defined (L_udi_to_td)
456 DFP_C_TYPE
INT_TO_DFP(INT_TYPE i)457 INT_TO_DFP (INT_TYPE i)
458 {
459   DFP_C_TYPE f;
460   IEEE_TYPE s;
461   char buf[BUFMAX];
462   decContext context;
463 
464   decContextDefault (&context, CONTEXT_INIT);
465   context.round = CONTEXT_ROUND;
466 
467   /* Use a C library function to get a floating point string.  */
468   sprintf (buf, INT_FMT ".0", CAST_FOR_FMT(i));
469   /* Convert from the floating point string to a decimal* type.  */
470   FROM_STRING (&s, buf, &context);
471   IEEE_TO_HOST (s, &f);
472   if (CONTEXT_TRAPS && (context.status & DEC_Inexact) != 0)
473     DFP_RAISE (DEC_Inexact);
474   return f;
475 }
476 #endif
477 
478 #if defined (L_sd_to_sf) || defined (L_dd_to_sf) || defined (L_td_to_sf) \
479  || defined (L_sd_to_df) || defined (L_dd_to_df) || defined (L_td_to_df) \
480  || ((defined (L_sd_to_xf) || defined (L_dd_to_xf) || defined (L_td_to_xf)) \
481      && LIBGCC2_HAS_XF_MODE)
482 BFP_TYPE
DFP_TO_BFP(DFP_C_TYPE f)483 DFP_TO_BFP (DFP_C_TYPE f)
484 {
485   IEEE_TYPE s;
486   char buf[BUFMAX];
487 
488   HOST_TO_IEEE (f, &s);
489   /* Write the value to a string.  */
490   TO_STRING (&s, buf);
491   /* Read it as the binary floating point type and return that.  */
492   return STR_TO_BFP (buf, NULL);
493 }
494 #endif
495 
496 #if defined (L_sf_to_sd) || defined (L_sf_to_dd) || defined (L_sf_to_td) \
497  || defined (L_df_to_sd) || defined (L_df_to_dd) || defined (L_df_to_td) \
498  || ((defined (L_xf_to_sd) || defined (L_xf_to_dd) || defined (L_xf_to_td)) \
499      && LIBGCC2_HAS_XF_MODE)
500 DFP_C_TYPE
BFP_TO_DFP(BFP_TYPE x)501 BFP_TO_DFP (BFP_TYPE x)
502 {
503   DFP_C_TYPE f;
504   IEEE_TYPE s;
505   char buf[BUFMAX];
506   decContext context;
507 
508   decContextDefault (&context, CONTEXT_INIT);
509   context.round = CONTEXT_ROUND;
510 
511   /* Use a C library function to write the floating point value to a string.  */
512 #ifdef BFP_VIA_TYPE
513   /* FIXME: Is there a better way to output an XFmode variable in C?  */
514   sprintf (buf, BFP_FMT, (BFP_VIA_TYPE) x);
515 #else
516   sprintf (buf, BFP_FMT, x);
517 #endif
518 
519   /* Convert from the floating point string to a decimal* type.  */
520   FROM_STRING (&s, buf, &context);
521   IEEE_TO_HOST (s, &f);
522   if (CONTEXT_TRAPS && (context.status & DEC_Inexact) != 0)
523     DFP_RAISE (DEC_Inexact);
524   return f;
525 }
526 #endif
527 
528 #if defined (L_unord_sd) || defined (L_unord_dd) || defined (L_unord_td)
529 CMPtype
DFP_UNORD(DFP_C_TYPE arg_a,DFP_C_TYPE arg_b)530 DFP_UNORD (DFP_C_TYPE arg_a, DFP_C_TYPE arg_b)
531 {
532   decNumber arg1, arg2;
533   IEEE_TYPE a, b;
534 
535   HOST_TO_IEEE (arg_a, &a);
536   HOST_TO_IEEE (arg_b, &b);
537   TO_INTERNAL (&a, &arg1);
538   TO_INTERNAL (&b, &arg2);
539   return (decNumberIsNaN (&arg1) || decNumberIsNaN (&arg2));
540 }
541 #endif /* L_unord_sd || L_unord_dd || L_unord_td */
542