xref: /netbsd-src/lib/libc/gdtoa/gethex.c (revision 35ea0de33581106483174313281ca7424bcc9601)
1*35ea0de3Skamil /* $NetBSD: gethex.c,v 1.7 2020/02/22 00:38:14 kamil Exp $ */
27684d5e0Skleink 
37684d5e0Skleink /****************************************************************
47684d5e0Skleink 
57684d5e0Skleink The author of this software is David M. Gay.
67684d5e0Skleink 
77684d5e0Skleink Copyright (C) 1998 by Lucent Technologies
87684d5e0Skleink All Rights Reserved
97684d5e0Skleink 
107684d5e0Skleink Permission to use, copy, modify, and distribute this software and
117684d5e0Skleink its documentation for any purpose and without fee is hereby
127684d5e0Skleink granted, provided that the above copyright notice appear in all
137684d5e0Skleink copies and that both that the copyright notice and this
147684d5e0Skleink permission notice and warranty disclaimer appear in supporting
157684d5e0Skleink documentation, and that the name of Lucent or any of its entities
167684d5e0Skleink not be used in advertising or publicity pertaining to
177684d5e0Skleink distribution of the software without specific, written prior
187684d5e0Skleink permission.
197684d5e0Skleink 
207684d5e0Skleink LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
217684d5e0Skleink INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
227684d5e0Skleink IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
237684d5e0Skleink SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
247684d5e0Skleink WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
257684d5e0Skleink IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
267684d5e0Skleink ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
277684d5e0Skleink THIS SOFTWARE.
287684d5e0Skleink 
297684d5e0Skleink ****************************************************************/
307684d5e0Skleink 
317684d5e0Skleink /* Please send bug reports to David M. Gay (dmg at acm dot org,
327684d5e0Skleink  * with " at " changed at "@" and " dot " changed to ".").	*/
337684d5e0Skleink 
347684d5e0Skleink #include "gdtoaimp.h"
357684d5e0Skleink 
367684d5e0Skleink #ifdef USE_LOCALE
377684d5e0Skleink #include "locale.h"
387684d5e0Skleink #endif
397684d5e0Skleink 
407684d5e0Skleink  int
gethex(CONST char ** sp,CONST FPI * fpi,Long * expt,Bigint ** bp,int sign,locale_t loc)410feb0f12Sjoerg gethex( CONST char **sp, CONST FPI *fpi, Long *expt, Bigint **bp, int sign, locale_t loc)
427684d5e0Skleink {
437684d5e0Skleink 	Bigint *b;
4461e56760Schristos 	CONST char *decpt, *s, *s0, *s1;
4561e56760Schristos 	int big, esign, havedig, irv, j, k, n, n0, nbits, up, zret;
467684d5e0Skleink 	ULong L, lostbits, *x;
477684d5e0Skleink 	Long e, e1;
487684d5e0Skleink #ifdef USE_LOCALE
4961e56760Schristos 	int i;
500feb0f12Sjoerg 	const char *decimalpoint = localeconv_l(loc)->decimal_point;
517684d5e0Skleink #endif
527684d5e0Skleink 
5361e56760Schristos 	if (!hexdig[(unsigned char)'0'])
547684d5e0Skleink 		hexdig_init_D2A();
5561e56760Schristos 	*bp = 0;
567684d5e0Skleink 	havedig = 0;
5761e56760Schristos 	s0 = *(CONST char **)sp + 2;
587684d5e0Skleink 	while(s0[havedig] == '0')
597684d5e0Skleink 		havedig++;
607684d5e0Skleink 	s0 += havedig;
617684d5e0Skleink 	s = s0;
627684d5e0Skleink 	decpt = 0;
637684d5e0Skleink 	zret = 0;
647684d5e0Skleink 	e = 0;
6561e56760Schristos 	if (hexdig[(unsigned char)*s])
6661e56760Schristos 		havedig++;
6761e56760Schristos 	else {
687684d5e0Skleink 		zret = 1;
6961e56760Schristos #ifdef USE_LOCALE
7061e56760Schristos 		for(i = 0; decimalpoint[i]; ++i) {
7161e56760Schristos 			if (s[i] != decimalpoint[i])
7261e56760Schristos 				goto pcheck;
7361e56760Schristos 			}
7461e56760Schristos 		decpt = s += i;
7561e56760Schristos #else
7661e56760Schristos 		if (*s != '.')
777684d5e0Skleink 			goto pcheck;
787684d5e0Skleink 		decpt = ++s;
7961e56760Schristos #endif
8061e56760Schristos 		if (!hexdig[(unsigned char)*s])
817684d5e0Skleink 			goto pcheck;
827684d5e0Skleink 		while(*s == '0')
837684d5e0Skleink 			s++;
8461e56760Schristos 		if (hexdig[(unsigned char)*s])
857684d5e0Skleink 			zret = 0;
867684d5e0Skleink 		havedig = 1;
877684d5e0Skleink 		s0 = s;
887684d5e0Skleink 		}
8961e56760Schristos 	while(hexdig[(unsigned char)*s])
907684d5e0Skleink 		s++;
9161e56760Schristos #ifdef USE_LOCALE
9261e56760Schristos 	if (*s == *decimalpoint && !decpt) {
9361e56760Schristos 		for(i = 1; decimalpoint[i]; ++i) {
9461e56760Schristos 			if (s[i] != decimalpoint[i])
9561e56760Schristos 				goto pcheck;
967684d5e0Skleink 			}
9761e56760Schristos 		decpt = s += i;
9861e56760Schristos #else
9961e56760Schristos 	if (*s == '.' && !decpt) {
10061e56760Schristos 		decpt = ++s;
10161e56760Schristos #endif
10261e56760Schristos 		while(hexdig[(unsigned char)*s])
10361e56760Schristos 			s++;
10461e56760Schristos 		}/*}*/
1057684d5e0Skleink 	if (decpt)
1067684d5e0Skleink 		e = -(((Long)(s-decpt)) << 2);
1077684d5e0Skleink  pcheck:
1087684d5e0Skleink 	s1 = s;
10961e56760Schristos 	big = esign = 0;
1107684d5e0Skleink 	switch(*s) {
1117684d5e0Skleink 	  case 'p':
1127684d5e0Skleink 	  case 'P':
1137684d5e0Skleink 		switch(*++s) {
1147684d5e0Skleink 		  case '-':
1157684d5e0Skleink 			esign = 1;
116ac898a26Skleink 			/* FALLTHROUGH */
1177684d5e0Skleink 		  case '+':
1187684d5e0Skleink 			s++;
1197684d5e0Skleink 		  }
12061e56760Schristos 		if ((n = hexdig[(unsigned char)*s]) == 0 || n > 0x19) {
1217684d5e0Skleink 			s = s1;
1227684d5e0Skleink 			break;
1237684d5e0Skleink 			}
1247684d5e0Skleink 		e1 = n - 0x10;
12561e56760Schristos 		while((n = hexdig[(unsigned char)*++s]) !=0 && n <= 0x19) {
12661e56760Schristos 			if (e1 & 0xf8000000)
12761e56760Schristos 				big = 1;
1287684d5e0Skleink 			e1 = 10*e1 + n - 0x10;
12961e56760Schristos 			}
1307684d5e0Skleink 		if (esign)
1317684d5e0Skleink 			e1 = -e1;
1327684d5e0Skleink 		e += e1;
1337684d5e0Skleink 	  }
134ac898a26Skleink 	*sp = __UNCONST(s);
13561e56760Schristos 	if (!havedig)
13661e56760Schristos 		*sp = (char*)__UNCONST(s0) - 1;
1377684d5e0Skleink 	if (zret)
13861e56760Schristos 		return STRTOG_Zero;
13961e56760Schristos 	if (big) {
14061e56760Schristos 		if (esign) {
14161e56760Schristos 			switch(fpi->rounding) {
14261e56760Schristos 			  case FPI_Round_up:
14361e56760Schristos 				if (sign)
14461e56760Schristos 					break;
14561e56760Schristos 				goto ret_tiny;
14661e56760Schristos 			  case FPI_Round_down:
14761e56760Schristos 				if (!sign)
14861e56760Schristos 					break;
14961e56760Schristos 				goto ret_tiny;
15061e56760Schristos 			  }
15161e56760Schristos 			goto retz;
15261e56760Schristos  ret_tiny:
15361e56760Schristos 			b = Balloc(0);
15461e56760Schristos 			b->wds = 1;
15561e56760Schristos 			b->x[0] = 1;
15661e56760Schristos 			goto dret;
15761e56760Schristos 			}
15861e56760Schristos 		switch(fpi->rounding) {
15961e56760Schristos 		  case FPI_Round_near:
16061e56760Schristos 			goto ovfl1;
16161e56760Schristos 		  case FPI_Round_up:
16261e56760Schristos 			if (!sign)
16361e56760Schristos 				goto ovfl1;
16461e56760Schristos 			goto ret_big;
16561e56760Schristos 		  case FPI_Round_down:
16661e56760Schristos 			if (sign)
16761e56760Schristos 				goto ovfl1;
16861e56760Schristos 			goto ret_big;
16961e56760Schristos 		  }
17061e56760Schristos  ret_big:
17161e56760Schristos 		nbits = fpi->nbits;
17261e56760Schristos 		n0 = n = (unsigned int)nbits >> kshift;
17361e56760Schristos 		if (nbits & kmask)
17461e56760Schristos 			++n;
17561e56760Schristos 		for(j = n, k = 0; (j = (unsigned int)j >> 1) != 0; ++k);
17661e56760Schristos 		*bp = b = Balloc(k);
17761e56760Schristos 		b->wds = n;
17861e56760Schristos 		for(j = 0; j < n0; ++j)
17961e56760Schristos 			b->x[j] = ALL_ON;
18061e56760Schristos 		if (n > n0)
18161e56760Schristos 			b->x[j] = ULbits >> (ULbits - (nbits & kmask));
18261e56760Schristos 		*expt = fpi->emin;
18361e56760Schristos 		return STRTOG_Normal | STRTOG_Inexlo;
18461e56760Schristos 		}
18561e56760Schristos 	n = (int)(s1 - s0) - 1;
18661e56760Schristos 	for(k = 0; n > (1 << (kshift-2)) - 1; n = (unsigned int)n >> 1)
1877684d5e0Skleink 		k++;
1887684d5e0Skleink 	b = Balloc(k);
189ab625449Schristos 	if (b == NULL)
190ab625449Schristos 		return STRTOG_NoMemory;
1917684d5e0Skleink 	x = b->x;
1927684d5e0Skleink 	n = 0;
1937684d5e0Skleink 	L = 0;
19461e56760Schristos #ifdef USE_LOCALE
19561e56760Schristos 	for(i = 0; decimalpoint[i+1]; ++i);
19661e56760Schristos #endif
1977684d5e0Skleink 	while(s1 > s0) {
19861e56760Schristos #ifdef USE_LOCALE
19961e56760Schristos 		if (*--s1 == decimalpoint[i]) {
20061e56760Schristos 			s1 -= i;
2017684d5e0Skleink 			continue;
20261e56760Schristos 			}
20361e56760Schristos #else
20461e56760Schristos 		if (*--s1 == '.')
20561e56760Schristos 			continue;
20661e56760Schristos #endif
20761e56760Schristos 		if (n == ULbits) {
2087684d5e0Skleink 			*x++ = L;
2097684d5e0Skleink 			L = 0;
2107684d5e0Skleink 			n = 0;
2117684d5e0Skleink 			}
212*35ea0de3Skamil 		L |= (unsigned int)(hexdig[(unsigned char)*s1] & 0x0f) << n;
2137684d5e0Skleink 		n += 4;
2147684d5e0Skleink 		}
2157684d5e0Skleink 	*x++ = L;
21661e56760Schristos 	b->wds = n = (int)(x - b->x);
21761e56760Schristos 	n = ULbits*n - hi0bits(L);
2187684d5e0Skleink 	nbits = fpi->nbits;
2197684d5e0Skleink 	lostbits = 0;
2207684d5e0Skleink 	x = b->x;
2217684d5e0Skleink 	if (n > nbits) {
2227684d5e0Skleink 		n -= nbits;
2237684d5e0Skleink 		if (any_on(b,n)) {
2247684d5e0Skleink 			lostbits = 1;
2257684d5e0Skleink 			k = n - 1;
226ac898a26Skleink 			if (x[(unsigned int)k>>kshift] & 1 << (k & kmask)) {
2277684d5e0Skleink 				lostbits = 2;
22861e56760Schristos 				if (k > 0 && any_on(b,k))
2297684d5e0Skleink 					lostbits = 3;
2307684d5e0Skleink 				}
2317684d5e0Skleink 			}
2327684d5e0Skleink 		rshift(b, n);
2337684d5e0Skleink 		e += n;
2347684d5e0Skleink 		}
2357684d5e0Skleink 	else if (n < nbits) {
2367684d5e0Skleink 		n = nbits - n;
2377684d5e0Skleink 		b = lshift(b, n);
238ab625449Schristos 		if (b == NULL)
239ab625449Schristos 			return STRTOG_NoMemory;
2407684d5e0Skleink 		e -= n;
2417684d5e0Skleink 		x = b->x;
2427684d5e0Skleink 		}
2437684d5e0Skleink 	if (e > fpi->emax) {
2447684d5e0Skleink  ovfl:
2457684d5e0Skleink 		Bfree(b);
24661e56760Schristos  ovfl1:
24761e56760Schristos #ifndef NO_ERRNO
24861e56760Schristos 		errno = ERANGE;
24961e56760Schristos #endif
2507684d5e0Skleink 		return STRTOG_Infinite | STRTOG_Overflow | STRTOG_Inexhi;
2517684d5e0Skleink 		}
2527684d5e0Skleink 	irv = STRTOG_Normal;
2537684d5e0Skleink 	if (e < fpi->emin) {
2547684d5e0Skleink 		irv = STRTOG_Denormal;
2557684d5e0Skleink 		n = fpi->emin - e;
2567684d5e0Skleink 		if (n >= nbits) {
2577684d5e0Skleink 			switch (fpi->rounding) {
2587684d5e0Skleink 			  case FPI_Round_near:
2597684d5e0Skleink 				if (n == nbits && (n < 2 || any_on(b,n-1)))
2607684d5e0Skleink 					goto one_bit;
2617684d5e0Skleink 				break;
2627684d5e0Skleink 			  case FPI_Round_up:
2637684d5e0Skleink 				if (!sign)
2647684d5e0Skleink 					goto one_bit;
2657684d5e0Skleink 				break;
2667684d5e0Skleink 			  case FPI_Round_down:
2677684d5e0Skleink 				if (sign) {
2687684d5e0Skleink  one_bit:
2697684d5e0Skleink 					x[0] = b->wds = 1;
27061e56760Schristos  dret:
2717684d5e0Skleink 					*bp = b;
27261e56760Schristos 					*expt = fpi->emin;
27361e56760Schristos #ifndef NO_ERRNO
27461e56760Schristos 					errno = ERANGE;
27561e56760Schristos #endif
2767684d5e0Skleink 					return STRTOG_Denormal | STRTOG_Inexhi
2777684d5e0Skleink 						| STRTOG_Underflow;
2787684d5e0Skleink 					}
2797684d5e0Skleink 			  }
2807684d5e0Skleink 			Bfree(b);
28161e56760Schristos  retz:
28261e56760Schristos #ifndef NO_ERRNO
28361e56760Schristos 			errno = ERANGE;
28461e56760Schristos #endif
2857684d5e0Skleink 			return STRTOG_Zero | STRTOG_Inexlo | STRTOG_Underflow;
2867684d5e0Skleink 			}
2877684d5e0Skleink 		k = n - 1;
2887684d5e0Skleink 		if (lostbits)
2897684d5e0Skleink 			lostbits = 1;
2907684d5e0Skleink 		else if (k > 0)
2917684d5e0Skleink 			lostbits = any_on(b,k);
292ac898a26Skleink 		if (x[(unsigned int)k>>kshift] & 1 << (k & kmask))
2937684d5e0Skleink 			lostbits |= 2;
2947684d5e0Skleink 		nbits -= n;
2957684d5e0Skleink 		rshift(b,n);
2967684d5e0Skleink 		e = fpi->emin;
2977684d5e0Skleink 		}
2987684d5e0Skleink 	if (lostbits) {
2997684d5e0Skleink 		up = 0;
3007684d5e0Skleink 		switch(fpi->rounding) {
3017684d5e0Skleink 		  case FPI_Round_zero:
3027684d5e0Skleink 			break;
3037684d5e0Skleink 		  case FPI_Round_near:
3047684d5e0Skleink 			if (lostbits & 2
30561e56760Schristos 			 && (lostbits | x[0]) & 1)
3067684d5e0Skleink 				up = 1;
3077684d5e0Skleink 			break;
3087684d5e0Skleink 		  case FPI_Round_up:
3097684d5e0Skleink 			up = 1 - sign;
3107684d5e0Skleink 			break;
3117684d5e0Skleink 		  case FPI_Round_down:
3127684d5e0Skleink 			up = sign;
3137684d5e0Skleink 		  }
3147684d5e0Skleink 		if (up) {
3157684d5e0Skleink 			k = b->wds;
3167684d5e0Skleink 			b = increment(b);
3177684d5e0Skleink 			x = b->x;
3187684d5e0Skleink 			if (irv == STRTOG_Denormal) {
3197684d5e0Skleink 				if (nbits == fpi->nbits - 1
320ac898a26Skleink 				 && x[(unsigned int)nbits >> kshift] & 1 << (nbits & kmask))
3217684d5e0Skleink 					irv =  STRTOG_Normal;
3227684d5e0Skleink 				}
3237684d5e0Skleink 			else if (b->wds > k
324ac898a26Skleink 			 || ((n = nbits & kmask) !=0
325ac898a26Skleink 			      && hi0bits(x[k-1]) < 32-n)) {
3267684d5e0Skleink 				rshift(b,1);
3277684d5e0Skleink 				if (++e > fpi->emax)
3287684d5e0Skleink 					goto ovfl;
3297684d5e0Skleink 				}
3307684d5e0Skleink 			irv |= STRTOG_Inexhi;
3317684d5e0Skleink 			}
3327684d5e0Skleink 		else
3337684d5e0Skleink 			irv |= STRTOG_Inexlo;
3347684d5e0Skleink 		}
3357684d5e0Skleink 	*bp = b;
336ac898a26Skleink 	*expt = e;
3377684d5e0Skleink 	return irv;
3387684d5e0Skleink 	}
339