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
52187808Sdas	int i;
53187808Sdas#ifdef NO_LOCALE_CACHE
54187808Sdas	const unsigned char *decimalpoint = (unsigned char*)localeconv()->decimal_point;
55112620Sdas#else
56187808Sdas	const unsigned char *decimalpoint;
57187808Sdas	static unsigned char *decimalpoint_cache;
58187808Sdas	if (!(s0 = decimalpoint_cache)) {
59187808Sdas		s0 = (unsigned char*)localeconv()->decimal_point;
60219557Sdas		if ((decimalpoint_cache = (char*)MALLOC(strlen(s0) + 1))) {
61187808Sdas			strcpy(decimalpoint_cache, s0);
62187808Sdas			s0 = decimalpoint_cache;
63187808Sdas			}
64187808Sdas		}
65187808Sdas	decimalpoint = s0;
66112620Sdas#endif
67187808Sdas#endif
68112158Sdas
69112158Sdas	if (!hexdig['0'])
70112158Sdas		hexdig_init_D2A();
71182709Sdas	*bp = 0;
72112158Sdas	havedig = 0;
73112158Sdas	s0 = *(CONST unsigned char **)sp + 2;
74112158Sdas	while(s0[havedig] == '0')
75112158Sdas		havedig++;
76112158Sdas	s0 += havedig;
77112158Sdas	s = s0;
78112158Sdas	decpt = 0;
79165743Sdas	zret = 0;
80165743Sdas	e = 0;
81187808Sdas	if (hexdig[*s])
82187808Sdas		havedig++;
83187808Sdas	else {
84165743Sdas		zret = 1;
85187808Sdas#ifdef USE_LOCALE
86187808Sdas		for(i = 0; decimalpoint[i]; ++i) {
87187808Sdas			if (s[i] != decimalpoint[i])
88187808Sdas				goto pcheck;
89187808Sdas			}
90187808Sdas		decpt = s += i;
91187808Sdas#else
92187808Sdas		if (*s != '.')
93165743Sdas			goto pcheck;
94165743Sdas		decpt = ++s;
95187808Sdas#endif
96165743Sdas		if (!hexdig[*s])
97165743Sdas			goto pcheck;
98112158Sdas		while(*s == '0')
99112158Sdas			s++;
100165743Sdas		if (hexdig[*s])
101165743Sdas			zret = 0;
102112158Sdas		havedig = 1;
103112158Sdas		s0 = s;
104112158Sdas		}
105112158Sdas	while(hexdig[*s])
106112158Sdas		s++;
107187808Sdas#ifdef USE_LOCALE
108187808Sdas	if (*s == *decimalpoint && !decpt) {
109187808Sdas		for(i = 1; decimalpoint[i]; ++i) {
110187808Sdas			if (s[i] != decimalpoint[i])
111187808Sdas				goto pcheck;
112187808Sdas			}
113187808Sdas		decpt = s += i;
114187808Sdas#else
115187808Sdas	if (*s == '.' && !decpt) {
116112158Sdas		decpt = ++s;
117187808Sdas#endif
118112158Sdas		while(hexdig[*s])
119112158Sdas			s++;
120187808Sdas		}/*}*/
121112158Sdas	if (decpt)
122112158Sdas		e = -(((Long)(s-decpt)) << 2);
123165743Sdas pcheck:
124112158Sdas	s1 = s;
125182709Sdas	big = esign = 0;
126112158Sdas	switch(*s) {
127112158Sdas	  case 'p':
128112158Sdas	  case 'P':
129112158Sdas		switch(*++s) {
130112158Sdas		  case '-':
131112158Sdas			esign = 1;
132112158Sdas			/* no break */
133112158Sdas		  case '+':
134112158Sdas			s++;
135112158Sdas		  }
136112158Sdas		if ((n = hexdig[*s]) == 0 || n > 0x19) {
137112158Sdas			s = s1;
138112158Sdas			break;
139112158Sdas			}
140112158Sdas		e1 = n - 0x10;
141182709Sdas		while((n = hexdig[*++s]) !=0 && n <= 0x19) {
142182709Sdas			if (e1 & 0xf8000000)
143182709Sdas				big = 1;
144112158Sdas			e1 = 10*e1 + n - 0x10;
145182709Sdas			}
146112158Sdas		if (esign)
147112158Sdas			e1 = -e1;
148112158Sdas		e += e1;
149112158Sdas	  }
150112158Sdas	*sp = (char*)s;
151182709Sdas	if (!havedig)
152187808Sdas		*sp = (char*)s0 - 1;
153182709Sdas	if (zret)
154179918Sdas		return STRTOG_Zero;
155182709Sdas	if (big) {
156182709Sdas		if (esign) {
157182709Sdas			switch(fpi->rounding) {
158182709Sdas			  case FPI_Round_up:
159182709Sdas				if (sign)
160182709Sdas					break;
161182709Sdas				goto ret_tiny;
162182709Sdas			  case FPI_Round_down:
163182709Sdas				if (!sign)
164182709Sdas					break;
165182709Sdas				goto ret_tiny;
166182709Sdas			  }
167182709Sdas			goto retz;
168182709Sdas ret_tiny:
169182709Sdas			b = Balloc(0);
170182709Sdas			b->wds = 1;
171182709Sdas			b->x[0] = 1;
172182709Sdas			goto dret;
173182709Sdas			}
174182709Sdas		switch(fpi->rounding) {
175182709Sdas		  case FPI_Round_near:
176182709Sdas			goto ovfl1;
177182709Sdas		  case FPI_Round_up:
178182709Sdas			if (!sign)
179182709Sdas				goto ovfl1;
180182709Sdas			goto ret_big;
181182709Sdas		  case FPI_Round_down:
182182709Sdas			if (sign)
183182709Sdas				goto ovfl1;
184182709Sdas			goto ret_big;
185182709Sdas		  }
186182709Sdas ret_big:
187182709Sdas		nbits = fpi->nbits;
188182709Sdas		n0 = n = nbits >> kshift;
189182709Sdas		if (nbits & kmask)
190182709Sdas			++n;
191182709Sdas		for(j = n, k = 0; j >>= 1; ++k);
192182709Sdas		*bp = b = Balloc(k);
193182709Sdas		b->wds = n;
194182709Sdas		for(j = 0; j < n0; ++j)
195182709Sdas			b->x[j] = ALL_ON;
196182709Sdas		if (n > n0)
197182709Sdas			b->x[j] = ULbits >> (ULbits - (nbits & kmask));
198182709Sdas		*exp = fpi->emin;
199182709Sdas		return STRTOG_Normal | STRTOG_Inexlo;
200179918Sdas		}
201112158Sdas	n = s1 - s0 - 1;
202219557Sdas	for(k = 0; n > (1 << (kshift-2)) - 1; n >>= 1)
203112158Sdas		k++;
204112158Sdas	b = Balloc(k);
205112158Sdas	x = b->x;
206112158Sdas	n = 0;
207112158Sdas	L = 0;
208187808Sdas#ifdef USE_LOCALE
209187808Sdas	for(i = 0; decimalpoint[i+1]; ++i);
210187808Sdas#endif
211112158Sdas	while(s1 > s0) {
212187808Sdas#ifdef USE_LOCALE
213187808Sdas		if (*--s1 == decimalpoint[i]) {
214187808Sdas			s1 -= i;
215112158Sdas			continue;
216187808Sdas			}
217187808Sdas#else
218187808Sdas		if (*--s1 == '.')
219187808Sdas			continue;
220187808Sdas#endif
221187808Sdas		if (n == ULbits) {
222112158Sdas			*x++ = L;
223112158Sdas			L = 0;
224112158Sdas			n = 0;
225112158Sdas			}
226112158Sdas		L |= (hexdig[*s1] & 0x0f) << n;
227112158Sdas		n += 4;
228112158Sdas		}
229112158Sdas	*x++ = L;
230112158Sdas	b->wds = n = x - b->x;
231187808Sdas	n = ULbits*n - hi0bits(L);
232112158Sdas	nbits = fpi->nbits;
233112158Sdas	lostbits = 0;
234112158Sdas	x = b->x;
235112158Sdas	if (n > nbits) {
236112158Sdas		n -= nbits;
237112158Sdas		if (any_on(b,n)) {
238112158Sdas			lostbits = 1;
239112158Sdas			k = n - 1;
240112158Sdas			if (x[k>>kshift] & 1 << (k & kmask)) {
241112158Sdas				lostbits = 2;
242182709Sdas				if (k > 0 && any_on(b,k))
243112158Sdas					lostbits = 3;
244112158Sdas				}
245112158Sdas			}
246112158Sdas		rshift(b, n);
247112158Sdas		e += n;
248112158Sdas		}
249112158Sdas	else if (n < nbits) {
250112158Sdas		n = nbits - n;
251112158Sdas		b = lshift(b, n);
252112158Sdas		e -= n;
253112158Sdas		x = b->x;
254112158Sdas		}
255112158Sdas	if (e > fpi->emax) {
256112158Sdas ovfl:
257112158Sdas		Bfree(b);
258182709Sdas ovfl1:
259182709Sdas#ifndef NO_ERRNO
260182709Sdas		errno = ERANGE;
261182709Sdas#endif
262112158Sdas		return STRTOG_Infinite | STRTOG_Overflow | STRTOG_Inexhi;
263112158Sdas		}
264112158Sdas	irv = STRTOG_Normal;
265112158Sdas	if (e < fpi->emin) {
266112158Sdas		irv = STRTOG_Denormal;
267112158Sdas		n = fpi->emin - e;
268112158Sdas		if (n >= nbits) {
269112158Sdas			switch (fpi->rounding) {
270112158Sdas			  case FPI_Round_near:
271124703Sdas				if (n == nbits && (n < 2 || any_on(b,n-1)))
272112158Sdas					goto one_bit;
273112158Sdas				break;
274112158Sdas			  case FPI_Round_up:
275112158Sdas				if (!sign)
276112158Sdas					goto one_bit;
277112158Sdas				break;
278112158Sdas			  case FPI_Round_down:
279112158Sdas				if (sign) {
280112158Sdas one_bit:
281112158Sdas					x[0] = b->wds = 1;
282182709Sdas dret:
283112158Sdas					*bp = b;
284182709Sdas					*exp = fpi->emin;
285182709Sdas#ifndef NO_ERRNO
286182709Sdas					errno = ERANGE;
287182709Sdas#endif
288112158Sdas					return STRTOG_Denormal | STRTOG_Inexhi
289112158Sdas						| STRTOG_Underflow;
290112158Sdas					}
291112158Sdas			  }
292112158Sdas			Bfree(b);
293182709Sdas retz:
294182709Sdas#ifndef NO_ERRNO
295182709Sdas			errno = ERANGE;
296182709Sdas#endif
297112158Sdas			return STRTOG_Zero | STRTOG_Inexlo | STRTOG_Underflow;
298112158Sdas			}
299112158Sdas		k = n - 1;
300112158Sdas		if (lostbits)
301112158Sdas			lostbits = 1;
302112158Sdas		else if (k > 0)
303112158Sdas			lostbits = any_on(b,k);
304112158Sdas		if (x[k>>kshift] & 1 << (k & kmask))
305112158Sdas			lostbits |= 2;
306112158Sdas		nbits -= n;
307112158Sdas		rshift(b,n);
308112158Sdas		e = fpi->emin;
309112158Sdas		}
310112158Sdas	if (lostbits) {
311112158Sdas		up = 0;
312112158Sdas		switch(fpi->rounding) {
313112158Sdas		  case FPI_Round_zero:
314112158Sdas			break;
315112158Sdas		  case FPI_Round_near:
316112158Sdas			if (lostbits & 2
317219557Sdas			 && (lostbits | x[0]) & 1)
318112158Sdas				up = 1;
319112158Sdas			break;
320112158Sdas		  case FPI_Round_up:
321112158Sdas			up = 1 - sign;
322112158Sdas			break;
323112158Sdas		  case FPI_Round_down:
324112158Sdas			up = sign;
325112158Sdas		  }
326112158Sdas		if (up) {
327112158Sdas			k = b->wds;
328112158Sdas			b = increment(b);
329112158Sdas			x = b->x;
330124703Sdas			if (irv == STRTOG_Denormal) {
331124703Sdas				if (nbits == fpi->nbits - 1
332124703Sdas				 && x[nbits >> kshift] & 1 << (nbits & kmask))
333124703Sdas					irv =  STRTOG_Normal;
334124703Sdas				}
335124703Sdas			else if (b->wds > k
336219557Sdas			 || ((n = nbits & kmask) !=0
337219557Sdas			      && hi0bits(x[k-1]) < 32-n)) {
338112158Sdas				rshift(b,1);
339112158Sdas				if (++e > fpi->emax)
340112158Sdas					goto ovfl;
341112158Sdas				}
342112158Sdas			irv |= STRTOG_Inexhi;
343112158Sdas			}
344112158Sdas		else
345112158Sdas			irv |= STRTOG_Inexlo;
346112158Sdas		}
347112158Sdas	*bp = b;
348112158Sdas	*exp = e;
349112158Sdas	return irv;
350112158Sdas	}
351