xref: /openbsd/lib/libc/gdtoa/gethex.c (revision 5b44245b)
17b36286aSmartynas /****************************************************************
27b36286aSmartynas 
37b36286aSmartynas The author of this software is David M. Gay.
47b36286aSmartynas 
57b36286aSmartynas Copyright (C) 1998 by Lucent Technologies
67b36286aSmartynas All Rights Reserved
77b36286aSmartynas 
87b36286aSmartynas Permission to use, copy, modify, and distribute this software and
97b36286aSmartynas its documentation for any purpose and without fee is hereby
107b36286aSmartynas granted, provided that the above copyright notice appear in all
117b36286aSmartynas copies and that both that the copyright notice and this
127b36286aSmartynas permission notice and warranty disclaimer appear in supporting
137b36286aSmartynas documentation, and that the name of Lucent or any of its entities
147b36286aSmartynas not be used in advertising or publicity pertaining to
157b36286aSmartynas distribution of the software without specific, written prior
167b36286aSmartynas permission.
177b36286aSmartynas 
187b36286aSmartynas LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
197b36286aSmartynas INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
207b36286aSmartynas IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
217b36286aSmartynas SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
227b36286aSmartynas WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
237b36286aSmartynas IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
247b36286aSmartynas ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
257b36286aSmartynas THIS SOFTWARE.
267b36286aSmartynas 
277b36286aSmartynas ****************************************************************/
287b36286aSmartynas 
297b36286aSmartynas /* Please send bug reports to David M. Gay (dmg at acm dot org,
307b36286aSmartynas  * with " at " changed at "@" and " dot " changed to ".").	*/
317b36286aSmartynas 
327b36286aSmartynas #include "gdtoaimp.h"
337b36286aSmartynas 
347b36286aSmartynas #ifdef USE_LOCALE
357b36286aSmartynas #include "locale.h"
367b36286aSmartynas #endif
377b36286aSmartynas 
387b36286aSmartynas  int
397b36286aSmartynas #ifdef KR_headers
gethex(sp,fpi,exp,bp,sign)407b36286aSmartynas gethex(sp, fpi, exp, bp, sign)
417b36286aSmartynas 	CONST char **sp; FPI *fpi; Long *exp; Bigint **bp; int sign;
427b36286aSmartynas #else
437b36286aSmartynas gethex( CONST char **sp, FPI *fpi, Long *exp, Bigint **bp, int sign)
447b36286aSmartynas #endif
457b36286aSmartynas {
467b36286aSmartynas 	Bigint *b;
477b36286aSmartynas 	CONST unsigned char *decpt, *s0, *s, *s1;
48aad11945Smartynas 	int big, esign, havedig, irv, j, k, n, n0, nbits, up, zret;
497b36286aSmartynas 	ULong L, lostbits, *x;
507b36286aSmartynas 	Long e, e1;
517b36286aSmartynas #ifdef USE_LOCALE
52aad11945Smartynas 	int i;
53aad11945Smartynas #ifdef NO_LOCALE_CACHE
54aad11945Smartynas 	const unsigned char *decimalpoint = (unsigned char*)localeconv()->decimal_point;
557b36286aSmartynas #else
56aad11945Smartynas 	const unsigned char *decimalpoint;
57aad11945Smartynas 	static unsigned char *decimalpoint_cache;
58aad11945Smartynas 	if (!(s0 = decimalpoint_cache)) {
59aad11945Smartynas 		s0 = (unsigned char*)localeconv()->decimal_point;
60*5b44245bSderaadt 		decimalpoint_cache = strdup(s0);
61aad11945Smartynas 	}
62aad11945Smartynas 	decimalpoint = s0;
63aad11945Smartynas #endif
647b36286aSmartynas #endif
657b36286aSmartynas 
667b36286aSmartynas 	if (!hexdig['0'])
67b3b7ef2eSguenther 		__hexdig_init_D2A();
68aad11945Smartynas 	*bp = 0;
697b36286aSmartynas 	havedig = 0;
707b36286aSmartynas 	s0 = *(CONST unsigned char **)sp + 2;
717b36286aSmartynas 	while(s0[havedig] == '0')
727b36286aSmartynas 		havedig++;
737b36286aSmartynas 	s0 += havedig;
747b36286aSmartynas 	s = s0;
757b36286aSmartynas 	decpt = 0;
767b36286aSmartynas 	zret = 0;
777b36286aSmartynas 	e = 0;
78aad11945Smartynas 	if (hexdig[*s])
79aad11945Smartynas 		havedig++;
80aad11945Smartynas 	else {
817b36286aSmartynas 		zret = 1;
82aad11945Smartynas #ifdef USE_LOCALE
83aad11945Smartynas 		for(i = 0; decimalpoint[i]; ++i) {
84aad11945Smartynas 			if (s[i] != decimalpoint[i])
85aad11945Smartynas 				goto pcheck;
86aad11945Smartynas 			}
87aad11945Smartynas 		decpt = s += i;
88aad11945Smartynas #else
89aad11945Smartynas 		if (*s != '.')
907b36286aSmartynas 			goto pcheck;
917b36286aSmartynas 		decpt = ++s;
92aad11945Smartynas #endif
937b36286aSmartynas 		if (!hexdig[*s])
947b36286aSmartynas 			goto pcheck;
957b36286aSmartynas 		while(*s == '0')
967b36286aSmartynas 			s++;
977b36286aSmartynas 		if (hexdig[*s])
987b36286aSmartynas 			zret = 0;
997b36286aSmartynas 		havedig = 1;
1007b36286aSmartynas 		s0 = s;
1017b36286aSmartynas 		}
1027b36286aSmartynas 	while(hexdig[*s])
1037b36286aSmartynas 		s++;
104aad11945Smartynas #ifdef USE_LOCALE
105aad11945Smartynas 	if (*s == *decimalpoint && !decpt) {
106aad11945Smartynas 		for(i = 1; decimalpoint[i]; ++i) {
107aad11945Smartynas 			if (s[i] != decimalpoint[i])
108aad11945Smartynas 				goto pcheck;
109aad11945Smartynas 			}
110aad11945Smartynas 		decpt = s += i;
111aad11945Smartynas #else
112aad11945Smartynas 	if (*s == '.' && !decpt) {
1137b36286aSmartynas 		decpt = ++s;
114aad11945Smartynas #endif
1157b36286aSmartynas 		while(hexdig[*s])
1167b36286aSmartynas 			s++;
117aad11945Smartynas 		}/*}*/
1187b36286aSmartynas 	if (decpt)
1197b36286aSmartynas 		e = -(((Long)(s-decpt)) << 2);
1207b36286aSmartynas  pcheck:
1217b36286aSmartynas 	s1 = s;
122aad11945Smartynas 	big = esign = 0;
1237b36286aSmartynas 	switch(*s) {
1247b36286aSmartynas 	  case 'p':
1257b36286aSmartynas 	  case 'P':
1267b36286aSmartynas 		switch(*++s) {
1277b36286aSmartynas 		  case '-':
1287b36286aSmartynas 			esign = 1;
1297b36286aSmartynas 			/* no break */
1307b36286aSmartynas 		  case '+':
1317b36286aSmartynas 			s++;
1327b36286aSmartynas 		  }
1337b36286aSmartynas 		if ((n = hexdig[*s]) == 0 || n > 0x19) {
1347b36286aSmartynas 			s = s1;
1357b36286aSmartynas 			break;
1367b36286aSmartynas 			}
1377b36286aSmartynas 		e1 = n - 0x10;
138aad11945Smartynas 		while((n = hexdig[*++s]) !=0 && n <= 0x19) {
139aad11945Smartynas 			if (e1 & 0xf8000000)
140aad11945Smartynas 				big = 1;
1417b36286aSmartynas 			e1 = 10*e1 + n - 0x10;
142aad11945Smartynas 			}
1437b36286aSmartynas 		if (esign)
1447b36286aSmartynas 			e1 = -e1;
1457b36286aSmartynas 		e += e1;
1467b36286aSmartynas 	  }
1477b36286aSmartynas 	*sp = (char*)s;
1487b36286aSmartynas 	if (!havedig)
149aad11945Smartynas 		*sp = (char*)s0 - 1;
150aad11945Smartynas 	if (zret)
1517b36286aSmartynas 		return STRTOG_Zero;
152aad11945Smartynas 	if (big) {
153aad11945Smartynas 		if (esign) {
154aad11945Smartynas 			switch(fpi->rounding) {
155aad11945Smartynas 			  case FPI_Round_up:
156aad11945Smartynas 				if (sign)
157aad11945Smartynas 					break;
158aad11945Smartynas 				goto ret_tiny;
159aad11945Smartynas 			  case FPI_Round_down:
160aad11945Smartynas 				if (!sign)
161aad11945Smartynas 					break;
162aad11945Smartynas 				goto ret_tiny;
163aad11945Smartynas 			  }
164aad11945Smartynas 			goto retz;
165aad11945Smartynas  ret_tiny:
166aad11945Smartynas 			b = Balloc(0);
167384cfdc1Smartynas 			if (b == NULL)
168384cfdc1Smartynas 				return (STRTOG_NoMemory);
169aad11945Smartynas 			b->wds = 1;
170aad11945Smartynas 			b->x[0] = 1;
171aad11945Smartynas 			goto dret;
172aad11945Smartynas 			}
173aad11945Smartynas 		switch(fpi->rounding) {
174aad11945Smartynas 		  case FPI_Round_near:
175aad11945Smartynas 			goto ovfl1;
176aad11945Smartynas 		  case FPI_Round_up:
177aad11945Smartynas 			if (!sign)
178aad11945Smartynas 				goto ovfl1;
179aad11945Smartynas 			goto ret_big;
180aad11945Smartynas 		  case FPI_Round_down:
181aad11945Smartynas 			if (sign)
182aad11945Smartynas 				goto ovfl1;
183aad11945Smartynas 			goto ret_big;
184aad11945Smartynas 		  }
185aad11945Smartynas  ret_big:
186aad11945Smartynas 		nbits = fpi->nbits;
187aad11945Smartynas 		n0 = n = nbits >> kshift;
188aad11945Smartynas 		if (nbits & kmask)
189aad11945Smartynas 			++n;
190aad11945Smartynas 		for(j = n, k = 0; j >>= 1; ++k);
191aad11945Smartynas 		*bp = b = Balloc(k);
1921a653cbcSmartynas 		if (*bp == NULL)
193384cfdc1Smartynas 			return (STRTOG_NoMemory);
194aad11945Smartynas 		b->wds = n;
195aad11945Smartynas 		for(j = 0; j < n0; ++j)
196aad11945Smartynas 			b->x[j] = ALL_ON;
197aad11945Smartynas 		if (n > n0)
198aad11945Smartynas 			b->x[j] = ULbits >> (ULbits - (nbits & kmask));
199aad11945Smartynas 		*exp = fpi->emin;
200aad11945Smartynas 		return STRTOG_Normal | STRTOG_Inexlo;
2017b36286aSmartynas 		}
2027b36286aSmartynas 	n = s1 - s0 - 1;
2031a653cbcSmartynas 	for(k = 0; n > (1 << (kshift-2)) - 1; n >>= 1)
2047b36286aSmartynas 		k++;
2057b36286aSmartynas 	b = Balloc(k);
206384cfdc1Smartynas 	if (b == NULL)
207384cfdc1Smartynas 		return (STRTOG_NoMemory);
2087b36286aSmartynas 	x = b->x;
2097b36286aSmartynas 	n = 0;
2107b36286aSmartynas 	L = 0;
211aad11945Smartynas #ifdef USE_LOCALE
212aad11945Smartynas 	for(i = 0; decimalpoint[i+1]; ++i);
213aad11945Smartynas #endif
2147b36286aSmartynas 	while(s1 > s0) {
215aad11945Smartynas #ifdef USE_LOCALE
216aad11945Smartynas 		if (*--s1 == decimalpoint[i]) {
217aad11945Smartynas 			s1 -= i;
2187b36286aSmartynas 			continue;
219aad11945Smartynas 			}
220aad11945Smartynas #else
221aad11945Smartynas 		if (*--s1 == '.')
222aad11945Smartynas 			continue;
223aad11945Smartynas #endif
2241a653cbcSmartynas 		if (n == ULbits) {
2257b36286aSmartynas 			*x++ = L;
2267b36286aSmartynas 			L = 0;
2277b36286aSmartynas 			n = 0;
2287b36286aSmartynas 			}
2297b36286aSmartynas 		L |= (hexdig[*s1] & 0x0f) << n;
2307b36286aSmartynas 		n += 4;
2317b36286aSmartynas 		}
2327b36286aSmartynas 	*x++ = L;
2337b36286aSmartynas 	b->wds = n = x - b->x;
2341a653cbcSmartynas 	n = ULbits*n - hi0bits(L);
2357b36286aSmartynas 	nbits = fpi->nbits;
2367b36286aSmartynas 	lostbits = 0;
2377b36286aSmartynas 	x = b->x;
2387b36286aSmartynas 	if (n > nbits) {
2397b36286aSmartynas 		n -= nbits;
2407b36286aSmartynas 		if (any_on(b,n)) {
2417b36286aSmartynas 			lostbits = 1;
2427b36286aSmartynas 			k = n - 1;
2437b36286aSmartynas 			if (x[k>>kshift] & 1 << (k & kmask)) {
2447b36286aSmartynas 				lostbits = 2;
245aad11945Smartynas 				if (k > 0 && any_on(b,k))
2467b36286aSmartynas 					lostbits = 3;
2477b36286aSmartynas 				}
2487b36286aSmartynas 			}
2497b36286aSmartynas 		rshift(b, n);
2507b36286aSmartynas 		e += n;
2517b36286aSmartynas 		}
2527b36286aSmartynas 	else if (n < nbits) {
2537b36286aSmartynas 		n = nbits - n;
2547b36286aSmartynas 		b = lshift(b, n);
255384cfdc1Smartynas 		if (b == NULL)
256384cfdc1Smartynas 			return (STRTOG_NoMemory);
2577b36286aSmartynas 		e -= n;
2587b36286aSmartynas 		x = b->x;
2597b36286aSmartynas 		}
2607b36286aSmartynas 	if (e > fpi->emax) {
2617b36286aSmartynas  ovfl:
2627b36286aSmartynas 		Bfree(b);
263aad11945Smartynas  ovfl1:
264aad11945Smartynas #ifndef NO_ERRNO
265aad11945Smartynas 		errno = ERANGE;
266aad11945Smartynas #endif
2677b36286aSmartynas 		return STRTOG_Infinite | STRTOG_Overflow | STRTOG_Inexhi;
2687b36286aSmartynas 		}
2697b36286aSmartynas 	irv = STRTOG_Normal;
2707b36286aSmartynas 	if (e < fpi->emin) {
2717b36286aSmartynas 		irv = STRTOG_Denormal;
2727b36286aSmartynas 		n = fpi->emin - e;
2737b36286aSmartynas 		if (n >= nbits) {
2747b36286aSmartynas 			switch (fpi->rounding) {
2757b36286aSmartynas 			  case FPI_Round_near:
2767b36286aSmartynas 				if (n == nbits && (n < 2 || any_on(b,n-1)))
2777b36286aSmartynas 					goto one_bit;
2787b36286aSmartynas 				break;
2797b36286aSmartynas 			  case FPI_Round_up:
2807b36286aSmartynas 				if (!sign)
2817b36286aSmartynas 					goto one_bit;
2827b36286aSmartynas 				break;
2837b36286aSmartynas 			  case FPI_Round_down:
2847b36286aSmartynas 				if (sign) {
2857b36286aSmartynas  one_bit:
2867b36286aSmartynas 					x[0] = b->wds = 1;
287aad11945Smartynas  dret:
2887b36286aSmartynas 					*bp = b;
289aad11945Smartynas 					*exp = fpi->emin;
290aad11945Smartynas #ifndef NO_ERRNO
291aad11945Smartynas 					errno = ERANGE;
292aad11945Smartynas #endif
2937b36286aSmartynas 					return STRTOG_Denormal | STRTOG_Inexhi
2947b36286aSmartynas 						| STRTOG_Underflow;
2957b36286aSmartynas 					}
2967b36286aSmartynas 			  }
2977b36286aSmartynas 			Bfree(b);
298aad11945Smartynas  retz:
299aad11945Smartynas #ifndef NO_ERRNO
300aad11945Smartynas 			errno = ERANGE;
301aad11945Smartynas #endif
3027b36286aSmartynas 			return STRTOG_Zero | STRTOG_Inexlo | STRTOG_Underflow;
3037b36286aSmartynas 			}
3047b36286aSmartynas 		k = n - 1;
3057b36286aSmartynas 		if (lostbits)
3067b36286aSmartynas 			lostbits = 1;
3077b36286aSmartynas 		else if (k > 0)
3087b36286aSmartynas 			lostbits = any_on(b,k);
3097b36286aSmartynas 		if (x[k>>kshift] & 1 << (k & kmask))
3107b36286aSmartynas 			lostbits |= 2;
3117b36286aSmartynas 		nbits -= n;
3127b36286aSmartynas 		rshift(b,n);
3137b36286aSmartynas 		e = fpi->emin;
3147b36286aSmartynas 		}
3157b36286aSmartynas 	if (lostbits) {
3167b36286aSmartynas 		up = 0;
3177b36286aSmartynas 		switch(fpi->rounding) {
3187b36286aSmartynas 		  case FPI_Round_zero:
3197b36286aSmartynas 			break;
3207b36286aSmartynas 		  case FPI_Round_near:
3217b36286aSmartynas 			if (lostbits & 2
3221a653cbcSmartynas 			 && (lostbits | x[0]) & 1)
3237b36286aSmartynas 				up = 1;
3247b36286aSmartynas 			break;
3257b36286aSmartynas 		  case FPI_Round_up:
3267b36286aSmartynas 			up = 1 - sign;
3277b36286aSmartynas 			break;
3287b36286aSmartynas 		  case FPI_Round_down:
3297b36286aSmartynas 			up = sign;
3307b36286aSmartynas 		  }
3317b36286aSmartynas 		if (up) {
3327b36286aSmartynas 			k = b->wds;
3337b36286aSmartynas 			b = increment(b);
334384cfdc1Smartynas 			if (b == NULL)
335384cfdc1Smartynas 				return (STRTOG_NoMemory);
3367b36286aSmartynas 			x = b->x;
3377b36286aSmartynas 			if (irv == STRTOG_Denormal) {
3387b36286aSmartynas 				if (nbits == fpi->nbits - 1
3397b36286aSmartynas 				 && x[nbits >> kshift] & 1 << (nbits & kmask))
3407b36286aSmartynas 					irv =  STRTOG_Normal;
3417b36286aSmartynas 				}
3427b36286aSmartynas 			else if (b->wds > k
3431a653cbcSmartynas 			 || ((n = nbits & kmask) !=0
3441a653cbcSmartynas 			      && hi0bits(x[k-1]) < 32-n)) {
3457b36286aSmartynas 				rshift(b,1);
3467b36286aSmartynas 				if (++e > fpi->emax)
3477b36286aSmartynas 					goto ovfl;
3487b36286aSmartynas 				}
3497b36286aSmartynas 			irv |= STRTOG_Inexhi;
3507b36286aSmartynas 			}
3517b36286aSmartynas 		else
3527b36286aSmartynas 			irv |= STRTOG_Inexlo;
3537b36286aSmartynas 		}
3547b36286aSmartynas 	*bp = b;
3557b36286aSmartynas 	*exp = e;
3567b36286aSmartynas 	return irv;
3577b36286aSmartynas 	}
358