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