xref: /openbsd/gnu/gcc/libdecnumber/decUtility.c (revision d415bd75)
1 /* Utility functions for decimal floating point support via decNumber.
2    Copyright (C) 2005 Free Software Foundation, Inc.
3    Contributed by IBM Corporation.  Author Mike Cowlishaw.
4 
5    This file is part of GCC.
6 
7    GCC is free software; you can redistribute it and/or modify it under
8    the terms of the GNU General Public License as published by the Free
9    Software Foundation; either version 2, or (at your option) any later
10    version.
11 
12    In addition to the permissions in the GNU General Public License,
13    the Free Software Foundation gives you unlimited permission to link
14    the compiled version of this file into combinations with other
15    programs, and to distribute those combinations without any
16    restriction coming from the use of this file.  (The General Public
17    License restrictions do apply in other respects; for example, they
18    cover modification of the file, and distribution when not linked
19    into a combine executable.)
20 
21    GCC is distributed in the hope that it will be useful, but WITHOUT ANY
22    WARRANTY; without even the implied warranty of MERCHANTABILITY or
23    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
24    for more details.
25 
26    You should have received a copy of the GNU General Public License
27    along with GCC; see the file COPYING.  If not, write to the Free
28    Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
29    02110-1301, USA.  */
30 
31 #include "config.h"
32 #include "decNumber.h"          /* base number library */
33 #include "decNumberLocal.h"     /* decNumber local types, etc. */
34 #include "decUtility.h"         /* utility routines */
35 
36 /* ================================================================== */
37 /* Shared utility routines                                            */
38 /* ================================================================== */
39 
40 /* define and include the conversion tables to use */
41 #define DEC_BIN2DPD 1		/* used for all sizes */
42 #if DECDPUN==3
43 #define DEC_DPD2BIN 1
44 #else
45 #define DEC_DPD2BCD 1
46 #endif
47 #include "decDPD.h"		/* lookup tables */
48 
49 /* The maximum number of decNumberUnits we need for a working copy of */
50 /* the units array is the ceiling of digits/DECDPUN, where digits is */
51 /* the maximum number of digits in any of the formats for which this */
52 /* is used.  We do not want to include decimal128.h, so, as a very */
53 /* special case, that number is defined here. */
54 #define DECMAX754   34
55 #define DECMAXUNITS ((DECMAX754+DECDPUN-1)/DECDPUN)
56 
57 /* ------------------------------------------------------------------ */
58 /* decDensePackCoeff -- densely pack coefficient into DPD form        */
59 /*                                                                    */
60 /*   dn is the source number (assumed valid, max DECMAX754 digits)    */
61 /*   bytes is the target's byte array                                 */
62 /*   len is length of target format's byte array                      */
63 /*   shift is the number of 0 digits to add on the right (normally 0) */
64 /*                                                                    */
65 /* The coefficient must be known small enough to fit, and is filled   */
66 /* in from the right (least significant first).  Note that the full   */
67 /* coefficient is copied, including the leading 'odd' digit.  This    */
68 /* digit is retrieved and packed into the combination field by the    */
69 /* caller.                                                            */
70 /*                                                                    */
71 /* shift is used for 'fold-down' padding.                             */
72 /*                                                                    */
73 /* No error is possible.                                              */
74 /* ------------------------------------------------------------------ */
75 void
76 decDensePackCoeff (const decNumber * dn, uByte * bytes, Int len, Int shift)
77 {
78   Int cut;			/* work */
79   Int n;			/* output bunch counter */
80   Int digits = dn->digits;	/* digit countdown */
81   uInt dpd;			/* densely packed decimal value */
82   uInt bin;			/* binary value 0-999 */
83   uByte *bout;			/* -> current output byte */
84   const Unit *inu = dn->lsu;	/* -> current input unit */
85   Unit uar[DECMAXUNITS];	/* working copy of units, iff shifted */
86 #if DECDPUN!=3			/* not fast path */
87   Unit in;			/* current input unit */
88 #endif
89 
90   if (shift != 0)
91     {				/* shift towards most significant required */
92       /* shift the units array to the left by pad digits and copy */
93       /* [this code is a special case of decShiftToMost, which could */
94       /* be used instead if exposed and the array were copied first] */
95       Unit *target, *first;	/* work */
96       const Unit *source;	/* work */
97       uInt next = 0;		/* work */
98 
99       source = dn->lsu + D2U (digits) - 1;	/* where msu comes from */
100       first = uar + D2U (digits + shift) - 1;	/* where msu will end up */
101       target = uar + D2U (digits) - 1 + D2U (shift);	/* where upper part of first cut goes */
102 
103       cut = (DECDPUN - shift % DECDPUN) % DECDPUN;
104       for (; source >= dn->lsu; source--, target--)
105 	{
106 	  /* split the source Unit and accumulate remainder for next */
107 	  uInt rem = *source % powers[cut];
108 	  next += *source / powers[cut];
109 	  if (target <= first)
110 	    *target = (Unit) next;	/* write to target iff valid */
111 	  next = rem * powers[DECDPUN - cut];	/* save remainder for next Unit */
112 	}
113       /* propagate remainder to one below and clear the rest */
114       for (; target >= uar; target--)
115 	{
116 	  *target = (Unit) next;
117 	  next = 0;
118 	}
119       digits += shift;		/* add count (shift) of zeros added */
120       inu = uar;		/* use units in working array */
121     }
122 
123   /* densely pack the coefficient into the byte array, starting from
124      the right (optionally padded) */
125   bout = &bytes[len - 1];	/* rightmost result byte for phase */
126 
127 #if DECDPUN!=3			/* not fast path */
128   in = *inu;			/* prime */
129   cut = 0;			/* at lowest digit */
130   bin = 0;			/* [keep compiler quiet] */
131 #endif
132 
133   for (n = 0; digits > 0; n++)
134     {				/* each output bunch */
135 #if DECDPUN==3			/* fast path, 3-at-a-time */
136       bin = *inu;		/* 3 ready for convert */
137       digits -= 3;		/* [may go negative] */
138       inu++;			/* may need another */
139 
140 #else /* must collect digit-by-digit */
141       Unit dig;			/* current digit */
142       Int j;			/* digit-in-bunch count */
143       for (j = 0; j < 3; j++)
144 	{
145 #if DECDPUN<=4
146 	  Unit temp = (Unit) ((uInt) (in * 6554) >> 16);
147 	  dig = (Unit) (in - X10 (temp));
148 	  in = temp;
149 #else
150 	  dig = in % 10;
151 	  in = in / 10;
152 #endif
153 
154 	  if (j == 0)
155 	    bin = dig;
156 	  else if (j == 1)
157 	    bin += X10 (dig);
158 	  else			/* j==2 */
159 	    bin += X100 (dig);
160 
161 	  digits--;
162 	  if (digits == 0)
163 	    break;		/* [also protects *inu below] */
164 	  cut++;
165 	  if (cut == DECDPUN)
166 	    {
167 	      inu++;
168 	      in = *inu;
169 	      cut = 0;
170 	    }
171 	}
172 #endif
173       /* here we have 3 digits in bin, or have used all input digits */
174 
175       dpd = BIN2DPD[bin];
176 
177       /* write bunch (bcd) to byte array */
178       switch (n & 0x03)
179 	{			/* phase 0-3 */
180 	case 0:
181 	  *bout = (uByte) dpd;	/* [top 2 bits truncated] */
182 	  bout--;
183 	  *bout = (uByte) (dpd >> 8);
184 	  break;
185 	case 1:
186 	  *bout |= (uByte) (dpd << 2);
187 	  bout--;
188 	  *bout = (uByte) (dpd >> 6);
189 	  break;
190 	case 2:
191 	  *bout |= (uByte) (dpd << 4);
192 	  bout--;
193 	  *bout = (uByte) (dpd >> 4);
194 	  break;
195 	case 3:
196 	  *bout |= (uByte) (dpd << 6);
197 	  bout--;
198 	  *bout = (uByte) (dpd >> 2);
199 	  bout--;
200 	  break;
201 	}			/* switch */
202     }				/* n bunches */
203   return;
204 }
205 
206 /* ------------------------------------------------------------------ */
207 /* decDenseUnpackCoeff -- unpack a format's coefficient               */
208 /*                                                                    */
209 /*   byte is the source's byte array                                  */
210 /*   len is length of the source's byte array                         */
211 /*   dn is the target number, with 7, 16, or 34-digit space.          */
212 /*   bunches is the count of DPD groups in the decNumber (2, 5, or 11)*/
213 /*   odd is 1 if there is a non-zero leading 10-bit group containing  */
214 /*     a single digit, 0 otherwise                                    */
215 /*                                                                    */
216 /* (This routine works on a copy of the number, if necessary, where   */
217 /* an extra 10-bit group is prefixed to the coefficient continuation  */
218 /* to hold the most significant digit if the latter is non-0.)        */
219 /*                                                                    */
220 /* dn->digits is set, but not the sign or exponent.                   */
221 /* No error is possible [the redundant 888 codes are allowed].        */
222 /* ------------------------------------------------------------------ */
223 void
224 decDenseUnpackCoeff (const uByte * bytes, Int len, decNumber * dn,
225 		     Int bunches, Int odd)
226 {
227   uInt dpd = 0;			/* collector for 10 bits */
228   Int n;			/* counter */
229   const uByte *bin;		/* -> current input byte */
230   Unit *uout = dn->lsu;		/* -> current output unit */
231   Unit out = 0;			/* accumulator */
232   Int cut = 0;			/* power of ten in current unit */
233   Unit *last = uout;		/* will be unit containing msd */
234 #if DECDPUN!=3
235   uInt bcd;			/* BCD result */
236   uInt nibble;			/* work */
237 #endif
238 
239   /* Expand the densely-packed integer, right to left */
240   bin = &bytes[len - 1];	/* next input byte to use */
241   for (n = 0; n < bunches + odd; n++)
242     {				/* N bunches of 10 bits */
243       /* assemble the 10 bits */
244       switch (n & 0x03)
245 	{			/* phase 0-3 */
246 	case 0:
247 	  dpd = *bin;
248 	  bin--;
249 	  dpd |= (*bin & 0x03) << 8;
250 	  break;
251 	case 1:
252 	  dpd = (unsigned) *bin >> 2;
253 	  bin--;
254 	  dpd |= (*bin & 0x0F) << 6;
255 	  break;
256 	case 2:
257 	  dpd = (unsigned) *bin >> 4;
258 	  bin--;
259 	  dpd |= (*bin & 0x3F) << 4;
260 	  break;
261 	case 3:
262 	  dpd = (unsigned) *bin >> 6;
263 	  bin--;
264 	  dpd |= (*bin) << 2;
265 	  bin--;
266 	  break;
267 	}			/*switch */
268 
269 #if DECDPUN==3
270       if (dpd == 0)
271 	*uout = 0;
272       else
273 	{
274 	  *uout = DPD2BIN[dpd];	/* convert 10 bits to binary 0-999 */
275 	  last = uout;		/* record most significant unit */
276 	}
277       uout++;
278 
279 #else /* DECDPUN!=3 */
280       if (dpd == 0)
281 	{			/* fastpath [e.g., leading zeros] */
282 	  cut += 3;
283 	  for (; cut >= DECDPUN;)
284 	    {
285 	      cut -= DECDPUN;
286 	      *uout = out;
287 	      uout++;
288 	      out = 0;
289 	    }
290 	  continue;
291 	}
292       bcd = DPD2BCD[dpd];	/* convert 10 bits to 12 bits BCD */
293       /* now split the 3 BCD nibbles into bytes, and accumulate into units */
294       /* If this is the last bunch and it is an odd one, we only have one */
295       /* nibble to handle [extras could overflow a Unit] */
296       nibble = bcd & 0x000f;
297       if (nibble)
298 	{
299 	  last = uout;
300 	  out = (Unit) (out + nibble * powers[cut]);
301 	}
302       cut++;
303       if (cut == DECDPUN)
304 	{
305 	  *uout = out;
306 	  uout++;
307 	  cut = 0;
308 	  out = 0;
309 	}
310       if (n < bunches)
311 	{
312 	  nibble = bcd & 0x00f0;
313 	  if (nibble)
314 	    {
315 	      nibble >>= 4;
316 	      last = uout;
317 	      out = (Unit) (out + nibble * powers[cut]);
318 	    }
319 	  cut++;
320 	  if (cut == DECDPUN)
321 	    {
322 	      *uout = out;
323 	      uout++;
324 	      cut = 0;
325 	      out = 0;
326 	    }
327 	  nibble = bcd & 0x0f00;
328 	  if (nibble)
329 	    {
330 	      nibble >>= 8;
331 	      last = uout;
332 	      out = (Unit) (out + nibble * powers[cut]);
333 	    }
334 	  cut++;
335 	  if (cut == DECDPUN)
336 	    {
337 	      *uout = out;
338 	      uout++;
339 	      cut = 0;
340 	      out = 0;
341 	    }
342 	}
343 #endif
344     }				/* n */
345   if (cut != 0)
346     *uout = out;		/* write out final unit */
347 
348   /* here, last points to the most significant unit with digits */
349   /* we need to inspect it to get final digits count */
350   dn->digits = (last - dn->lsu) * DECDPUN;	/* floor of digits */
351   for (cut = 0; cut < DECDPUN; cut++)
352     {
353       if (*last < powers[cut])
354 	break;
355       dn->digits++;
356     }
357   if (dn->digits == 0)
358     dn->digits++;		/* zero has one digit */
359   return;
360 }
361