decPacked.c revision 1.1 1 1.1 mrg /* Packed decimal conversion module for the decNumber C Library.
2 1.1 mrg Copyright (C) 2007, 2009 Free Software Foundation, Inc.
3 1.1 mrg Contributed by IBM Corporation. Author Mike Cowlishaw.
4 1.1 mrg
5 1.1 mrg This file is part of GCC.
6 1.1 mrg
7 1.1 mrg GCC is free software; you can redistribute it and/or modify it under
8 1.1 mrg the terms of the GNU General Public License as published by the Free
9 1.1 mrg Software Foundation; either version 3, or (at your option) any later
10 1.1 mrg version.
11 1.1 mrg
12 1.1 mrg GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 1.1 mrg WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 1.1 mrg FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 1.1 mrg for more details.
16 1.1 mrg
17 1.1 mrg Under Section 7 of GPL version 3, you are granted additional
18 1.1 mrg permissions described in the GCC Runtime Library Exception, version
19 1.1 mrg 3.1, as published by the Free Software Foundation.
20 1.1 mrg
21 1.1 mrg You should have received a copy of the GNU General Public License and
22 1.1 mrg a copy of the GCC Runtime Library Exception along with this program;
23 1.1 mrg see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
24 1.1 mrg <http://www.gnu.org/licenses/>. */
25 1.1 mrg
26 1.1 mrg /* ------------------------------------------------------------------ */
27 1.1 mrg /* Packed Decimal conversion module */
28 1.1 mrg /* ------------------------------------------------------------------ */
29 1.1 mrg /* This module comprises the routines for Packed Decimal format */
30 1.1 mrg /* numbers. Conversions are supplied to and from decNumber, which in */
31 1.1 mrg /* turn supports: */
32 1.1 mrg /* conversions to and from string */
33 1.1 mrg /* arithmetic routines */
34 1.1 mrg /* utilities. */
35 1.1 mrg /* Conversions from decNumber to and from densely packed decimal */
36 1.1 mrg /* formats are provided by the decimal32 through decimal128 modules. */
37 1.1 mrg /* ------------------------------------------------------------------ */
38 1.1 mrg
39 1.1 mrg #include <string.h> /* for NULL */
40 1.1 mrg #include "decNumber.h" /* base number library */
41 1.1 mrg #include "decPacked.h" /* packed decimal */
42 1.1 mrg #include "decNumberLocal.h" /* decNumber local types, etc. */
43 1.1 mrg
44 1.1 mrg /* ------------------------------------------------------------------ */
45 1.1 mrg /* decPackedFromNumber -- convert decNumber to BCD Packed Decimal */
46 1.1 mrg /* */
47 1.1 mrg /* bcd is the BCD bytes */
48 1.1 mrg /* length is the length of the BCD array */
49 1.1 mrg /* scale is the scale result */
50 1.1 mrg /* dn is the decNumber */
51 1.1 mrg /* returns bcd, or NULL if error */
52 1.1 mrg /* */
53 1.1 mrg /* The number is converted to a BCD packed decimal byte array, */
54 1.1 mrg /* right aligned in the bcd array, whose length is indicated by the */
55 1.1 mrg /* second parameter. The final 4-bit nibble in the array will be a */
56 1.1 mrg /* sign nibble, C (1100) for + and D (1101) for -. Unused bytes and */
57 1.1 mrg /* nibbles to the left of the number are set to 0. */
58 1.1 mrg /* */
59 1.1 mrg /* scale is set to the scale of the number (this is the exponent, */
60 1.1 mrg /* negated). To force the number to a specified scale, first use the */
61 1.1 mrg /* decNumberRescale routine, which will round and change the exponent */
62 1.1 mrg /* as necessary. */
63 1.1 mrg /* */
64 1.1 mrg /* If there is an error (that is, the decNumber has too many digits */
65 1.1 mrg /* to fit in length bytes, or it is a NaN or Infinity), NULL is */
66 1.1 mrg /* returned and the bcd and scale results are unchanged. Otherwise */
67 1.1 mrg /* bcd is returned. */
68 1.1 mrg /* ------------------------------------------------------------------ */
69 1.1 mrg uByte * decPackedFromNumber(uByte *bcd, Int length, Int *scale,
70 1.1 mrg const decNumber *dn) {
71 1.1 mrg const Unit *up=dn->lsu; /* Unit array pointer */
72 1.1 mrg uByte obyte, *out; /* current output byte, and where it goes */
73 1.1 mrg Int indigs=dn->digits; /* digits processed */
74 1.1 mrg uInt cut=DECDPUN; /* downcounter per Unit */
75 1.1 mrg uInt u=*up; /* work */
76 1.1 mrg uInt nib; /* .. */
77 1.1 mrg #if DECDPUN<=4
78 1.1 mrg uInt temp; /* .. */
79 1.1 mrg #endif
80 1.1 mrg
81 1.1 mrg if (dn->digits>length*2-1 /* too long .. */
82 1.1 mrg ||(dn->bits & DECSPECIAL)) return NULL; /* .. or special -- hopeless */
83 1.1 mrg
84 1.1 mrg if (dn->bits&DECNEG) obyte=DECPMINUS; /* set the sign .. */
85 1.1 mrg else obyte=DECPPLUS;
86 1.1 mrg *scale=-dn->exponent; /* .. and scale */
87 1.1 mrg
88 1.1 mrg /* loop from lowest (rightmost) byte */
89 1.1 mrg out=bcd+length-1; /* -> final byte */
90 1.1 mrg for (; out>=bcd; out--) {
91 1.1 mrg if (indigs>0) {
92 1.1 mrg if (cut==0) {
93 1.1 mrg up++;
94 1.1 mrg u=*up;
95 1.1 mrg cut=DECDPUN;
96 1.1 mrg }
97 1.1 mrg #if DECDPUN<=4
98 1.1 mrg temp=(u*6554)>>16; /* fast /10 */
99 1.1 mrg nib=u-X10(temp);
100 1.1 mrg u=temp;
101 1.1 mrg #else
102 1.1 mrg nib=u%10; /* cannot use *6554 trick :-( */
103 1.1 mrg u=u/10;
104 1.1 mrg #endif
105 1.1 mrg obyte|=(nib<<4);
106 1.1 mrg indigs--;
107 1.1 mrg cut--;
108 1.1 mrg }
109 1.1 mrg *out=obyte;
110 1.1 mrg obyte=0; /* assume 0 */
111 1.1 mrg if (indigs>0) {
112 1.1 mrg if (cut==0) {
113 1.1 mrg up++;
114 1.1 mrg u=*up;
115 1.1 mrg cut=DECDPUN;
116 1.1 mrg }
117 1.1 mrg #if DECDPUN<=4
118 1.1 mrg temp=(u*6554)>>16; /* as above */
119 1.1 mrg obyte=(uByte)(u-X10(temp));
120 1.1 mrg u=temp;
121 1.1 mrg #else
122 1.1 mrg obyte=(uByte)(u%10);
123 1.1 mrg u=u/10;
124 1.1 mrg #endif
125 1.1 mrg indigs--;
126 1.1 mrg cut--;
127 1.1 mrg }
128 1.1 mrg } /* loop */
129 1.1 mrg
130 1.1 mrg return bcd;
131 1.1 mrg } /* decPackedFromNumber */
132 1.1 mrg
133 1.1 mrg /* ------------------------------------------------------------------ */
134 1.1 mrg /* decPackedToNumber -- convert BCD Packed Decimal to a decNumber */
135 1.1 mrg /* */
136 1.1 mrg /* bcd is the BCD bytes */
137 1.1 mrg /* length is the length of the BCD array */
138 1.1 mrg /* scale is the scale associated with the BCD integer */
139 1.1 mrg /* dn is the decNumber [with space for length*2 digits] */
140 1.1 mrg /* returns dn, or NULL if error */
141 1.1 mrg /* */
142 1.1 mrg /* The BCD packed decimal byte array, together with an associated */
143 1.1 mrg /* scale, is converted to a decNumber. The BCD array is assumed full */
144 1.1 mrg /* of digits, and must be ended by a 4-bit sign nibble in the least */
145 1.1 mrg /* significant four bits of the final byte. */
146 1.1 mrg /* */
147 1.1 mrg /* The scale is used (negated) as the exponent of the decNumber. */
148 1.1 mrg /* Note that zeros may have a sign and/or a scale. */
149 1.1 mrg /* */
150 1.1 mrg /* The decNumber structure is assumed to have sufficient space to */
151 1.1 mrg /* hold the converted number (that is, up to length*2-1 digits), so */
152 1.1 mrg /* no error is possible unless the adjusted exponent is out of range, */
153 1.1 mrg /* no sign nibble was found, or a sign nibble was found before the */
154 1.1 mrg /* final nibble. In these error cases, NULL is returned and the */
155 1.1 mrg /* decNumber will be 0. */
156 1.1 mrg /* ------------------------------------------------------------------ */
157 1.1 mrg decNumber * decPackedToNumber(const uByte *bcd, Int length,
158 1.1 mrg const Int *scale, decNumber *dn) {
159 1.1 mrg const uByte *last=bcd+length-1; /* -> last byte */
160 1.1 mrg const uByte *first; /* -> first non-zero byte */
161 1.1 mrg uInt nib; /* work nibble */
162 1.1 mrg Unit *up=dn->lsu; /* output pointer */
163 1.1 mrg Int digits; /* digits count */
164 1.1 mrg Int cut=0; /* phase of output */
165 1.1 mrg
166 1.1 mrg decNumberZero(dn); /* default result */
167 1.1 mrg last=&bcd[length-1];
168 1.1 mrg nib=*last & 0x0f; /* get the sign */
169 1.1 mrg if (nib==DECPMINUS || nib==DECPMINUSALT) dn->bits=DECNEG;
170 1.1 mrg else if (nib<=9) return NULL; /* not a sign nibble */
171 1.1 mrg
172 1.1 mrg /* skip leading zero bytes [final byte is always non-zero, due to sign] */
173 1.1 mrg for (first=bcd; *first==0;) first++;
174 1.1 mrg digits=(last-first)*2+1; /* calculate digits .. */
175 1.1 mrg if ((*first & 0xf0)==0) digits--; /* adjust for leading zero nibble */
176 1.1 mrg if (digits!=0) dn->digits=digits; /* count of actual digits [if 0, */
177 1.1 mrg /* leave as 1] */
178 1.1 mrg
179 1.1 mrg /* check the adjusted exponent; note that scale could be unbounded */
180 1.1 mrg dn->exponent=-*scale; /* set the exponent */
181 1.1 mrg if (*scale>=0) { /* usual case */
182 1.1 mrg if ((dn->digits-*scale-1)<-DECNUMMAXE) { /* underflow */
183 1.1 mrg decNumberZero(dn);
184 1.1 mrg return NULL;}
185 1.1 mrg }
186 1.1 mrg else { /* -ve scale; +ve exponent */
187 1.1 mrg /* need to be careful to avoid wrap, here, also BADINT case */
188 1.1 mrg if ((*scale<-DECNUMMAXE) /* overflow even without digits */
189 1.1 mrg || ((dn->digits-*scale-1)>DECNUMMAXE)) { /* overflow */
190 1.1 mrg decNumberZero(dn);
191 1.1 mrg return NULL;}
192 1.1 mrg }
193 1.1 mrg if (digits==0) return dn; /* result was zero */
194 1.1 mrg
195 1.1 mrg /* copy the digits to the number's units, starting at the lsu */
196 1.1 mrg /* [unrolled] */
197 1.1 mrg for (;;) { /* forever */
198 1.1 mrg /* left nibble first */
199 1.1 mrg nib=(unsigned)(*last & 0xf0)>>4;
200 1.1 mrg /* got a digit, in nib */
201 1.1 mrg if (nib>9) {decNumberZero(dn); return NULL;}
202 1.1 mrg
203 1.1 mrg if (cut==0) *up=(Unit)nib;
204 1.1 mrg else *up=(Unit)(*up+nib*DECPOWERS[cut]);
205 1.1 mrg digits--;
206 1.1 mrg if (digits==0) break; /* got them all */
207 1.1 mrg cut++;
208 1.1 mrg if (cut==DECDPUN) {
209 1.1 mrg up++;
210 1.1 mrg cut=0;
211 1.1 mrg }
212 1.1 mrg last--; /* ready for next */
213 1.1 mrg nib=*last & 0x0f; /* get right nibble */
214 1.1 mrg if (nib>9) {decNumberZero(dn); return NULL;}
215 1.1 mrg
216 1.1 mrg /* got a digit, in nib */
217 1.1 mrg if (cut==0) *up=(Unit)nib;
218 1.1 mrg else *up=(Unit)(*up+nib*DECPOWERS[cut]);
219 1.1 mrg digits--;
220 1.1 mrg if (digits==0) break; /* got them all */
221 1.1 mrg cut++;
222 1.1 mrg if (cut==DECDPUN) {
223 1.1 mrg up++;
224 1.1 mrg cut=0;
225 1.1 mrg }
226 1.1 mrg } /* forever */
227 1.1 mrg
228 1.1 mrg return dn;
229 1.1 mrg } /* decPackedToNumber */
230 1.1 mrg
231