Index: uspace/lib/posix/stdlib/strtold.c
===================================================================
--- uspace/lib/posix/stdlib/strtold.c	(revision d43c11740c60507d47737c0458e9238ef68ca93f)
+++ uspace/lib/posix/stdlib/strtold.c	(revision ec18957a189501a5dd40dfc278bd8ebcad3d256b)
@@ -30,5 +30,5 @@
  * @{
  */
-/** @file Backend for floating point conversions.
+/** @file
  */
 
@@ -46,7 +46,4 @@
 #include "../strings.h"
 #include "../errno.h"
-#include "../limits.h"
-
-// FIXME: #include <float.h>
 
 #ifndef HUGE_VALL
@@ -55,56 +52,49 @@
 
 #ifndef abs
-	#define abs(x) (((x) < 0) ? -(x) : (x))
+	#define abs(x) ((x < 0) ? -x : x)
 #endif
 
-/* If the constants are not defined, use double precision as default. */
-#ifndef LDBL_MANT_DIG
-	#define LDBL_MANT_DIG 53
-#endif
-#ifndef LDBL_MAX_EXP
-	#define LDBL_MAX_EXP 1024
-#endif
-#ifndef LDBL_MIN_EXP
-	#define LDBL_MIN_EXP (-1021)
-#endif
-#ifndef LDBL_DIG
-	#define LDBL_DIG 15
-#endif
-#ifndef LDBL_MIN
-	#define LDBL_MIN 2.2250738585072014E-308
-#endif
-
-/* power functions ************************************************************/
-
-#if LDBL_MAX_EXP >= 16384
-const int MAX_POW5 = 12;
-#else
-const int MAX_POW5 = 8;
-#endif
+// TODO: clean up, documentation
+
+// FIXME: ensure it builds and works on all platforms
+
+const int max_small_pow5 = 15;
+
+/* The value at index i is approximately 5**i. */
+long double small_pow5[] = {
+	0x1P0,
+	0x5P0,
+	0x19P0,
+	0x7dP0,
+	0x271P0,
+	0xc35P0,
+	0x3d09P0,
+	0x1312dP0,
+	0x5f5e1P0,
+	0x1dcd65P0,
+	0x9502f9P0,
+	0x2e90eddP0,
+	0xe8d4a51P0,
+	0x48c27395P0,
+	0x16bcc41e9P0,
+	0x71afd498dP0
+};
 
 /* The value at index i is approximately 5**(2**i). */
-long double pow5[] = {
-	0x5p0l,
-	0x19p0l,
-	0x271p0l,
-	0x5F5E1p0l,
-	0x2386F26FC1p0l,
-	0x4EE2D6D415B85ACEF81p0l,
-	0x184F03E93FF9F4DAA797ED6E38ED6p36l,
-	0x127748F9301D319BF8CDE66D86D62p185l,
-	0x154FDD7F73BF3BD1BBB77203731FDp482l,
-#if LDBL_MAX_EXP >= 16384
-	0x1C633415D4C1D238D98CAB8A978A0p1076l,
-	0x192ECEB0D02EA182ECA1A7A51E316p2265l,
-	0x13D1676BB8A7ABBC94E9A519C6535p4643l,
-	0x188C0A40514412F3592982A7F0094p9398l,
-#endif
+long double large_pow5[] = {
+	0x5P0l,
+	0x19P0l,
+	0x271P0l,
+	0x5f5e1P0l,
+	0x2386f26fc1P0l,
+	0x4ee2d6d415b85acef81P0l,
+	0x184f03e93ff9f4daa797ed6e38ed64bf6a1f01P0l,
+	0x24ee91f2603a6337f19bccdb0dac404dc08d3cff5ecP128l,
+	0x553f75fdcefcef46eeddcP512l,
+	0x1c633415d4c1d238d98cab8a978a0b1f138cb07303P1024l,
+	0x325d9d61a05d4305d9434f4a3c62d433949ae6209d492P2200l,
+	0x9e8b3b5dc53d5de4a74d28ce329ace526a3197bbebe3034f77154ce2bcba1964P4500l,
+	0x6230290145104bcd64a60a9fc025254932bb0fd922271133eeae7P9300l
 };
-
-#if LDBL_MAX_EXP >= 16384
-const int MAX_POW2 = 15;
-#else
-const int MAX_POW2 = 9;
-#endif
 
 /* Powers of two. */
@@ -120,41 +110,41 @@
 	0x1P256l,
 	0x1P512l,
-#if LDBL_MAX_EXP >= 16384
 	0x1P1024l,
 	0x1P2048l,
 	0x1P4096l,
-	0x1P8192l,
-#endif
+	0x1P8192l
 };
+
+static inline bool out_of_range(long double num)
+{
+	return num == 0.0l || num == HUGE_VALL;
+}
 
 /**
  * Multiplies a number by a power of five.
- * The result may be inexact and may not be the best possible approximation.
+ * The result is not exact and may not be the best possible approximation.
  *
- * @param mant Number to be multiplied.
- * @param exp Base 5 exponent.
- * @return mant multiplied by 5**exp
- */
-static long double mul_pow5(long double mant, int exp)
-{
-	if (mant == 0.0l || mant == HUGE_VALL) {
-		return mant;
-	}
-	
-	if (abs(exp) >> (MAX_POW5 + 1) != 0) {
-		/* Too large exponent. */
+ * @param base Number to be multiplied.
+ * @param exponent Base 5 exponent.
+ * @return base multiplied by 5**exponent
+ */
+static long double mul_pow5(long double base, int exponent)
+{
+	if (out_of_range(base)) {
+		return base;
+	}
+	
+	if (abs(exponent) >> 13 != 0) {
 		errno = ERANGE;
-		return exp < 0 ? LDBL_MIN : HUGE_VALL;
-	}
-	
-	if (exp < 0) {
-		exp = abs(exp);
-		for (int bit = 0; bit <= MAX_POW5; ++bit) {
-			/* Multiply by powers of five bit-by-bit. */
-			if (((exp >> bit) & 1) != 0) {
-				mant /= pow5[bit];
-				if (mant == 0.0l) {
-					/* Underflow. */
-					mant = LDBL_MIN;
+		return exponent < 0 ? 0.0l : HUGE_VALL;
+	}
+	
+	if (exponent < 0) {
+		exponent = -exponent;
+		base /= small_pow5[exponent & 0xF];
+		for (int i = 4; i < 13; ++i) {
+			if (((exponent >> i) & 1) != 0) {
+				base /= large_pow5[i];
+				if (out_of_range(base)) {
 					errno = ERANGE;
 					break;
@@ -163,10 +153,9 @@
 		}
 	} else {
-		for (int bit = 0; bit <= MAX_POW5; ++bit) {
-			/* Multiply by powers of five bit-by-bit. */
-			if (((exp >> bit) & 1) != 0) {
-				mant *= pow5[bit];
-				if (mant == HUGE_VALL) {
-					/* Overflow. */
+		base *= small_pow5[exponent & 0xF];
+		for (int i = 4; i < 13; ++i) {
+			if (((exponent >> i) & 1) != 0) {
+				base *= large_pow5[i];
+				if (out_of_range(base)) {
 					errno = ERANGE;
 					break;
@@ -176,32 +165,31 @@
 	}
 	
-	return mant;
+	return base;
 }
 
 /**
- * Multiplies a number by a power of two. This is always exact.
+ * Multiplies a number by a power of two.
  *
- * @param mant Number to be multiplied.
- * @param exp Base 2 exponent.
- * @return mant multiplied by 2**exp.
- */
-static long double mul_pow2(long double mant, int exp)
-{
-	if (mant == 0.0l || mant == HUGE_VALL) {
-		return mant;
-	}
-	
-	if (exp > LDBL_MAX_EXP || exp < LDBL_MIN_EXP) {
+ * @param base Number to be multiplied.
+ * @param exponent Base 2 exponent.
+ * @return base multiplied by 2**exponent
+ */
+static long double mul_pow2(long double base, int exponent)
+{
+	if (out_of_range(base)) {
+		return base;
+	}
+	
+	if (abs(exponent) >> 14 != 0) {
 		errno = ERANGE;
-		return exp < 0 ? LDBL_MIN : HUGE_VALL;
-	}
-	
-	if (exp < 0) {
-		exp = abs(exp);
-		for (int i = 0; i <= MAX_POW2; ++i) {
-			if (((exp >> i) & 1) != 0) {
-				mant /= pow2[i];
-				if (mant == 0.0l) {
-					mant = LDBL_MIN;
+		return exponent < 0 ? 0.0l : HUGE_VALL;
+	}
+	
+	if (exponent < 0) {
+		exponent = -exponent;
+		for (int i = 0; i < 14; ++i) {
+			if (((exponent >> i) & 1) != 0) {
+				base /= pow2[i];
+				if (out_of_range(base)) {
 					errno = ERANGE;
 					break;
@@ -210,8 +198,8 @@
 		}
 	} else {
-		for (int i = 0; i <= MAX_POW2; ++i) {
-			if (((exp >> i) & 1) != 0) {
-				mant *= pow2[i];
-				if (mant == HUGE_VALL) {
+		for (int i = 0; i < 14; ++i) {
+			if (((exponent >> i) & 1) != 0) {
+				base *= pow2[i];
+				if (out_of_range(base)) {
 					errno = ERANGE;
 					break;
@@ -221,49 +209,40 @@
 	}
 	
-	return mant;
-}
-
-/* end power functions ********************************************************/
-
-
-
-/**
- * Convert decimal string representation of the floating point number.
- * Function expects the string pointer to be already pointed at the first
- * digit (i.e. leading optional sign was already consumed by the caller).
- * 
- * @param sptr Pointer to the storage of the string pointer. Upon successful
- *     conversion, the string pointer is updated to point to the first
- *     unrecognized character.
- * @return An approximate representation of the input floating-point number.
- */
+	return base;
+}
+
+
 static long double parse_decimal(const char **sptr)
 {
-	assert(sptr != NULL);
-	assert (*sptr != NULL);
+	// TODO: Use strtol(), at least for exponent.
 	
 	const int DEC_BASE = 10;
 	const char DECIMAL_POINT = '.';
 	const char EXPONENT_MARK = 'e';
-	
-	const char *str = *sptr;
-	long double significand = 0;
-	long exponent = 0;
+	/* The highest amount of digits that can be safely parsed
+	 * before an overflow occurs.
+	 */
+	const int PARSE_DECIMAL_DIGS = 19;
+	
+	/* significand */
+	uint64_t significand = 0;
+	
+	/* position in the input string */
+	int i = 0;
 	
 	/* number of digits parsed so far */
 	int parsed_digits = 0;
-	bool after_decimal = false;
-	
-	while (isdigit(*str) || (!after_decimal && *str == DECIMAL_POINT)) {
-		if (*str == DECIMAL_POINT) {
-			after_decimal = true;
-			str++;
-			continue;
-		}
-		
-		if (parsed_digits == 0 && *str == '0') {
+	
+	int exponent = 0;
+	
+	const char *str = *sptr;
+	
+	/* digits before decimal point */
+	while (isdigit(str[i])) {
+		if (parsed_digits == 0 && str[i] == '0') {
 			/* Nothing, just skip leading zeros. */
-		} else if (parsed_digits < LDBL_DIG) {
-			significand = significand * DEC_BASE + (*str - '0');
+		} else if (parsed_digits < PARSE_DECIMAL_DIGS) {
+			significand *= DEC_BASE;
+			significand += str[i] - '0';
 			parsed_digits++;
 		} else {
@@ -271,40 +250,69 @@
 		}
 		
-		if (after_decimal) {
-			/* Decrement exponent if we are parsing the fractional part. */
-			exponent--;
-		}
-		
-		str++;
+		i++;
+	}
+	
+	if (str[i] == DECIMAL_POINT) {
+		i++;
+		
+		/* digits after decimal point */
+		while (isdigit(str[i])) {
+			if (parsed_digits == 0 && str[i] == '0') {
+				/* Skip leading zeros and decrement exponent. */
+				exponent--;
+			} else if (parsed_digits < PARSE_DECIMAL_DIGS) {
+				significand *= DEC_BASE;
+				significand += str[i] - '0';
+				exponent--;
+				parsed_digits++;
+			} else {
+				/* ignore */
+			}
+			
+			i++;
+		}
 	}
 	
 	/* exponent */
-	if (tolower(*str) == EXPONENT_MARK) {
-		str++;
-		
-		/* Returns MIN/MAX value on error, which is ok. */
-		long exp = strtol(str, (char **) &str, DEC_BASE);
-		
-		if (exponent > 0 && exp > LONG_MAX - exponent) {
-			exponent = LONG_MAX;
-		} else if (exponent < 0 && exp < LONG_MIN - exponent) {
-			exponent = LONG_MIN;
-		} else {
-			exponent += exp;
-		}
-	}
-	
-	*sptr = str;
-	
-	/* Return multiplied by a power of ten. */
-	return mul_pow2(mul_pow5(significand, exponent), exponent);
-}
-
-/**
- * Derive a hexadecimal digit from its character representation.
- * 
- * @param ch Character representation of the hexadecimal digit.
- * @return Digit value represented by an integer.
- */
+	if (tolower(str[i]) == EXPONENT_MARK) {
+		i++;
+		
+		bool negative = false;
+		int exp = 0;
+		
+		switch (str[i]) {
+		case '-':
+			negative = true;
+			/* fallthrough */
+		case '+':
+			i++;
+		}
+		
+		while (isdigit(str[i])) {
+			if (exp < 65536) {
+				exp *= DEC_BASE;
+				exp += str[i] - '0';
+			}
+			
+			i++;
+		}
+		
+		if (negative) {
+			exp = -exp;
+		}
+		
+		exponent += exp;
+	}
+	
+	long double result = (long double) significand;
+	result = mul_pow5(result, exponent);
+	if (result != HUGE_VALL) {
+		result = mul_pow2(result, exponent);
+	}
+	
+	*sptr = &str[i];
+	return result;
+}
+
 static inline int hex_value(char ch)
 {
@@ -317,17 +325,24 @@
 
 /**
- * Convert hexadecimal string representation of the floating point number.
- * Function expects the string pointer to be already pointed at the first
- * digit (i.e. leading optional sign and 0x prefix were already consumed
- * by the caller).
- *
- * @param sptr Pointer to the storage of the string pointer. Upon successful
- *     conversion, the string pointer is updated to point to the first
- *     unrecognized character.
- * @return Representation of the input floating-point number.
- */
+ * @param val Integer value.
+ * @return How many leading zero bits there are. (Maximum is 3)
+ */
+static inline int leading_zeros(uint64_t val)
+{
+	for (int i = 3; i > 0; --i) {
+		if ((val >> (64 - i)) == 0) {
+			return i;
+		}
+	}
+	
+	return 0;
+}
+
 static long double parse_hexadecimal(const char **sptr)
 {
-	assert(sptr != NULL && *sptr != NULL);
+	// TODO: Use strtol(), at least for exponent.
+	
+	/* this function currently always rounds to zero */
+	// TODO: honor rounding mode
 	
 	const int DEC_BASE = 10;
@@ -335,56 +350,118 @@
 	const char DECIMAL_POINT = '.';
 	const char EXPONENT_MARK = 'p';
+	/* The highest amount of digits that can be safely parsed
+	 * before an overflow occurs.
+	 */
+	const int PARSE_HEX_DIGS = 16;
+	
+	/* significand */
+	uint64_t significand = 0;
+	
+	/* position in the input string */
+	int i = 0;
+	
+	/* number of digits parsed so far */
+	int parsed_digits = 0;
+	
+	int exponent = 0;
 	
 	const char *str = *sptr;
-	long double significand = 0;
-	long exponent = 0;
-	
-	/* number of bits parsed so far */
-	int parsed_bits = 0;
-	bool after_decimal = false;
-	
-	while (posix_isxdigit(*str) || (!after_decimal && *str == DECIMAL_POINT)) {
-		if (*str == DECIMAL_POINT) {
-			after_decimal = true;
-			str++;
-			continue;
-		}
-		
-		if (parsed_bits == 0 && *str == '0') {
+	
+	/* digits before decimal point */
+	while (posix_isxdigit(str[i])) {
+		if (parsed_digits == 0 && str[i] == '0') {
 			/* Nothing, just skip leading zeros. */
-		} else if (parsed_bits <= LDBL_MANT_DIG) {
-			significand = significand * HEX_BASE + hex_value(*str);
-			parsed_bits += 4;
+		} else if (parsed_digits < PARSE_HEX_DIGS) {
+			significand *= HEX_BASE;
+			significand += hex_value(str[i]);
+			parsed_digits++;
+		} else if (parsed_digits == PARSE_HEX_DIGS) {
+			/* The first digit may have had leading zeros,
+			 * so we need to parse one more digit and shift
+			 * the value accordingly.
+			 */
+			
+			int zeros = leading_zeros(significand);
+			significand = (significand << zeros) |
+			    (hex_value(str[i]) >> (4 - zeros));
+			
+			exponent += (4 - zeros);
+			parsed_digits++;
 		} else {
 			exponent += 4;
 		}
 		
-		if (after_decimal) {
-			exponent -= 4;
-		}
-		
-		str++;
+		i++;
+	}
+	
+	if (str[i] == DECIMAL_POINT) {
+		i++;
+		
+		/* digits after decimal point */
+		while (posix_isxdigit(str[i])) {
+			if (parsed_digits == 0 && str[i] == '0') {
+				/* Skip leading zeros and decrement exponent. */
+				exponent -= 4;
+			} else if (parsed_digits < PARSE_HEX_DIGS) {
+				significand *= HEX_BASE;
+				significand += hex_value(str[i]);
+				exponent -= 4;
+				parsed_digits++;
+			} else if (parsed_digits == PARSE_HEX_DIGS) {
+				/* The first digit may have had leading zeros,
+				 * so we need to parse one more digit and shift
+				 * the value accordingly.
+				 */
+				
+				int zeros = leading_zeros(significand);
+				significand = (significand << zeros) |
+				    (hex_value(str[i]) >> (4 - zeros));
+				
+				exponent -= zeros;
+				parsed_digits++;
+			} else {
+				/* ignore */
+			}
+			
+			i++;
+		}
 	}
 	
 	/* exponent */
-	if (tolower(*str) == EXPONENT_MARK) {
-		str++;
-		
-		/* Returns MIN/MAX value on error, which is ok. */
-		long exp = strtol(str, (char **) &str, DEC_BASE);
-		
-		if (exponent > 0 && exp > LONG_MAX - exponent) {
-			exponent = LONG_MAX;
-		} else if (exponent < 0 && exp < LONG_MIN - exponent) {
-			exponent = LONG_MIN;
-		} else {
-			exponent += exp;
-		}
-	}
-	
-	*sptr = str;
-	
-	/* Return multiplied by a power of two. */
-	return mul_pow2(significand, exponent);
+	if (tolower(str[i]) == EXPONENT_MARK) {
+		i++;
+		
+		bool negative = false;
+		int exp = 0;
+		
+		switch (str[i]) {
+		case '-':
+			negative = true;
+			/* fallthrough */
+		case '+':
+			i++;
+		}
+		
+		while (isdigit(str[i])) {
+			if (exp < 65536) {
+				exp *= DEC_BASE;
+				exp += str[i] - '0';
+			}
+			
+			i++;
+		}
+		
+		if (negative) {
+			exp = -exp;
+		}
+		
+		exponent += exp;
+	}
+	
+	long double result = (long double) significand;
+	result = mul_pow2(result, exponent);
+	
+	*sptr = &str[i];
+	return result;
 }
 
@@ -401,5 +478,5 @@
  * @param nptr Input string.
  * @param endptr If non-NULL, *endptr is set to the position of the first
- *     unrecognized character.
+ *    unrecognized character.
  * @return An approximate representation of the input floating-point number.
  */
@@ -435,8 +512,8 @@
 		
 		if (endptr != NULL) {
-			*endptr = (char *) nptr;
-		}
-		errno = EINVAL;
-		return 0;
+			*endptr = (char *) &nptr[i + 3];
+		}
+		errno = ERANGE;
+		return negative ? -0.0l : +0.0l;
 	}
 	
@@ -490,2 +567,3 @@
 /** @}
  */
+
