1/* This is a software decimal floating point library.
2   Copyright (C) 2005, 2006 Free Software Foundation, Inc.
3
4This file is part of GCC.
5
6GCC is free software; you can redistribute it and/or modify it under
7the terms of the GNU General Public License as published by the Free
8Software Foundation; either version 2, or (at your option) any later
9version.
10
11In addition to the permissions in the GNU General Public License, the
12Free Software Foundation gives you unlimited permission to link the
13compiled version of this file into combinations with other programs,
14and to distribute those combinations without any restriction coming
15from the use of this file.  (The General Public License restrictions
16do apply in other respects; for example, they cover modification of
17the file, and distribution when not linked into a combine
18executable.)
19
20GCC is distributed in the hope that it will be useful, but WITHOUT ANY
21WARRANTY; without even the implied warranty of MERCHANTABILITY or
22FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
23for more details.
24
25You should have received a copy of the GNU General Public License
26along with GCC; see the file COPYING.  If not, write to the Free
27Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
2802110-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
49void __host_to_ieee_32 (_Decimal32 in, decimal32 *out);
50void __ieee_to_host_32 (decimal32 in, _Decimal32 *out);
51#endif
52#if WIDTH == 64 || WIDTH_TO == 64
53void __host_to_ieee_64 (_Decimal64 in, decimal64 *out);
54void __ieee_to_host_64 (decimal64 in, _Decimal64 *out);
55#endif
56#if WIDTH == 128 || WIDTH_TO == 128
57void __host_to_ieee_128 (_Decimal128 in, decimal128 *out);
58void __ieee_to_host_128 (decimal128 in, _Decimal128 *out);
59#endif
60
61/* A pointer to a unary decNumber operation.  */
62typedef decNumber* (*dfp_unary_func)
63     (decNumber *, decNumber *, decContext *);
64
65/* A pointer to a binary decNumber operation.  */
66typedef decNumber* (*dfp_binary_func)
67     (decNumber *, decNumber *, decNumber *, decContext *);
68
69extern unsigned long __dec_byte_swap (unsigned long);
70
71/* Unary operations.  */
72
73static inline DFP_C_TYPE
74dfp_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
101static inline DFP_C_TYPE
102dfp_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
131static inline int
132dfp_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)
166void
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
181void
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)
198static void
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
216void
217__host_to_ieee_64 (_Decimal64 in, decimal64 *out)
218{
219  __swap64 ((char *) &in, (char *) out);
220}
221
222void
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)
230static void
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
254void
255__host_to_ieee_128 (_Decimal128 in, decimal128 *out)
256{
257  __swap128 ((char *) &in, (char *) out);
258}
259
260void
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)
268DFP_C_TYPE
269DFP_ADD (DFP_C_TYPE arg_a, DFP_C_TYPE arg_b)
270{
271  return dfp_binary_op (decNumberAdd, arg_a, arg_b);
272}
273
274DFP_C_TYPE
275DFP_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)
282DFP_C_TYPE
283DFP_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)
290DFP_C_TYPE
291DFP_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)
298CMPtype
299DFP_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)
309CMPtype
310DFP_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)
320CMPtype
321DFP_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)
331CMPtype
332DFP_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)
342CMPtype
343DFP_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)
353CMPtype
354DFP_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)
367DFP_C_TYPE_TO
368DFP_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)
394INT_TYPE
395DFP_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)
456DFP_C_TYPE
457INT_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)
482BFP_TYPE
483DFP_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)
500DFP_C_TYPE
501BFP_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)
529CMPtype
530DFP_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