gethex.c revision 182709
1112158Sdas/****************************************************************
2112158Sdas
3112158SdasThe author of this software is David M. Gay.
4112158Sdas
5112158SdasCopyright (C) 1998 by Lucent Technologies
6112158SdasAll Rights Reserved
7112158Sdas
8112158SdasPermission to use, copy, modify, and distribute this software and
9112158Sdasits documentation for any purpose and without fee is hereby
10112158Sdasgranted, provided that the above copyright notice appear in all
11112158Sdascopies and that both that the copyright notice and this
12112158Sdaspermission notice and warranty disclaimer appear in supporting
13112158Sdasdocumentation, and that the name of Lucent or any of its entities
14112158Sdasnot be used in advertising or publicity pertaining to
15112158Sdasdistribution of the software without specific, written prior
16112158Sdaspermission.
17112158Sdas
18112158SdasLUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
19112158SdasINCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
20112158SdasIN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
21112158SdasSPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
22112158SdasWHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
23112158SdasIN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
24112158SdasARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
25112158SdasTHIS SOFTWARE.
26112158Sdas
27112158Sdas****************************************************************/
28112158Sdas
29165743Sdas/* Please send bug reports to David M. Gay (dmg at acm dot org,
30165743Sdas * with " at " changed at "@" and " dot " changed to ".").	*/
31112158Sdas
32112158Sdas#include "gdtoaimp.h"
33112158Sdas
34112620Sdas#ifdef USE_LOCALE
35112620Sdas#include "locale.h"
36112620Sdas#endif
37112620Sdas
38112158Sdas int
39112158Sdas#ifdef KR_headers
40112158Sdasgethex(sp, fpi, exp, bp, sign)
41112158Sdas	CONST char **sp; FPI *fpi; Long *exp; Bigint **bp; int sign;
42112158Sdas#else
43112158Sdasgethex( CONST char **sp, FPI *fpi, Long *exp, Bigint **bp, int sign)
44112158Sdas#endif
45112158Sdas{
46112158Sdas	Bigint *b;
47112158Sdas	CONST unsigned char *decpt, *s0, *s, *s1;
48182709Sdas	int big, esign, havedig, irv, j, k, n, n0, nbits, up, zret;
49112158Sdas	ULong L, lostbits, *x;
50112158Sdas	Long e, e1;
51112620Sdas#ifdef USE_LOCALE
52124703Sdas	unsigned char decimalpoint = *localeconv()->decimal_point;
53112620Sdas#else
54112620Sdas#define decimalpoint '.'
55112620Sdas#endif
56112158Sdas
57112158Sdas	if (!hexdig['0'])
58112158Sdas		hexdig_init_D2A();
59182709Sdas	*bp = 0;
60112158Sdas	havedig = 0;
61112158Sdas	s0 = *(CONST unsigned char **)sp + 2;
62112158Sdas	while(s0[havedig] == '0')
63112158Sdas		havedig++;
64112158Sdas	s0 += havedig;
65112158Sdas	s = s0;
66112158Sdas	decpt = 0;
67165743Sdas	zret = 0;
68165743Sdas	e = 0;
69112158Sdas	if (!hexdig[*s]) {
70165743Sdas		zret = 1;
71165743Sdas		if (*s != decimalpoint)
72165743Sdas			goto pcheck;
73165743Sdas		decpt = ++s;
74165743Sdas		if (!hexdig[*s])
75165743Sdas			goto pcheck;
76112158Sdas		while(*s == '0')
77112158Sdas			s++;
78165743Sdas		if (hexdig[*s])
79165743Sdas			zret = 0;
80112158Sdas		havedig = 1;
81112158Sdas		s0 = s;
82112158Sdas		}
83112158Sdas	while(hexdig[*s])
84112158Sdas		s++;
85112620Sdas	if (*s == decimalpoint && !decpt) {
86112158Sdas		decpt = ++s;
87112158Sdas		while(hexdig[*s])
88112158Sdas			s++;
89112158Sdas		}
90112158Sdas	if (decpt)
91112158Sdas		e = -(((Long)(s-decpt)) << 2);
92165743Sdas pcheck:
93112158Sdas	s1 = s;
94182709Sdas	big = esign = 0;
95112158Sdas	switch(*s) {
96112158Sdas	  case 'p':
97112158Sdas	  case 'P':
98112158Sdas		switch(*++s) {
99112158Sdas		  case '-':
100112158Sdas			esign = 1;
101112158Sdas			/* no break */
102112158Sdas		  case '+':
103112158Sdas			s++;
104112158Sdas		  }
105112158Sdas		if ((n = hexdig[*s]) == 0 || n > 0x19) {
106112158Sdas			s = s1;
107112158Sdas			break;
108112158Sdas			}
109112158Sdas		e1 = n - 0x10;
110182709Sdas		while((n = hexdig[*++s]) !=0 && n <= 0x19) {
111182709Sdas			if (e1 & 0xf8000000)
112182709Sdas				big = 1;
113112158Sdas			e1 = 10*e1 + n - 0x10;
114182709Sdas			}
115112158Sdas		if (esign)
116112158Sdas			e1 = -e1;
117112158Sdas		e += e1;
118112158Sdas	  }
119112158Sdas	*sp = (char*)s;
120182709Sdas	if (!havedig)
121182709Sdas		*sp = s0 - 1;
122182709Sdas	if (zret)
123179918Sdas		return STRTOG_Zero;
124182709Sdas	if (big) {
125182709Sdas		if (esign) {
126182709Sdas			switch(fpi->rounding) {
127182709Sdas			  case FPI_Round_up:
128182709Sdas				if (sign)
129182709Sdas					break;
130182709Sdas				goto ret_tiny;
131182709Sdas			  case FPI_Round_down:
132182709Sdas				if (!sign)
133182709Sdas					break;
134182709Sdas				goto ret_tiny;
135182709Sdas			  }
136182709Sdas			goto retz;
137182709Sdas ret_tiny:
138182709Sdas			b = Balloc(0);
139182709Sdas			b->wds = 1;
140182709Sdas			b->x[0] = 1;
141182709Sdas			goto dret;
142182709Sdas			}
143182709Sdas		switch(fpi->rounding) {
144182709Sdas		  case FPI_Round_near:
145182709Sdas			goto ovfl1;
146182709Sdas		  case FPI_Round_up:
147182709Sdas			if (!sign)
148182709Sdas				goto ovfl1;
149182709Sdas			goto ret_big;
150182709Sdas		  case FPI_Round_down:
151182709Sdas			if (sign)
152182709Sdas				goto ovfl1;
153182709Sdas			goto ret_big;
154182709Sdas		  }
155182709Sdas ret_big:
156182709Sdas		nbits = fpi->nbits;
157182709Sdas		n0 = n = nbits >> kshift;
158182709Sdas		if (nbits & kmask)
159182709Sdas			++n;
160182709Sdas		for(j = n, k = 0; j >>= 1; ++k);
161182709Sdas		*bp = b = Balloc(k);
162182709Sdas		b->wds = n;
163182709Sdas		for(j = 0; j < n0; ++j)
164182709Sdas			b->x[j] = ALL_ON;
165182709Sdas		if (n > n0)
166182709Sdas			b->x[j] = ULbits >> (ULbits - (nbits & kmask));
167182709Sdas		*exp = fpi->emin;
168182709Sdas		return STRTOG_Normal | STRTOG_Inexlo;
169179918Sdas		}
170112158Sdas	n = s1 - s0 - 1;
171112158Sdas	for(k = 0; n > 7; n >>= 1)
172112158Sdas		k++;
173112158Sdas	b = Balloc(k);
174112158Sdas	x = b->x;
175112158Sdas	n = 0;
176112158Sdas	L = 0;
177112158Sdas	while(s1 > s0) {
178112620Sdas		if (*--s1 == decimalpoint)
179112158Sdas			continue;
180112158Sdas		if (n == 32) {
181112158Sdas			*x++ = L;
182112158Sdas			L = 0;
183112158Sdas			n = 0;
184112158Sdas			}
185112158Sdas		L |= (hexdig[*s1] & 0x0f) << n;
186112158Sdas		n += 4;
187112158Sdas		}
188112158Sdas	*x++ = L;
189112158Sdas	b->wds = n = x - b->x;
190112158Sdas	n = 32*n - hi0bits(L);
191112158Sdas	nbits = fpi->nbits;
192112158Sdas	lostbits = 0;
193112158Sdas	x = b->x;
194112158Sdas	if (n > nbits) {
195112158Sdas		n -= nbits;
196112158Sdas		if (any_on(b,n)) {
197112158Sdas			lostbits = 1;
198112158Sdas			k = n - 1;
199112158Sdas			if (x[k>>kshift] & 1 << (k & kmask)) {
200112158Sdas				lostbits = 2;
201182709Sdas				if (k > 0 && any_on(b,k))
202112158Sdas					lostbits = 3;
203112158Sdas				}
204112158Sdas			}
205112158Sdas		rshift(b, n);
206112158Sdas		e += n;
207112158Sdas		}
208112158Sdas	else if (n < nbits) {
209112158Sdas		n = nbits - n;
210112158Sdas		b = lshift(b, n);
211112158Sdas		e -= n;
212112158Sdas		x = b->x;
213112158Sdas		}
214112158Sdas	if (e > fpi->emax) {
215112158Sdas ovfl:
216112158Sdas		Bfree(b);
217182709Sdas ovfl1:
218182709Sdas#ifndef NO_ERRNO
219182709Sdas		errno = ERANGE;
220182709Sdas#endif
221112158Sdas		return STRTOG_Infinite | STRTOG_Overflow | STRTOG_Inexhi;
222112158Sdas		}
223112158Sdas	irv = STRTOG_Normal;
224112158Sdas	if (e < fpi->emin) {
225112158Sdas		irv = STRTOG_Denormal;
226112158Sdas		n = fpi->emin - e;
227112158Sdas		if (n >= nbits) {
228112158Sdas			switch (fpi->rounding) {
229112158Sdas			  case FPI_Round_near:
230124703Sdas				if (n == nbits && (n < 2 || any_on(b,n-1)))
231112158Sdas					goto one_bit;
232112158Sdas				break;
233112158Sdas			  case FPI_Round_up:
234112158Sdas				if (!sign)
235112158Sdas					goto one_bit;
236112158Sdas				break;
237112158Sdas			  case FPI_Round_down:
238112158Sdas				if (sign) {
239112158Sdas one_bit:
240112158Sdas					x[0] = b->wds = 1;
241182709Sdas dret:
242112158Sdas					*bp = b;
243182709Sdas					*exp = fpi->emin;
244182709Sdas#ifndef NO_ERRNO
245182709Sdas					errno = ERANGE;
246182709Sdas#endif
247112158Sdas					return STRTOG_Denormal | STRTOG_Inexhi
248112158Sdas						| STRTOG_Underflow;
249112158Sdas					}
250112158Sdas			  }
251112158Sdas			Bfree(b);
252182709Sdas retz:
253182709Sdas#ifndef NO_ERRNO
254182709Sdas			errno = ERANGE;
255182709Sdas#endif
256112158Sdas			return STRTOG_Zero | STRTOG_Inexlo | STRTOG_Underflow;
257112158Sdas			}
258112158Sdas		k = n - 1;
259112158Sdas		if (lostbits)
260112158Sdas			lostbits = 1;
261112158Sdas		else if (k > 0)
262112158Sdas			lostbits = any_on(b,k);
263112158Sdas		if (x[k>>kshift] & 1 << (k & kmask))
264112158Sdas			lostbits |= 2;
265112158Sdas		nbits -= n;
266112158Sdas		rshift(b,n);
267112158Sdas		e = fpi->emin;
268112158Sdas		}
269112158Sdas	if (lostbits) {
270112158Sdas		up = 0;
271112158Sdas		switch(fpi->rounding) {
272112158Sdas		  case FPI_Round_zero:
273112158Sdas			break;
274112158Sdas		  case FPI_Round_near:
275112158Sdas			if (lostbits & 2
276112158Sdas			 && (lostbits & 1) | x[0] & 1)
277112158Sdas				up = 1;
278112158Sdas			break;
279112158Sdas		  case FPI_Round_up:
280112158Sdas			up = 1 - sign;
281112158Sdas			break;
282112158Sdas		  case FPI_Round_down:
283112158Sdas			up = sign;
284112158Sdas		  }
285112158Sdas		if (up) {
286112158Sdas			k = b->wds;
287112158Sdas			b = increment(b);
288112158Sdas			x = b->x;
289124703Sdas			if (irv == STRTOG_Denormal) {
290124703Sdas				if (nbits == fpi->nbits - 1
291124703Sdas				 && x[nbits >> kshift] & 1 << (nbits & kmask))
292124703Sdas					irv =  STRTOG_Normal;
293124703Sdas				}
294124703Sdas			else if (b->wds > k
295112158Sdas			 || (n = nbits & kmask) !=0
296112158Sdas			     && hi0bits(x[k-1]) < 32-n) {
297112158Sdas				rshift(b,1);
298112158Sdas				if (++e > fpi->emax)
299112158Sdas					goto ovfl;
300112158Sdas				}
301112158Sdas			irv |= STRTOG_Inexhi;
302112158Sdas			}
303112158Sdas		else
304112158Sdas			irv |= STRTOG_Inexlo;
305112158Sdas		}
306112158Sdas	*bp = b;
307112158Sdas	*exp = e;
308112158Sdas	return irv;
309112158Sdas	}
310