| /* Packed decimal conversion module for the decNumber C Library. | |
| Copyright (C) 2007-2018 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 3, 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. | |
| Under Section 7 of GPL version 3, you are granted additional | |
| permissions described in the GCC Runtime Library Exception, version | |
| 3.1, as published by the Free Software Foundation. | |
| You should have received a copy of the GNU General Public License and | |
| a copy of the GCC Runtime Library Exception along with this program; | |
| see the files COPYING3 and COPYING.RUNTIME respectively. If not, see | |
| <http://www.gnu.org/licenses/>. */ | |
| /* ------------------------------------------------------------------ */ | |
| /* Packed Decimal conversion module */ | |
| /* ------------------------------------------------------------------ */ | |
| /* This module comprises the routines for Packed Decimal format */ | |
| /* numbers. Conversions are supplied to and from decNumber, which in */ | |
| /* turn supports: */ | |
| /* conversions to and from string */ | |
| /* arithmetic routines */ | |
| /* utilities. */ | |
| /* Conversions from decNumber to and from densely packed decimal */ | |
| /* formats are provided by the decimal32 through decimal128 modules. */ | |
| /* ------------------------------------------------------------------ */ | |
| #include <string.h> /* for NULL */ | |
| #include "decNumber.h" /* base number library */ | |
| #include "decPacked.h" /* packed decimal */ | |
| #include "decNumberLocal.h" /* decNumber local types, etc. */ | |
| /* ------------------------------------------------------------------ */ | |
| /* decPackedFromNumber -- convert decNumber to BCD Packed Decimal */ | |
| /* */ | |
| /* bcd is the BCD bytes */ | |
| /* length is the length of the BCD array */ | |
| /* scale is the scale result */ | |
| /* dn is the decNumber */ | |
| /* returns bcd, or NULL if error */ | |
| /* */ | |
| /* The number is converted to a BCD packed decimal byte array, */ | |
| /* right aligned in the bcd array, whose length is indicated by the */ | |
| /* second parameter. The final 4-bit nibble in the array will be a */ | |
| /* sign nibble, C (1100) for + and D (1101) for -. Unused bytes and */ | |
| /* nibbles to the left of the number are set to 0. */ | |
| /* */ | |
| /* scale is set to the scale of the number (this is the exponent, */ | |
| /* negated). To force the number to a specified scale, first use the */ | |
| /* decNumberRescale routine, which will round and change the exponent */ | |
| /* as necessary. */ | |
| /* */ | |
| /* If there is an error (that is, the decNumber has too many digits */ | |
| /* to fit in length bytes, or it is a NaN or Infinity), NULL is */ | |
| /* returned and the bcd and scale results are unchanged. Otherwise */ | |
| /* bcd is returned. */ | |
| /* ------------------------------------------------------------------ */ | |
| uByte * decPackedFromNumber(uByte *bcd, Int length, Int *scale, | |
| const decNumber *dn) { | |
| const Unit *up=dn->lsu; /* Unit array pointer */ | |
| uByte obyte, *out; /* current output byte, and where it goes */ | |
| Int indigs=dn->digits; /* digits processed */ | |
| uInt cut=DECDPUN; /* downcounter per Unit */ | |
| uInt u=*up; /* work */ | |
| uInt nib; /* .. */ | |
| #if DECDPUN<=4 | |
| uInt temp; /* .. */ | |
| #endif | |
| if (dn->digits>length*2-1 /* too long .. */ | |
| ||(dn->bits & DECSPECIAL)) return NULL; /* .. or special -- hopeless */ | |
| if (dn->bits&DECNEG) obyte=DECPMINUS; /* set the sign .. */ | |
| else obyte=DECPPLUS; | |
| *scale=-dn->exponent; /* .. and scale */ | |
| /* loop from lowest (rightmost) byte */ | |
| out=bcd+length-1; /* -> final byte */ | |
| for (; out>=bcd; out--) { | |
| if (indigs>0) { | |
| if (cut==0) { | |
| up++; | |
| u=*up; | |
| cut=DECDPUN; | |
| } | |
| #if DECDPUN<=4 | |
| temp=(u*6554)>>16; /* fast /10 */ | |
| nib=u-X10(temp); | |
| u=temp; | |
| #else | |
| nib=u%10; /* cannot use *6554 trick :-( */ | |
| u=u/10; | |
| #endif | |
| obyte|=(nib<<4); | |
| indigs--; | |
| cut--; | |
| } | |
| *out=obyte; | |
| obyte=0; /* assume 0 */ | |
| if (indigs>0) { | |
| if (cut==0) { | |
| up++; | |
| u=*up; | |
| cut=DECDPUN; | |
| } | |
| #if DECDPUN<=4 | |
| temp=(u*6554)>>16; /* as above */ | |
| obyte=(uByte)(u-X10(temp)); | |
| u=temp; | |
| #else | |
| obyte=(uByte)(u%10); | |
| u=u/10; | |
| #endif | |
| indigs--; | |
| cut--; | |
| } | |
| } /* loop */ | |
| return bcd; | |
| } /* decPackedFromNumber */ | |
| /* ------------------------------------------------------------------ */ | |
| /* decPackedToNumber -- convert BCD Packed Decimal to a decNumber */ | |
| /* */ | |
| /* bcd is the BCD bytes */ | |
| /* length is the length of the BCD array */ | |
| /* scale is the scale associated with the BCD integer */ | |
| /* dn is the decNumber [with space for length*2 digits] */ | |
| /* returns dn, or NULL if error */ | |
| /* */ | |
| /* The BCD packed decimal byte array, together with an associated */ | |
| /* scale, is converted to a decNumber. The BCD array is assumed full */ | |
| /* of digits, and must be ended by a 4-bit sign nibble in the least */ | |
| /* significant four bits of the final byte. */ | |
| /* */ | |
| /* The scale is used (negated) as the exponent of the decNumber. */ | |
| /* Note that zeros may have a sign and/or a scale. */ | |
| /* */ | |
| /* The decNumber structure is assumed to have sufficient space to */ | |
| /* hold the converted number (that is, up to length*2-1 digits), so */ | |
| /* no error is possible unless the adjusted exponent is out of range, */ | |
| /* no sign nibble was found, or a sign nibble was found before the */ | |
| /* final nibble. In these error cases, NULL is returned and the */ | |
| /* decNumber will be 0. */ | |
| /* ------------------------------------------------------------------ */ | |
| decNumber * decPackedToNumber(const uByte *bcd, Int length, | |
| const Int *scale, decNumber *dn) { | |
| const uByte *last=bcd+length-1; /* -> last byte */ | |
| const uByte *first; /* -> first non-zero byte */ | |
| uInt nib; /* work nibble */ | |
| Unit *up=dn->lsu; /* output pointer */ | |
| Int digits; /* digits count */ | |
| Int cut=0; /* phase of output */ | |
| decNumberZero(dn); /* default result */ | |
| last=&bcd[length-1]; | |
| nib=*last & 0x0f; /* get the sign */ | |
| if (nib==DECPMINUS || nib==DECPMINUSALT) dn->bits=DECNEG; | |
| else if (nib<=9) return NULL; /* not a sign nibble */ | |
| /* skip leading zero bytes [final byte is always non-zero, due to sign] */ | |
| for (first=bcd; *first==0;) first++; | |
| digits=(last-first)*2+1; /* calculate digits .. */ | |
| if ((*first & 0xf0)==0) digits--; /* adjust for leading zero nibble */ | |
| if (digits!=0) dn->digits=digits; /* count of actual digits [if 0, */ | |
| /* leave as 1] */ | |
| /* check the adjusted exponent; note that scale could be unbounded */ | |
| dn->exponent=-*scale; /* set the exponent */ | |
| if (*scale>=0) { /* usual case */ | |
| if ((dn->digits-*scale-1)<-DECNUMMAXE) { /* underflow */ | |
| decNumberZero(dn); | |
| return NULL;} | |
| } | |
| else { /* -ve scale; +ve exponent */ | |
| /* need to be careful to avoid wrap, here, also BADINT case */ | |
| if ((*scale<-DECNUMMAXE) /* overflow even without digits */ | |
| || ((dn->digits-*scale-1)>DECNUMMAXE)) { /* overflow */ | |
| decNumberZero(dn); | |
| return NULL;} | |
| } | |
| if (digits==0) return dn; /* result was zero */ | |
| /* copy the digits to the number's units, starting at the lsu */ | |
| /* [unrolled] */ | |
| for (;;) { /* forever */ | |
| /* left nibble first */ | |
| nib=(unsigned)(*last & 0xf0)>>4; | |
| /* got a digit, in nib */ | |
| if (nib>9) {decNumberZero(dn); return NULL;} | |
| if (cut==0) *up=(Unit)nib; | |
| else *up=(Unit)(*up+nib*DECPOWERS[cut]); | |
| digits--; | |
| if (digits==0) break; /* got them all */ | |
| cut++; | |
| if (cut==DECDPUN) { | |
| up++; | |
| cut=0; | |
| } | |
| last--; /* ready for next */ | |
| nib=*last & 0x0f; /* get right nibble */ | |
| if (nib>9) {decNumberZero(dn); return NULL;} | |
| /* got a digit, in nib */ | |
| if (cut==0) *up=(Unit)nib; | |
| else *up=(Unit)(*up+nib*DECPOWERS[cut]); | |
| digits--; | |
| if (digits==0) break; /* got them all */ | |
| cut++; | |
| if (cut==DECDPUN) { | |
| up++; | |
| cut=0; | |
| } | |
| } /* forever */ | |
| return dn; | |
| } /* decPackedToNumber */ | |