Linux Audio

Check our new training course

Embedded Linux Audio

Check our new training course
with Creative Commons CC-BY-SA
lecture materials

Bootlin logo

Elixir Cross Referencer

Loading...
#include <stdlib.h>
#include <errno.h>
#include <ctype.h>

static int valid_exp(const unsigned char *s)
{
	return isdigit(*s) || ((s[0]=='+'||s[0]=='-') && isdigit(s[1]));
}

long double strtold(const char *s1, char **p)
{
	const unsigned char *s = (void *)s1;
	long double x = 0;
	long double frac;
	int sign = 0;
	int nonzero = 0;
	int radix = '.';
	long e;
	int saved_errno = errno;

	if (!p) p = (char **)&s1;

	/* Initial whitespace */
	for (; isspace(*s); s++);

	/* Optional sign */
	if (*s == '-') sign = *s++;
	else if (*s == '+') s++;

	/* Handle infinities and NaNs. */
	if ((s[0]|32)=='i' && (s[1]|32)=='n' && (s[2]|32)=='f') {
		*p = (char *)s + 3;
		return sign ? -1.0/0.0 : 1.0/0.0;
	} else if ((s[0]|32)=='n' && (s[1]|32)=='a' && (s[2]|32)=='n') {
		*p = (char *)s + 3;
		return 0.0/0.0;
	}

	/* Possible hex float */
	if (s[0]=='0' && (s[1]|32)=='x') {
		/* Mantissa must be non-degenerate */
		if (!isxdigit(s[2]) && (s[2]!=radix || !isxdigit(s[3]))) {
			/* Decimal float 0, 'x' extraneous */
			*p = (char *)++s;
			return 0;
		}
		/* We have a real hex float */
		s += 2;
		for (; isxdigit(*s); s++) {
			x = 16*x + (isdigit(*s)?*s-'0':(*s|32)-'a'+10);
			if (*s!='0') nonzero=1;
		}
		if (*s == radix) {
			frac = 1.0/16.0;
			for (s++; isxdigit(*s); s++) {
				x += frac * (isdigit(*s)?*s-'0':(*s|32)-'a'+10);
				frac *= 1.0/16.0;
				if (*s!='0') nonzero=1;
			}
		}
		if ((*s|32) == 'p' && valid_exp(s+1)) {
			e = strtol((void *)(s+1), (void *)&s, 10);
			for (; e>0; e--) x *= 2.0;
			for (; e<0; e++) x *= 0.5;
		}
		goto finish;
	}

	/* Mantissa must be non-degenerate */
	if (!isdigit(s[0]) && (s[0]!=radix || !isdigit(s[1]))) {
		*p = (char *)s1;
		return 0;
	}

	for (; isdigit(*s); s++) {
		x = 10*x + *s-'0';
		if (*s!='0') nonzero=1;
	}
	if (*s == radix) {
		frac = 10.0;
		for (s++; isdigit(*s); s++) {
			x += (*s-'0') / frac;
			frac *= 10.0;
			if (*s!='0') nonzero=1;
		}
	}
	if ((*s|32)=='e' && valid_exp(s+1)) {
		e = strtol((void *)++s, (void *)&s, 10);
		for (; e>0; e--) x *= 10.0;
		for (; e<0; e++) x /= 10.0;
	}
finish:
	errno = ((nonzero && !x) || !(1.0/x)) ? ERANGE : saved_errno;
	*p = (char*)s;
	return sign ? -x : x;
}