OSDN Git Service

2007-09-10 Janis Johnson <janis187@us.ibm.com>
[pf3gnuchains/gcc-fork.git] / gcc / config / dfp-bit.c
1 /* This is a software decimal floating point library.
2    Copyright (C) 2005, 2006, 2007 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 /* FIXME: This implementation doesn't support TFmode conversions.  */
41 #if !(defined (L_sd_to_tf) || defined (L_dd_to_tf) \
42       || defined (L_td_to_tf) || defined (L_tf_to_sd) \
43       || defined (L_tf_to_dd) || defined (L_tf_to_td))
44
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <limits.h>
49
50 #include "config/dfp-bit.h"
51
52 /* Forward declarations.  */
53 #if WIDTH == 32 || WIDTH_TO == 32
54 void __host_to_ieee_32 (_Decimal32 in, decimal32 *out);
55 void __ieee_to_host_32 (decimal32 in, _Decimal32 *out);
56 #endif
57 #if WIDTH == 64 || WIDTH_TO == 64
58 void __host_to_ieee_64 (_Decimal64 in, decimal64 *out);
59 void __ieee_to_host_64 (decimal64 in, _Decimal64 *out);
60 #endif
61 #if WIDTH == 128 || WIDTH_TO == 128
62 void __host_to_ieee_128 (_Decimal128 in, decimal128 *out);
63 void __ieee_to_host_128 (decimal128 in, _Decimal128 *out);
64 #endif
65
66 /* A pointer to a unary decNumber operation.  */
67 typedef decNumber* (*dfp_unary_func)
68      (decNumber *, decNumber *, decContext *);
69
70 /* A pointer to a binary decNumber operation.  */
71 typedef decNumber* (*dfp_binary_func)
72      (decNumber *, const decNumber *, const decNumber *, decContext *);
73 \f
74 /* Unary operations.  */
75
76 static inline DFP_C_TYPE
77 dfp_unary_op (dfp_unary_func op, DFP_C_TYPE arg)
78 {
79   DFP_C_TYPE result;
80   decContext context;
81   decNumber arg1, res;
82   IEEE_TYPE a, encoded_result;
83
84   HOST_TO_IEEE (arg, &a);
85
86   decContextDefault (&context, CONTEXT_INIT);
87   DFP_INIT_ROUNDMODE (context.round);
88
89   TO_INTERNAL (&a, &arg1);
90
91   /* Perform the operation.  */
92   op (&res, &arg1, &context);
93
94   if (DFP_EXCEPTIONS_ENABLED && context.status != 0)
95     {
96       /* decNumber exception flags we care about here.  */
97       int ieee_flags;
98       int dec_flags = DEC_IEEE_854_Division_by_zero | DEC_IEEE_854_Inexact
99                       | DEC_IEEE_854_Invalid_operation | DEC_IEEE_854_Overflow
100                       | DEC_IEEE_854_Underflow;
101       dec_flags &= context.status;
102       ieee_flags = DFP_IEEE_FLAGS (dec_flags);
103       if (ieee_flags != 0)
104         DFP_HANDLE_EXCEPTIONS (ieee_flags);
105     }
106
107   TO_ENCODED (&encoded_result, &res, &context);
108   IEEE_TO_HOST (encoded_result, &result);
109   return result;
110 }
111
112 /* Binary operations.  */
113
114 static inline DFP_C_TYPE
115 dfp_binary_op (dfp_binary_func op, DFP_C_TYPE arg_a, DFP_C_TYPE arg_b)
116 {
117   DFP_C_TYPE result;
118   decContext context;
119   decNumber arg1, arg2, res;
120   IEEE_TYPE a, b, encoded_result;
121
122   HOST_TO_IEEE (arg_a, &a);
123   HOST_TO_IEEE (arg_b, &b);
124
125   decContextDefault (&context, CONTEXT_INIT);
126   DFP_INIT_ROUNDMODE (context.round);
127
128   TO_INTERNAL (&a, &arg1);
129   TO_INTERNAL (&b, &arg2);
130
131   /* Perform the operation.  */
132   op (&res, &arg1, &arg2, &context);
133
134   if (DFP_EXCEPTIONS_ENABLED && context.status != 0)
135     {
136       /* decNumber exception flags we care about here.  */
137       int ieee_flags;
138       int dec_flags = DEC_IEEE_854_Division_by_zero | DEC_IEEE_854_Inexact
139                       | DEC_IEEE_854_Invalid_operation | DEC_IEEE_854_Overflow
140                       | DEC_IEEE_854_Underflow;
141       dec_flags &= context.status;
142       ieee_flags = DFP_IEEE_FLAGS (dec_flags);
143       if (ieee_flags != 0)
144         DFP_HANDLE_EXCEPTIONS (ieee_flags);
145     }
146
147   TO_ENCODED (&encoded_result, &res, &context);
148   IEEE_TO_HOST (encoded_result, &result);
149   return result;
150 }
151
152 /* Comparison operations.  */
153
154 static inline int
155 dfp_compare_op (dfp_binary_func op, DFP_C_TYPE arg_a, DFP_C_TYPE arg_b)
156 {
157   IEEE_TYPE a, b;
158   decContext context;
159   decNumber arg1, arg2, res;
160   int result;
161
162   HOST_TO_IEEE (arg_a, &a);
163   HOST_TO_IEEE (arg_b, &b);
164
165   decContextDefault (&context, CONTEXT_INIT);
166   DFP_INIT_ROUNDMODE (context.round);
167
168   TO_INTERNAL (&a, &arg1);
169   TO_INTERNAL (&b, &arg2);
170
171   /* Perform the comparison.  */
172   op (&res, &arg1, &arg2, &context);
173
174   if (decNumberIsNegative (&res))
175     result = -1;
176   else if (decNumberIsZero (&res))
177     result = 0;
178   else if (decNumberIsNaN (&res))
179     result = -2;
180   else
181     result = 1;
182
183   return result;
184 }
185
186 \f
187 #if defined(L_conv_sd)
188 void
189 __host_to_ieee_32 (_Decimal32 in, decimal32 *out)
190 {
191   memcpy (out, &in, 4);
192 }
193
194 void
195 __ieee_to_host_32 (decimal32 in, _Decimal32 *out)
196 {
197   memcpy (out, &in, 4);
198 }
199 #endif /* L_conv_sd */
200
201 #if defined(L_conv_dd)
202 void
203 __host_to_ieee_64 (_Decimal64 in, decimal64 *out)
204 {
205   memcpy (out, &in, 8);
206 }
207
208 void
209 __ieee_to_host_64 (decimal64 in, _Decimal64 *out)
210 {
211   memcpy (out, &in, 8);
212 }
213 #endif /* L_conv_dd */
214
215 #if defined(L_conv_td)
216 void
217 __host_to_ieee_128 (_Decimal128 in, decimal128 *out)
218 {
219   memcpy (out, &in, 16);
220 }
221
222 void
223 __ieee_to_host_128 (decimal128 in, _Decimal128 *out)
224 {
225   memcpy (out, &in, 16);
226 }
227 #endif /* L_conv_td */
228
229 #if defined(L_addsub_sd) || defined(L_addsub_dd) || defined(L_addsub_td)
230 DFP_C_TYPE
231 DFP_ADD (DFP_C_TYPE arg_a, DFP_C_TYPE arg_b)
232 {
233   return dfp_binary_op (decNumberAdd, arg_a, arg_b);
234 }
235
236 DFP_C_TYPE
237 DFP_SUB (DFP_C_TYPE arg_a, DFP_C_TYPE arg_b)
238 {
239   return dfp_binary_op (decNumberSubtract, arg_a, arg_b);
240 }
241 #endif /* L_addsub */
242
243 #if defined(L_mul_sd) || defined(L_mul_dd) || defined(L_mul_td)
244 DFP_C_TYPE
245 DFP_MULTIPLY (DFP_C_TYPE arg_a, DFP_C_TYPE arg_b)
246 {
247   return dfp_binary_op (decNumberMultiply, arg_a, arg_b);
248 }
249 #endif /* L_mul */
250
251 #if defined(L_div_sd) || defined(L_div_dd) || defined(L_div_td)
252 DFP_C_TYPE
253 DFP_DIVIDE (DFP_C_TYPE arg_a, DFP_C_TYPE arg_b)
254 {
255   return dfp_binary_op (decNumberDivide, arg_a, arg_b);
256 }
257 #endif /* L_div */
258
259 #if defined (L_eq_sd) || defined (L_eq_dd) || defined (L_eq_td)
260 CMPtype
261 DFP_EQ (DFP_C_TYPE arg_a, DFP_C_TYPE arg_b)
262 {
263   int stat;
264   stat = dfp_compare_op (decNumberCompare, arg_a, arg_b);
265   /* For EQ return zero for true, nonzero for false.  */
266   return stat != 0;
267 }
268 #endif /* L_eq */
269
270 #if defined (L_ne_sd) || defined (L_ne_dd) || defined (L_ne_td)
271 CMPtype
272 DFP_NE (DFP_C_TYPE arg_a, DFP_C_TYPE arg_b)
273 {
274   int stat;
275   stat = dfp_compare_op (decNumberCompare, arg_a, arg_b);
276   /* For NE return zero for true, nonzero for false.  */
277   if (__builtin_expect (stat == -2, 0))  /* An operand is NaN.  */
278     return 1;
279   return stat != 0;
280 }
281 #endif /* L_ne */
282
283 #if defined (L_lt_sd) || defined (L_lt_dd) || defined (L_lt_td)
284 CMPtype
285 DFP_LT (DFP_C_TYPE arg_a, DFP_C_TYPE arg_b)
286 {
287   int stat;
288   stat = dfp_compare_op (decNumberCompare, arg_a, arg_b);
289   /* For LT return -1 (<0) for true, 1 for false.  */
290   return (stat == -1) ? -1 : 1;
291 }
292 #endif /* L_lt */
293
294 #if defined (L_gt_sd) || defined (L_gt_dd) || defined (L_gt_td)
295 CMPtype
296 DFP_GT (DFP_C_TYPE arg_a, DFP_C_TYPE arg_b)
297 {
298   int stat;
299   stat = dfp_compare_op (decNumberCompare, arg_a, arg_b);
300   /* For GT return 1 (>0) for true, -1 for false.  */
301   return (stat == 1) ? 1 : -1;
302 }
303 #endif
304
305 #if defined (L_le_sd) || defined (L_le_dd) || defined (L_le_td)
306 CMPtype
307 DFP_LE (DFP_C_TYPE arg_a, DFP_C_TYPE arg_b)
308 {
309   int stat;
310   stat = dfp_compare_op (decNumberCompare, arg_a, arg_b);
311   /* For LE return 0 (<= 0) for true, 1 for false.  */
312   if (__builtin_expect (stat == -2, 0))  /* An operand is NaN.  */
313     return 1;
314   return stat == 1;
315 }
316 #endif /* L_le */
317
318 #if defined (L_ge_sd) || defined (L_ge_dd) || defined (L_ge_td)
319 CMPtype
320 DFP_GE (DFP_C_TYPE arg_a, DFP_C_TYPE arg_b)
321 {
322   int stat;
323   stat = dfp_compare_op (decNumberCompare, arg_a, arg_b);
324   /* For GE return 1 (>=0) for true, -1 for false.  */
325   if (__builtin_expect (stat == -2, 0))  /* An operand is NaN.  */
326     return -1;
327   return (stat != -1) ? 1 : -1;
328 }
329 #endif /* L_ge */
330
331 #define BUFMAX 128
332
333 #if defined (L_sd_to_dd) || defined (L_sd_to_td) || defined (L_dd_to_sd) \
334  || defined (L_dd_to_td) || defined (L_td_to_sd) || defined (L_td_to_dd)
335 DFP_C_TYPE_TO
336 DFP_TO_DFP (DFP_C_TYPE f_from)
337 {
338   DFP_C_TYPE_TO f_to;
339   IEEE_TYPE s_from;
340   IEEE_TYPE_TO s_to;
341   decNumber d;
342   decContext context;
343
344   decContextDefault (&context, CONTEXT_INIT);
345   DFP_INIT_ROUNDMODE (context.round);
346
347   HOST_TO_IEEE (f_from, &s_from);
348   TO_INTERNAL (&s_from, &d);
349   TO_ENCODED_TO (&s_to, &d, &context);
350
351   if (DFP_EXCEPTIONS_ENABLED && context.status != 0)
352     {
353       /* decNumber exception flags we care about here.  */
354       int ieee_flags;
355       int dec_flags = DEC_IEEE_854_Inexact | DEC_IEEE_854_Invalid_operation
356                       | DEC_IEEE_854_Overflow;
357       dec_flags &= context.status;
358       ieee_flags = DFP_IEEE_FLAGS (dec_flags);
359       if (ieee_flags != 0)
360         DFP_HANDLE_EXCEPTIONS (ieee_flags);
361     }
362
363   IEEE_TO_HOST_TO (s_to, &f_to);
364   return f_to;
365 }
366 #endif
367
368 #if defined (L_sd_to_si) || defined (L_dd_to_si) || defined (L_td_to_si) \
369   || defined (L_sd_to_di) || defined (L_dd_to_di) || defined (L_td_to_di) \
370   || defined (L_sd_to_usi) || defined (L_dd_to_usi) || defined (L_td_to_usi) \
371   || defined (L_sd_to_udi) || defined (L_dd_to_udi) || defined (L_td_to_udi)
372 INT_TYPE
373 DFP_TO_INT (DFP_C_TYPE x)
374 {
375   /* decNumber's decimal* types have the same format as C's _Decimal*
376      types, but they have different calling conventions.  */
377
378   /* TODO: Decimal float to integer conversions should raise FE_INVALID
379      if the result value does not fit into the result type.  */
380
381   IEEE_TYPE s;
382   char buf[BUFMAX];
383   char *pos;
384   decNumber qval, n1, n2;
385   decContext context;
386
387   /* Use a large context to avoid losing precision.  */
388   decContextDefault (&context, DEC_INIT_DECIMAL128);
389   /* Need non-default rounding mode here.  */
390   context.round = DEC_ROUND_DOWN;
391
392   HOST_TO_IEEE (x, &s);
393   TO_INTERNAL (&s, &n1);
394   /* Rescale if the exponent is less than zero.  */
395   decNumberToIntegralValue (&n2, &n1, &context);
396   /* Get a value to use for the quantize call.  */
397   decNumberFromString (&qval, (char *) "1.", &context);
398   /* Force the exponent to zero.  */
399   decNumberQuantize (&n1, &n2, &qval, &context);
400   /* Get a string, which at this point will not include an exponent.  */
401   decNumberToString (&n1, buf);
402   /* Ignore the fractional part.  */
403   pos = strchr (buf, '.');
404   if (pos)
405     *pos = 0;
406   /* Use a C library function to convert to the integral type.  */
407   return STR_TO_INT (buf, NULL, 10);
408 }
409 #endif
410
411 #if defined (L_si_to_sd) || defined (L_si_to_dd) || defined (L_si_to_td) \
412   || defined (L_di_to_sd) || defined (L_di_to_dd) || defined (L_di_to_td) \
413   || defined (L_usi_to_sd) || defined (L_usi_to_dd) || defined (L_usi_to_td) \
414   || defined (L_udi_to_sd) || defined (L_udi_to_dd) || defined (L_udi_to_td)
415 DFP_C_TYPE
416 INT_TO_DFP (INT_TYPE i)
417 {
418   DFP_C_TYPE f;
419   IEEE_TYPE s;
420   char buf[BUFMAX];
421   decContext context;
422
423   decContextDefault (&context, CONTEXT_INIT);
424   DFP_INIT_ROUNDMODE (context.round);
425
426   /* Use a C library function to get a floating point string.  */
427   sprintf (buf, INT_FMT ".0", CAST_FOR_FMT(i));
428   /* Convert from the floating point string to a decimal* type.  */
429   FROM_STRING (&s, buf, &context);
430   IEEE_TO_HOST (s, &f);
431
432   if (DFP_EXCEPTIONS_ENABLED && context.status != 0)
433     {
434       /* decNumber exception flags we care about here.  */
435       int ieee_flags;
436       int dec_flags = DEC_IEEE_854_Inexact | DEC_IEEE_854_Invalid_operation
437                       | DEC_IEEE_854_Overflow;
438       dec_flags &= context.status;
439       ieee_flags = DFP_IEEE_FLAGS (dec_flags);
440       if (ieee_flags != 0)
441         DFP_HANDLE_EXCEPTIONS (ieee_flags);
442     }
443
444   return f;
445 }
446 #endif
447
448 #if defined (L_sd_to_sf) || defined (L_dd_to_sf) || defined (L_td_to_sf) \
449  || defined (L_sd_to_df) || defined (L_dd_to_df) || defined (L_td_to_df) \
450  || ((defined (L_sd_to_xf) || defined (L_dd_to_xf) || defined (L_td_to_xf)) \
451      && LIBGCC2_HAS_XF_MODE)
452 BFP_TYPE
453 DFP_TO_BFP (DFP_C_TYPE f)
454 {
455   IEEE_TYPE s;
456   char buf[BUFMAX];
457
458   HOST_TO_IEEE (f, &s);
459   /* Write the value to a string.  */
460   TO_STRING (&s, buf);
461   /* Read it as the binary floating point type and return that.  */
462   return STR_TO_BFP (buf, NULL);
463 }
464 #endif
465                                                                                 
466 #if defined (L_sf_to_sd) || defined (L_sf_to_dd) || defined (L_sf_to_td) \
467  || defined (L_df_to_sd) || defined (L_df_to_dd) || defined (L_df_to_td) \
468  || ((defined (L_xf_to_sd) || defined (L_xf_to_dd) || defined (L_xf_to_td)) \
469      && LIBGCC2_HAS_XF_MODE)
470 DFP_C_TYPE
471 BFP_TO_DFP (BFP_TYPE x)
472 {
473   DFP_C_TYPE f;
474   IEEE_TYPE s;
475   char buf[BUFMAX];
476   decContext context;
477
478   decContextDefault (&context, CONTEXT_INIT);
479   DFP_INIT_ROUNDMODE (context.round);
480
481   /* Use a C library function to write the floating point value to a string.  */
482 #ifdef BFP_VIA_TYPE
483   /* FIXME: Is there a better way to output an XFmode variable in C?  */
484   sprintf (buf, BFP_FMT, (BFP_VIA_TYPE) x);
485 #else
486   sprintf (buf, BFP_FMT, x);
487 #endif
488
489   /* Convert from the floating point string to a decimal* type.  */
490   FROM_STRING (&s, buf, &context);
491   IEEE_TO_HOST (s, &f);
492
493   if (DFP_EXCEPTIONS_ENABLED && context.status != 0)
494     {
495       /* decNumber exception flags we care about here.  */
496       int ieee_flags;
497       int dec_flags = DEC_IEEE_854_Inexact | DEC_IEEE_854_Invalid_operation
498                       | DEC_IEEE_854_Overflow | DEC_IEEE_854_Underflow;
499       dec_flags &= context.status;
500       ieee_flags = DFP_IEEE_FLAGS (dec_flags);
501       if (ieee_flags != 0)
502         DFP_HANDLE_EXCEPTIONS (ieee_flags);
503     }
504
505   return f;
506 }
507 #endif
508
509 #if defined (L_unord_sd) || defined (L_unord_dd) || defined (L_unord_td)
510 CMPtype
511 DFP_UNORD (DFP_C_TYPE arg_a, DFP_C_TYPE arg_b)
512 {
513   decNumber arg1, arg2;
514   IEEE_TYPE a, b;
515
516   HOST_TO_IEEE (arg_a, &a);
517   HOST_TO_IEEE (arg_b, &b);
518   TO_INTERNAL (&a, &arg1);
519   TO_INTERNAL (&b, &arg2);
520   return (decNumberIsNaN (&arg1) || decNumberIsNaN (&arg2));
521 }
522 #endif /* L_unord_sd || L_unord_dd || L_unord_td */
523
524 /* !(L_sd_to_tf || L_dd_to_tf || L_td_to_tf \
525      || L_tf_to_sd || L_tf_to_dd || L_tf_to_td)  */
526 #endif