diff options
Diffstat (limited to 'libdecnumber/decUtility.c')
-rw-r--r-- | libdecnumber/decUtility.c | 372 |
1 files changed, 372 insertions, 0 deletions
diff --git a/libdecnumber/decUtility.c b/libdecnumber/decUtility.c new file mode 100644 index 00000000000..75439227bba --- /dev/null +++ b/libdecnumber/decUtility.c @@ -0,0 +1,372 @@ +/* Utility functions for decimal floating point support via decNumber. + Copyright (C) 2005 Free Software Foundation, Inc. + Contributed by IBM Corporation. Author Mike Cowlishaw. + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2, or (at your option) any later + version. + + GCC is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License + along with GCC; see the file COPYING. If not, write to the Free + Software Foundation, 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + +#include "decNumber.h" /* base number library */ +#include "decNumberLocal.h" /* decNumber local types, etc. */ +#include "decUtility.h" /* utility routines */ + +#include "tconfig.h" +#include "coretypes.h" +#include "tm.h" + +/* ================================================================== */ +/* Shared utility routines */ +/* ================================================================== */ + +/* define and include the conversion tables to use */ +#define DEC_BIN2DPD 1 /* used for all sizes */ +#if DECDPUN==3 +#define DEC_DPD2BIN 1 +#else +#define DEC_DPD2BCD 1 +#endif +#include "decDPD.h" /* lookup tables */ + +/* The maximum number of decNumberUnits we need for a working copy of */ +/* the units array is the ceiling of digits/DECDPUN, where digits is */ +/* the maximum number of digits in any of the formats for which this */ +/* is used. We do not want to include decimal128.h, so, as a very */ +/* special case, that number is defined here. */ +#define DECMAX754 34 +#define DECMAXUNITS ((DECMAX754+DECDPUN-1)/DECDPUN) + +/* ------------------------------------------------------------------ */ +/* decDensePackCoeff -- densely pack coefficient into DPD form */ +/* */ +/* dn is the source number (assumed valid, max DECMAX754 digits) */ +/* bytes is the target's byte array */ +/* len is length of target format's byte array */ +/* shift is the number of 0 digits to add on the right (normally 0) */ +/* */ +/* The coefficient must be known small enough to fit, and is filled */ +/* in from the right (least significant first). Note that the full */ +/* coefficient is copied, including the leading 'odd' digit. This */ +/* digit is retrieved and packed into the combination field by the */ +/* caller. */ +/* */ +/* shift is used for 'fold-down' padding. */ +/* */ +/* No error is possible. */ +/* ------------------------------------------------------------------ */ +void +decDensePackCoeff (decNumber * dn, uByte * bytes, Int len, Int shift) +{ + Int cut; /* work */ + Int n; /* output bunch counter */ + Int digits = dn->digits; /* digit countdown */ + uInt dpd; /* densely packed decimal value */ + uInt bin; /* binary value 0-999 */ + uByte *bout; /* -> current output byte */ + Unit *inu = dn->lsu; /* -> current input unit */ + Unit uar[DECMAXUNITS]; /* working copy of units, iff shifted */ +#if DECDPUN!=3 /* not fast path */ + Unit in; /* current input unit */ +#endif + + if (shift != 0) + { /* shift towards most significant required */ + /* shift the units array to the left by pad digits and copy */ + /* [this code is a special case of decShiftToMost, which could */ + /* be used instead if exposed and the array were copied first] */ + Unit *target, *source, *first; /* work */ + uInt next = 0; /* work */ + + source = dn->lsu + D2U (digits) - 1; /* where msu comes from */ + first = uar + D2U (digits + shift) - 1; /* where msu will end up */ + target = uar + D2U (digits) - 1 + D2U (shift); /* where upper part of first cut goes */ + + cut = (DECDPUN - shift % DECDPUN) % DECDPUN; + for (; source >= dn->lsu; source--, target--) + { + /* split the source Unit and accumulate remainder for next */ + uInt rem = *source % powers[cut]; + next += *source / powers[cut]; + if (target <= first) + *target = (Unit) next; /* write to target iff valid */ + next = rem * powers[DECDPUN - cut]; /* save remainder for next Unit */ + } + /* propagate remainder to one below and clear the rest */ + for (; target >= uar; target--) + { + *target = (Unit) next; + next = 0; + } + digits += shift; /* add count (shift) of zeros added */ + inu = uar; /* use units in working array */ + } + + /* densely pack the coefficient into the byte array, starting from + the right (optionally padded) */ + bout = &bytes[len - 1]; /* rightmost result byte for phase */ + +#if DECDPUN!=3 /* not fast path */ + in = *inu; /* prime */ + cut = 0; /* at lowest digit */ + bin = 0; /* [keep compiler quiet] */ +#endif + + for (n = 0; digits > 0; n++) + { /* each output bunch */ +#if DECDPUN==3 /* fast path, 3-at-a-time */ + bin = *inu; /* 3 ready for convert */ + digits -= 3; /* [may go negative] */ + inu++; /* may need another */ + +#else /* must collect digit-by-digit */ + Unit dig; /* current digit */ + Int j; /* digit-in-bunch count */ + for (j = 0; j < 3; j++) + { +#if DECDPUN<=4 + Unit temp = (Unit) ((uInt) (in * 6554) >> 16); + dig = (Unit) (in - X10 (temp)); + in = temp; +#else + dig = in % 10; + in = in / 10; +#endif + + if (j == 0) + bin = dig; + else if (j == 1) + bin += X10 (dig); + else /* j==2 */ + bin += X100 (dig); + + digits--; + if (digits == 0) + break; /* [also protects *inu below] */ + cut++; + if (cut == DECDPUN) + { + inu++; + in = *inu; + cut = 0; + } + } +#endif + /* here we have 3 digits in bin, or have used all input digits */ + + dpd = BIN2DPD[bin]; + + /* write bunch (bcd) to byte array */ + switch (n & 0x03) + { /* phase 0-3 */ + case 0: + *bout = (uByte) dpd; /* [top 2 bits truncated] */ + bout--; + *bout = (uByte) (dpd >> 8); + break; + case 1: + *bout |= (uByte) (dpd << 2); + bout--; + *bout = (uByte) (dpd >> 6); + break; + case 2: + *bout |= (uByte) (dpd << 4); + bout--; + *bout = (uByte) (dpd >> 4); + break; + case 3: + *bout |= (uByte) (dpd << 6); + bout--; + *bout = (uByte) (dpd >> 2); + bout--; + break; + } /* switch */ + } /* n bunches */ + return; +} + +/* ------------------------------------------------------------------ */ +/* decDenseUnpackCoeff -- unpack a format's coefficient */ +/* */ +/* byte is the source's byte array */ +/* len is length of the source's byte array */ +/* dn is the target number, with 7, 16, or 34-digit space. */ +/* bunches is the count of DPD groups in the decNumber (2, 5, or 11)*/ +/* odd is 1 if there is a non-zero leading 10-bit group containing */ +/* a single digit, 0 otherwise */ +/* */ +/* (This routine works on a copy of the number, if necessary, where */ +/* an extra 10-bit group is prefixed to the coefficient continuation */ +/* to hold the most significant digit if the latter is non-0.) */ +/* */ +/* dn->digits is set, but not the sign or exponent. */ +/* No error is possible [the redundant 888 codes are allowed]. */ +/* ------------------------------------------------------------------ */ +void +decDenseUnpackCoeff (uByte * bytes, Int len, decNumber * dn, + Int bunches, Int odd) +{ + uInt dpd = 0; /* collector for 10 bits */ + Int n; /* counter */ + uByte *bin; /* -> current input byte */ + Unit *uout = dn->lsu; /* -> current output unit */ + Unit out = 0; /* accumulator */ + Int cut = 0; /* power of ten in current unit */ + Unit *last = uout; /* will be unit containing msd */ +#if DECDPUN!=3 + uInt bcd; /* BCD result */ + uInt nibble; /* work */ +#endif + + /* Expand the densely-packed integer, right to left */ + bin = &bytes[len - 1]; /* next input byte to use */ + for (n = 0; n < bunches + odd; n++) + { /* N bunches of 10 bits */ + /* assemble the 10 bits */ + switch (n & 0x03) + { /* phase 0-3 */ + case 0: + dpd = *bin; + bin--; + dpd |= (*bin & 0x03) << 8; + break; + case 1: + dpd = (unsigned) *bin >> 2; + bin--; + dpd |= (*bin & 0x0F) << 6; + break; + case 2: + dpd = (unsigned) *bin >> 4; + bin--; + dpd |= (*bin & 0x3F) << 4; + break; + case 3: + dpd = (unsigned) *bin >> 6; + bin--; + dpd |= (*bin) << 2; + bin--; + break; + } /*switch */ + +#if DECDPUN==3 + if (dpd == 0) + *uout = 0; + else + { + *uout = DPD2BIN[dpd]; /* convert 10 bits to binary 0-999 */ + last = uout; /* record most significant unit */ + } + uout++; + +#else /* DECDPUN!=3 */ + if (dpd == 0) + { /* fastpath [e.g., leading zeros] */ + cut += 3; + for (; cut >= DECDPUN;) + { + cut -= DECDPUN; + *uout = out; + uout++; + out = 0; + } + continue; + } + bcd = DPD2BCD[dpd]; /* convert 10 bits to 12 bits BCD */ + /* now split the 3 BCD nibbles into bytes, and accumulate into units */ + /* If this is the last bunch and it is an odd one, we only have one */ + /* nibble to handle [extras could overflow a Unit] */ + nibble = bcd & 0x000f; + if (nibble) + { + last = uout; + out = (Unit) (out + nibble * powers[cut]); + } + cut++; + if (cut == DECDPUN) + { + *uout = out; + uout++; + cut = 0; + out = 0; + } + if (n < bunches) + { + nibble = bcd & 0x00f0; + if (nibble) + { + nibble >>= 4; + last = uout; + out = (Unit) (out + nibble * powers[cut]); + } + cut++; + if (cut == DECDPUN) + { + *uout = out; + uout++; + cut = 0; + out = 0; + } + nibble = bcd & 0x0f00; + if (nibble) + { + nibble >>= 8; + last = uout; + out = (Unit) (out + nibble * powers[cut]); + } + cut++; + if (cut == DECDPUN) + { + *uout = out; + uout++; + cut = 0; + out = 0; + } + } +#endif + } /* n */ + if (cut != 0) + *uout = out; /* write out final unit */ + + /* here, last points to the most significant unit with digits */ + /* we need to inspect it to get final digits count */ + dn->digits = (last - dn->lsu) * DECDPUN; /* floor of digits */ + for (cut = 0; cut < DECDPUN; cut++) + { + if (*last < powers[cut]) + break; + dn->digits++; + } + if (dn->digits == 0) + dn->digits++; /* zero has one digit */ + return; +} + +unsigned long +__dec_byte_swap (unsigned long in) +{ + unsigned long out; + unsigned char *p = (unsigned char *) &out; + union { + unsigned long i; + unsigned char b[4]; + } u; + + u.i = in; + p[0] = u.b[3]; + p[1] = u.b[2]; + p[2] = u.b[1]; + p[3] = u.b[0]; + + return out; +} |