gethex.c revision 219557
133965Sjdp/****************************************************************
260484Sobrien
360484SobrienThe author of this software is David M. Gay.
433965Sjdp
533965SjdpCopyright (C) 1998 by Lucent Technologies
633965SjdpAll Rights Reserved
733965Sjdp
833965SjdpPermission to use, copy, modify, and distribute this software and
933965Sjdpits documentation for any purpose and without fee is hereby
1033965Sjdpgranted, provided that the above copyright notice appear in all
1133965Sjdpcopies and that both that the copyright notice and this
1233965Sjdppermission notice and warranty disclaimer appear in supporting
1333965Sjdpdocumentation, and that the name of Lucent or any of its entities
1433965Sjdpnot be used in advertising or publicity pertaining to
1533965Sjdpdistribution of the software without specific, written prior
1633965Sjdppermission.
1733965Sjdp
1833965SjdpLUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
1933965SjdpINCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
2033965SjdpIN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
2133965SjdpSPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
2233965SjdpWHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
2333965SjdpIN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
2433965SjdpARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
2533965SjdpTHIS SOFTWARE.
2633965Sjdp
2733965Sjdp****************************************************************/
2833965Sjdp
2933965Sjdp/* Please send bug reports to David M. Gay (dmg at acm dot org,
3033965Sjdp * with " at " changed at "@" and " dot " changed to ".").	*/
3133965Sjdp
3233965Sjdp#include "gdtoaimp.h"
3333965Sjdp
3433965Sjdp#ifdef USE_LOCALE
3533965Sjdp#include "locale.h"
3633965Sjdp#endif
3733965Sjdp
3833965Sjdp int
3933965Sjdp#ifdef KR_headers
4033965Sjdpgethex(sp, fpi, exp, bp, sign)
4133965Sjdp	CONST char **sp; FPI *fpi; Long *exp; Bigint **bp; int sign;
4233965Sjdp#else
4333965Sjdpgethex( CONST char **sp, FPI *fpi, Long *exp, Bigint **bp, int sign)
4433965Sjdp#endif
4533965Sjdp{
4633965Sjdp	Bigint *b;
4733965Sjdp	CONST unsigned char *decpt, *s0, *s, *s1;
4833965Sjdp	int big, esign, havedig, irv, j, k, n, n0, nbits, up, zret;
4933965Sjdp	ULong L, lostbits, *x;
5033965Sjdp	Long e, e1;
5133965Sjdp#ifdef USE_LOCALE
5233965Sjdp	int i;
5333965Sjdp#ifdef NO_LOCALE_CACHE
5433965Sjdp	const unsigned char *decimalpoint = (unsigned char*)localeconv()->decimal_point;
5533965Sjdp#else
5633965Sjdp	const unsigned char *decimalpoint;
5733965Sjdp	static unsigned char *decimalpoint_cache;
5833965Sjdp	if (!(s0 = decimalpoint_cache)) {
5933965Sjdp		s0 = (unsigned char*)localeconv()->decimal_point;
6033965Sjdp		if ((decimalpoint_cache = (char*)MALLOC(strlen(s0) + 1))) {
6133965Sjdp			strcpy(decimalpoint_cache, s0);
6233965Sjdp			s0 = decimalpoint_cache;
6333965Sjdp			}
6433965Sjdp		}
6533965Sjdp	decimalpoint = s0;
6633965Sjdp#endif
6733965Sjdp#endif
6833965Sjdp
6933965Sjdp	if (!hexdig['0'])
7033965Sjdp		hexdig_init_D2A();
7133965Sjdp	*bp = 0;
7233965Sjdp	havedig = 0;
7333965Sjdp	s0 = *(CONST unsigned char **)sp + 2;
7433965Sjdp	while(s0[havedig] == '0')
7533965Sjdp		havedig++;
7633965Sjdp	s0 += havedig;
7733965Sjdp	s = s0;
7833965Sjdp	decpt = 0;
7933965Sjdp	zret = 0;
8033965Sjdp	e = 0;
8133965Sjdp	if (hexdig[*s])
8233965Sjdp		havedig++;
8333965Sjdp	else {
8433965Sjdp		zret = 1;
8533965Sjdp#ifdef USE_LOCALE
8633965Sjdp		for(i = 0; decimalpoint[i]; ++i) {
8733965Sjdp			if (s[i] != decimalpoint[i])
8833965Sjdp				goto pcheck;
8960484Sobrien			}
9060484Sobrien		decpt = s += i;
9160484Sobrien#else
9260484Sobrien		if (*s != '.')
9360484Sobrien			goto pcheck;
9460484Sobrien		decpt = ++s;
9560484Sobrien#endif
9660484Sobrien		if (!hexdig[*s])
9760484Sobrien			goto pcheck;
9860484Sobrien		while(*s == '0')
9933965Sjdp			s++;
10033965Sjdp		if (hexdig[*s])
10133965Sjdp			zret = 0;
10233965Sjdp		havedig = 1;
10333965Sjdp		s0 = s;
10433965Sjdp		}
10533965Sjdp	while(hexdig[*s])
10633965Sjdp		s++;
10733965Sjdp#ifdef USE_LOCALE
10833965Sjdp	if (*s == *decimalpoint && !decpt) {
10933965Sjdp		for(i = 1; decimalpoint[i]; ++i) {
11060484Sobrien			if (s[i] != decimalpoint[i])
11160484Sobrien				goto pcheck;
11260484Sobrien			}
11360484Sobrien		decpt = s += i;
11460484Sobrien#else
11560484Sobrien	if (*s == '.' && !decpt) {
11660484Sobrien		decpt = ++s;
11760484Sobrien#endif
11860484Sobrien		while(hexdig[*s])
11960484Sobrien			s++;
12060484Sobrien		}/*}*/
12133965Sjdp	if (decpt)
12260484Sobrien		e = -(((Long)(s-decpt)) << 2);
12360484Sobrien pcheck:
12460484Sobrien	s1 = s;
12560484Sobrien	big = esign = 0;
12660484Sobrien	switch(*s) {
12760484Sobrien	  case 'p':
12833965Sjdp	  case 'P':
12933965Sjdp		switch(*++s) {
13033965Sjdp		  case '-':
13133965Sjdp			esign = 1;
13233965Sjdp			/* no break */
13333965Sjdp		  case '+':
13433965Sjdp			s++;
13533965Sjdp		  }
13633965Sjdp		if ((n = hexdig[*s]) == 0 || n > 0x19) {
13733965Sjdp			s = s1;
13833965Sjdp			break;
13933965Sjdp			}
14033965Sjdp		e1 = n - 0x10;
14133965Sjdp		while((n = hexdig[*++s]) !=0 && n <= 0x19) {
14233965Sjdp			if (e1 & 0xf8000000)
14333965Sjdp				big = 1;
14433965Sjdp			e1 = 10*e1 + n - 0x10;
14533965Sjdp			}
14633965Sjdp		if (esign)
14760484Sobrien			e1 = -e1;
14860484Sobrien		e += e1;
14960484Sobrien	  }
15060484Sobrien	*sp = (char*)s;
15160484Sobrien	if (!havedig)
15260484Sobrien		*sp = (char*)s0 - 1;
15360484Sobrien	if (zret)
15460484Sobrien		return STRTOG_Zero;
15560484Sobrien	if (big) {
15660484Sobrien		if (esign) {
15733965Sjdp			switch(fpi->rounding) {
15833965Sjdp			  case FPI_Round_up:
15933965Sjdp				if (sign)
16060484Sobrien					break;
16133965Sjdp				goto ret_tiny;
16233965Sjdp			  case FPI_Round_down:
16360484Sobrien				if (!sign)
16460484Sobrien					break;
16560484Sobrien				goto ret_tiny;
16633965Sjdp			  }
16733965Sjdp			goto retz;
16833965Sjdp ret_tiny:
16933965Sjdp			b = Balloc(0);
17033965Sjdp			b->wds = 1;
17133965Sjdp			b->x[0] = 1;
17233965Sjdp			goto dret;
17333965Sjdp			}
17433965Sjdp		switch(fpi->rounding) {
17533965Sjdp		  case FPI_Round_near:
17660484Sobrien			goto ovfl1;
17760484Sobrien		  case FPI_Round_up:
17833965Sjdp			if (!sign)
17960484Sobrien				goto ovfl1;
18033965Sjdp			goto ret_big;
18160484Sobrien		  case FPI_Round_down:
18233965Sjdp			if (sign)
18360484Sobrien				goto ovfl1;
18433965Sjdp			goto ret_big;
18560484Sobrien		  }
18633965Sjdp ret_big:
18760484Sobrien		nbits = fpi->nbits;
18833965Sjdp		n0 = n = nbits >> kshift;
18960484Sobrien		if (nbits & kmask)
19060484Sobrien			++n;
19160484Sobrien		for(j = n, k = 0; j >>= 1; ++k);
19260484Sobrien		*bp = b = Balloc(k);
19360484Sobrien		b->wds = n;
19460484Sobrien		for(j = 0; j < n0; ++j)
19533965Sjdp			b->x[j] = ALL_ON;
19633965Sjdp		if (n > n0)
19760484Sobrien			b->x[j] = ULbits >> (ULbits - (nbits & kmask));
19860484Sobrien		*exp = fpi->emin;
19960484Sobrien		return STRTOG_Normal | STRTOG_Inexlo;
20060484Sobrien		}
20160484Sobrien	n = s1 - s0 - 1;
20260484Sobrien	for(k = 0; n > (1 << (kshift-2)) - 1; n >>= 1)
20360484Sobrien		k++;
20460484Sobrien	b = Balloc(k);
20560484Sobrien	x = b->x;
20660484Sobrien	n = 0;
20760484Sobrien	L = 0;
20860484Sobrien#ifdef USE_LOCALE
20960484Sobrien	for(i = 0; decimalpoint[i+1]; ++i);
21060484Sobrien#endif
21177298Sobrien	while(s1 > s0) {
21260484Sobrien#ifdef USE_LOCALE
21360484Sobrien		if (*--s1 == decimalpoint[i]) {
21460484Sobrien			s1 -= i;
21560484Sobrien			continue;
21633965Sjdp			}
21733965Sjdp#else
21833965Sjdp		if (*--s1 == '.')
21933965Sjdp			continue;
22033965Sjdp#endif
22133965Sjdp		if (n == ULbits) {
22233965Sjdp			*x++ = L;
22333965Sjdp			L = 0;
22433965Sjdp			n = 0;
22533965Sjdp			}
22633965Sjdp		L |= (hexdig[*s1] & 0x0f) << n;
22733965Sjdp		n += 4;
22833965Sjdp		}
22933965Sjdp	*x++ = L;
23033965Sjdp	b->wds = n = x - b->x;
23133965Sjdp	n = ULbits*n - hi0bits(L);
23233965Sjdp	nbits = fpi->nbits;
23333965Sjdp	lostbits = 0;
23433965Sjdp	x = b->x;
23533965Sjdp	if (n > nbits) {
23633965Sjdp		n -= nbits;
23733965Sjdp		if (any_on(b,n)) {
23833965Sjdp			lostbits = 1;
23933965Sjdp			k = n - 1;
24033965Sjdp			if (x[k>>kshift] & 1 << (k & kmask)) {
24133965Sjdp				lostbits = 2;
24233965Sjdp				if (k > 0 && any_on(b,k))
24333965Sjdp					lostbits = 3;
24460484Sobrien				}
24560484Sobrien			}
24677298Sobrien		rshift(b, n);
24777298Sobrien		e += n;
24877298Sobrien		}
24933965Sjdp	else if (n < nbits) {
25033965Sjdp		n = nbits - n;
25133965Sjdp		b = lshift(b, n);
25233965Sjdp		e -= n;
25333965Sjdp		x = b->x;
25433965Sjdp		}
25533965Sjdp	if (e > fpi->emax) {
25633965Sjdp ovfl:
25733965Sjdp		Bfree(b);
25833965Sjdp ovfl1:
25933965Sjdp#ifndef NO_ERRNO
26033965Sjdp		errno = ERANGE;
26133965Sjdp#endif
26233965Sjdp		return STRTOG_Infinite | STRTOG_Overflow | STRTOG_Inexhi;
26333965Sjdp		}
26433965Sjdp	irv = STRTOG_Normal;
26533965Sjdp	if (e < fpi->emin) {
26633965Sjdp		irv = STRTOG_Denormal;
26733965Sjdp		n = fpi->emin - e;
26833965Sjdp		if (n >= nbits) {
26933965Sjdp			switch (fpi->rounding) {
27033965Sjdp			  case FPI_Round_near:
27133965Sjdp				if (n == nbits && (n < 2 || any_on(b,n-1)))
27233965Sjdp					goto one_bit;
27333965Sjdp				break;
27433965Sjdp			  case FPI_Round_up:
27533965Sjdp				if (!sign)
27660484Sobrien					goto one_bit;
27760484Sobrien				break;
27860484Sobrien			  case FPI_Round_down:
27960484Sobrien				if (sign) {
28060484Sobrien one_bit:
28160484Sobrien					x[0] = b->wds = 1;
28260484Sobrien dret:
28333965Sjdp					*bp = b;
28433965Sjdp					*exp = fpi->emin;
28533965Sjdp#ifndef NO_ERRNO
28633965Sjdp					errno = ERANGE;
28733965Sjdp#endif
28833965Sjdp					return STRTOG_Denormal | STRTOG_Inexhi
28933965Sjdp						| STRTOG_Underflow;
29033965Sjdp					}
29133965Sjdp			  }
29233965Sjdp			Bfree(b);
29360484Sobrien retz:
29460484Sobrien#ifndef NO_ERRNO
29560484Sobrien			errno = ERANGE;
29660484Sobrien#endif
29760484Sobrien			return STRTOG_Zero | STRTOG_Inexlo | STRTOG_Underflow;
29860484Sobrien			}
29960484Sobrien		k = n - 1;
30060484Sobrien		if (lostbits)
30160484Sobrien			lostbits = 1;
30260484Sobrien		else if (k > 0)
30360484Sobrien			lostbits = any_on(b,k);
30460484Sobrien		if (x[k>>kshift] & 1 << (k & kmask))
30560484Sobrien			lostbits |= 2;
30660484Sobrien		nbits -= n;
30760484Sobrien		rshift(b,n);
30860484Sobrien		e = fpi->emin;
30960484Sobrien		}
31060484Sobrien	if (lostbits) {
31160484Sobrien		up = 0;
31260484Sobrien		switch(fpi->rounding) {
31360484Sobrien		  case FPI_Round_zero:
31460484Sobrien			break;
31560484Sobrien		  case FPI_Round_near:
31660484Sobrien			if (lostbits & 2
31760484Sobrien			 && (lostbits | x[0]) & 1)
31860484Sobrien				up = 1;
31933965Sjdp			break;
32033965Sjdp		  case FPI_Round_up:
32133965Sjdp			up = 1 - sign;
32233965Sjdp			break;
32333965Sjdp		  case FPI_Round_down:
32433965Sjdp			up = sign;
32533965Sjdp		  }
32633965Sjdp		if (up) {
32733965Sjdp			k = b->wds;
32833965Sjdp			b = increment(b);
32933965Sjdp			x = b->x;
33033965Sjdp			if (irv == STRTOG_Denormal) {
33133965Sjdp				if (nbits == fpi->nbits - 1
33233965Sjdp				 && x[nbits >> kshift] & 1 << (nbits & kmask))
33333965Sjdp					irv =  STRTOG_Normal;
33433965Sjdp				}
33533965Sjdp			else if (b->wds > k
33633965Sjdp			 || ((n = nbits & kmask) !=0
33733965Sjdp			      && hi0bits(x[k-1]) < 32-n)) {
33833965Sjdp				rshift(b,1);
33933965Sjdp				if (++e > fpi->emax)
34033965Sjdp					goto ovfl;
34133965Sjdp				}
34233965Sjdp			irv |= STRTOG_Inexhi;
34333965Sjdp			}
34433965Sjdp		else
34533965Sjdp			irv |= STRTOG_Inexlo;
34633965Sjdp		}
34733965Sjdp	*bp = b;
34833965Sjdp	*exp = e;
34933965Sjdp	return irv;
35033965Sjdp	}
35133965Sjdp