1220747Snwhitehorn/* Decimal 64-bit format module for the decNumber C Library
2251843Sbapt   Copyright (C) 2005 Free Software Foundation, Inc.
3220747Snwhitehorn   Contributed by IBM Corporation.  Author Mike Cowlishaw.
4251843Sbapt
5220747Snwhitehorn   This file is part of GCC.
6220747Snwhitehorn
7220747Snwhitehorn   GCC is free software; you can redistribute it and/or modify it under
8220747Snwhitehorn   the terms of the GNU General Public License as published by the Free
9220747Snwhitehorn   Software Foundation; either version 2, or (at your option) any later
10220747Snwhitehorn   version.
11220747Snwhitehorn
12220747Snwhitehorn   In addition to the permissions in the GNU General Public License,
13220747Snwhitehorn   the Free Software Foundation gives you unlimited permission to link
14220747Snwhitehorn   the compiled version of this file into combinations with other
15220747Snwhitehorn   programs, and to distribute those combinations without any
16220747Snwhitehorn   restriction coming from the use of this file.  (The General Public
17220747Snwhitehorn   License restrictions do apply in other respects; for example, they
18220747Snwhitehorn   cover modification of the file, and distribution when not linked
19220747Snwhitehorn   into a combine executable.)
20220747Snwhitehorn
21220747Snwhitehorn   GCC is distributed in the hope that it will be useful, but WITHOUT ANY
22220747Snwhitehorn   WARRANTY; without even the implied warranty of MERCHANTABILITY or
23220747Snwhitehorn   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
24220747Snwhitehorn   for more details.
25220747Snwhitehorn
26220747Snwhitehorn   You should have received a copy of the GNU General Public License
27220747Snwhitehorn   along with GCC; see the file COPYING.  If not, write to the Free
28220747Snwhitehorn   Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
29220747Snwhitehorn   02110-1301, USA.  */
30220747Snwhitehorn
31220747Snwhitehorn/* ------------------------------------------------------------------ */
32220747Snwhitehorn/* This module comprises the routines for decimal64 format numbers.   */
33220747Snwhitehorn/* Conversions are supplied to and from decNumber and String.         */
34220747Snwhitehorn/*                                                                    */
35220747Snwhitehorn/* No arithmetic routines are included; decNumber provides these.     */
36220747Snwhitehorn/*                                                                    */
37220747Snwhitehorn/* Error handling is the same as decNumber (qv.).                     */
38220747Snwhitehorn/* ------------------------------------------------------------------ */
39220747Snwhitehorn#include <string.h>		/* [for memset/memcpy] */
40220747Snwhitehorn#include <stdio.h>		/* [for printf] */
41220747Snwhitehorn
42220747Snwhitehorn#define  DECNUMDIGITS 16	/* we need decNumbers with space for 16 */
43220747Snwhitehorn#include "config.h"
44220747Snwhitehorn#include "decNumber.h"		/* base number library */
45220747Snwhitehorn#include "decNumberLocal.h"	/* decNumber local types, etc. */
46220747Snwhitehorn#include "decimal64.h"		/* our primary include */
47220747Snwhitehorn#include "decUtility.h"		/* utility routines */
48220747Snwhitehorn
49220747Snwhitehorn#if DECTRACE || DECCHECK
50220747Snwhitehornvoid decimal64Show (const decimal64 *);	/* for debug */
51220747Snwhitehornvoid decNumberShow (const decNumber *);	/* .. */
52220747Snwhitehorn#endif
53220747Snwhitehorn
54220747Snwhitehorn/* Useful macro */
55220747Snwhitehorn/* Clear a structure (e.g., a decNumber) */
56220747Snwhitehorn#define DEC_clear(d) memset(d, 0, sizeof(*d))
57251843Sbapt
58220747Snwhitehorn/* ------------------------------------------------------------------ */
59220747Snwhitehorn/* decimal64FromNumber -- convert decNumber to decimal64              */
60220747Snwhitehorn/*                                                                    */
61/*   ds is the target decimal64                                       */
62/*   dn is the source number (assumed valid)                          */
63/*   set is the context, used only for reporting errors               */
64/*                                                                    */
65/* The set argument is used only for status reporting and for the     */
66/* rounding mode (used if the coefficient is more than DECIMAL64_Pmax */
67/* digits or an overflow is detected).  If the exponent is out of the */
68/* valid range then Overflow or Underflow will be raised.             */
69/* After Underflow a subnormal result is possible.                    */
70/*                                                                    */
71/* DEC_Clamped is set if the number has to be 'folded down' to fit,   */
72/* by reducing its exponent and multiplying the coefficient by a      */
73/* power of ten, or if the exponent on a zero had to be clamped.      */
74/* ------------------------------------------------------------------ */
75decimal64 *
76decimal64FromNumber (decimal64 * d64, const decNumber * dn, decContext * set)
77{
78  uInt status = 0;		/* status accumulator */
79  Int pad = 0;			/* coefficient pad digits */
80  decNumber dw;			/* work */
81  decContext dc;		/* .. */
82  uByte isneg = dn->bits & DECNEG;	/* non-0 if original sign set */
83  uInt comb, exp;		/* work */
84
85  /* If the number is finite, and has too many digits, or the exponent */
86  /* could be out of range then we reduce the number under the */
87  /* appropriate constraints */
88  if (!(dn->bits & DECSPECIAL))
89    {				/* not a special value */
90      Int ae = dn->exponent + dn->digits - 1;	/* adjusted exponent */
91      if (dn->digits > DECIMAL64_Pmax	/* too many digits */
92	  || ae > DECIMAL64_Emax	/* likely overflow */
93	  || ae < DECIMAL64_Emin)
94	{			/* likely underflow */
95	  decContextDefault (&dc, DEC_INIT_DECIMAL64);	/* [no traps] */
96	  dc.round = set->round;	/* use supplied rounding */
97	  decNumberPlus (&dw, dn, &dc);	/* (round and check) */
98	  /* [this changes -0 to 0, but it will be restored below] */
99	  status |= dc.status;	/* save status */
100	  dn = &dw;		/* use the work number */
101	}
102      /* [this could have pushed number to Infinity or zero, so this */
103      /* rounding must be done before we generate the decimal64] */
104    }
105
106  DEC_clear (d64);		/* clean the target */
107  if (dn->bits & DECSPECIAL)
108    {				/* a special value */
109      uByte top;		/* work */
110      if (dn->bits & DECINF)
111	top = DECIMAL_Inf;
112      else
113	{			/* sNaN or qNaN */
114	  if ((*dn->lsu != 0 || dn->digits > 1)	/* non-zero coefficient */
115	      && (dn->digits < DECIMAL64_Pmax))
116	    {			/* coefficient fits */
117	      decDensePackCoeff (dn, d64->bytes, sizeof (d64->bytes), 0);
118	    }
119	  if (dn->bits & DECNAN)
120	    top = DECIMAL_NaN;
121	  else
122	    top = DECIMAL_sNaN;
123	}
124      d64->bytes[0] = top;
125    }
126  else if (decNumberIsZero (dn))
127    {				/* a zero */
128      /* set and clamp exponent */
129      if (dn->exponent < -DECIMAL64_Bias)
130	{
131	  exp = 0;
132	  status |= DEC_Clamped;
133	}
134      else
135	{
136	  exp = dn->exponent + DECIMAL64_Bias;	/* bias exponent */
137	  if (exp > DECIMAL64_Ehigh)
138	    {			/* top clamp */
139	      exp = DECIMAL64_Ehigh;
140	      status |= DEC_Clamped;
141	    }
142	}
143      comb = (exp >> 5) & 0x18;	/* combination field */
144      d64->bytes[0] = (uByte) (comb << 2);
145      exp &= 0xff;		/* remaining exponent bits */
146      decimal64SetExpCon (d64, exp);
147    }
148  else
149    {				/* non-zero finite number */
150      uInt msd;			/* work */
151
152      /* we have a dn that fits, but it may need to be padded */
153      exp = (uInt) (dn->exponent + DECIMAL64_Bias);	/* bias exponent */
154      if (exp > DECIMAL64_Ehigh)
155	{			/* fold-down case */
156	  pad = exp - DECIMAL64_Ehigh;
157	  exp = DECIMAL64_Ehigh;	/* [to maximum] */
158	  status |= DEC_Clamped;
159	}
160
161      decDensePackCoeff (dn, d64->bytes, sizeof (d64->bytes), pad);
162
163      /* save and clear the top digit */
164      msd = ((unsigned) d64->bytes[1] >> 2) & 0x0f;
165      d64->bytes[1] &= 0x03;
166      /* create the combination field */
167      if (msd >= 8)
168	comb = 0x18 | (msd & 0x01) | ((exp >> 7) & 0x06);
169      else
170	comb = (msd & 0x07) | ((exp >> 5) & 0x18);
171      d64->bytes[0] = (uByte) (comb << 2);
172      exp &= 0xff;		/* remaining exponent bits */
173      decimal64SetExpCon (d64, exp);
174    }
175
176  if (isneg)
177    decimal64SetSign (d64, 1);
178  if (status != 0)
179    decContextSetStatus (set, status);	/* pass on status */
180
181  /*decimal64Show(d64); */
182  return d64;
183}
184
185/* ------------------------------------------------------------------ */
186/* decimal64ToNumber -- convert decimal64 to decNumber                */
187/*   d64 is the source decimal64                                      */
188/*   dn is the target number, with appropriate space                  */
189/* No error is possible.                                              */
190/* ------------------------------------------------------------------ */
191decNumber *
192decimal64ToNumber (const decimal64 * d64, decNumber * dn)
193{
194  uInt msd;			/* coefficient MSD */
195  decimal64 wk;			/* working copy, if needed */
196  uInt top = d64->bytes[0] & 0x7f;	/* top byte, less sign bit */
197  decNumberZero (dn);		/* clean target */
198  /* set the sign if negative */
199  if (decimal64Sign (d64))
200    dn->bits = DECNEG;
201
202  if (top >= 0x78)
203    {				/* is a special */
204      if ((top & 0x7c) == (DECIMAL_Inf & 0x7c))
205	dn->bits |= DECINF;
206      else if ((top & 0x7e) == (DECIMAL_NaN & 0x7e))
207	dn->bits |= DECNAN;
208      else
209	dn->bits |= DECSNAN;
210      msd = 0;			/* no top digit */
211    }
212  else
213    {				/* have a finite number */
214      uInt comb = top >> 2;	/* combination field */
215      uInt exp;			/* exponent */
216
217      if (comb >= 0x18)
218	{
219	  msd = 8 + (comb & 0x01);
220	  exp = (comb & 0x06) << 7;	/* MSBs */
221	}
222      else
223	{
224	  msd = comb & 0x07;
225	  exp = (comb & 0x18) << 5;
226	}
227      dn->exponent = exp + decimal64ExpCon (d64) - DECIMAL64_Bias;	/* remove bias */
228    }
229
230  /* get the coefficient, unless infinite */
231  if (!(dn->bits & DECINF))
232    {
233      Int bunches = DECIMAL64_Pmax / 3;	/* coefficient full bunches to convert */
234      Int odd = 0;		/* assume MSD is 0 (no odd bunch) */
235      if (msd != 0)
236	{			/* coefficient has leading non-0 digit */
237	  /* make a copy of the decimal64, with an extra bunch which has */
238	  /* the top digit ready for conversion */
239	  wk = *d64;		/* take a copy */
240	  wk.bytes[0] = 0;	/* clear all but coecon */
241	  wk.bytes[1] &= 0x03;	/* .. */
242	  wk.bytes[1] |= (msd << 2);	/* and prefix MSD */
243	  odd++;		/* indicate the extra */
244	  d64 = &wk;		/* use the work copy */
245	}
246      decDenseUnpackCoeff (d64->bytes, sizeof (d64->bytes), dn, bunches, odd);
247    }
248  return dn;
249}
250
251/* ------------------------------------------------------------------ */
252/* to-scientific-string -- conversion to numeric string               */
253/* to-engineering-string -- conversion to numeric string              */
254/*                                                                    */
255/*   decimal64ToString(d64, string);                                  */
256/*   decimal64ToEngString(d64, string);                               */
257/*                                                                    */
258/*  d64 is the decimal64 format number to convert                     */
259/*  string is the string where the result will be laid out            */
260/*                                                                    */
261/*  string must be at least 24 characters                             */
262/*                                                                    */
263/*  No error is possible, and no status can be set.                   */
264/* ------------------------------------------------------------------ */
265char *
266decimal64ToString (const decimal64 * d64, char *string)
267{
268  decNumber dn;			/* work */
269  decimal64ToNumber (d64, &dn);
270  decNumberToString (&dn, string);
271  return string;
272}
273
274char *
275decimal64ToEngString (const decimal64 * d64, char *string)
276{
277  decNumber dn;			/* work */
278  decimal64ToNumber (d64, &dn);
279  decNumberToEngString (&dn, string);
280  return string;
281}
282
283/* ------------------------------------------------------------------ */
284/* to-number -- conversion from numeric string                        */
285/*                                                                    */
286/*   decimal64FromString(result, string, set);                        */
287/*                                                                    */
288/*  result  is the decimal64 format number which gets the result of   */
289/*          the conversion                                            */
290/*  *string is the character string which should contain a valid      */
291/*          number (which may be a special value)                     */
292/*  set     is the context                                            */
293/*                                                                    */
294/* The context is supplied to this routine is used for error handling */
295/* (setting of status and traps) and for the rounding mode, only.     */
296/* If an error occurs, the result will be a valid decimal64 NaN.      */
297/* ------------------------------------------------------------------ */
298decimal64 *
299decimal64FromString (decimal64 * result, const char *string, decContext * set)
300{
301  decContext dc;		/* work */
302  decNumber dn;			/* .. */
303
304  decContextDefault (&dc, DEC_INIT_DECIMAL64);	/* no traps, please */
305  dc.round = set->round;	/* use supplied rounding */
306
307  decNumberFromString (&dn, string, &dc);	/* will round if needed */
308
309  decimal64FromNumber (result, &dn, &dc);
310  if (dc.status != 0)
311    {				/* something happened */
312      decContextSetStatus (set, dc.status);	/* .. pass it on */
313    }
314  return result;
315}
316
317#if DECTRACE || DECCHECK
318/* ------------------------------------------------------------------ */
319/* decimal64Show -- display a single in hexadecimal [debug aid]       */
320/*   d64 -- the number to show                                        */
321/* ------------------------------------------------------------------ */
322/* Also shows sign/cob/expconfields extracted */
323void
324decimal64Show (const decimal64 * d64)
325{
326  char buf[DECIMAL64_Bytes * 2 + 1];
327  Int i, j;
328  j = 0;
329  for (i = 0; i < DECIMAL64_Bytes; i++)
330    {
331      sprintf (&buf[j], "%02x", d64->bytes[i]);
332      j = j + 2;
333    }
334  printf (" D64> %s [S:%d Cb:%02x E:%d]\n", buf,
335	  decimal64Sign (d64), decimal64Comb (d64), decimal64ExpCon (d64));
336}
337#endif
338