[uClibc]here's a new version of strtod
Manuel Novoa III
mnovoa3 at bellsouth.net
Mon Dec 11 23:58:05 UTC 2000
Erik,
Here's an implementation of strtod, along with one support routine.
Compiling with -Os -fomit-frame-pointer for i386, I get the sizes
(for all data = 0, bss = 0) for the text section for various selections
of compile-time options:
270 current version converted to doubles + 2 bugs fixed
227 above, but uses isdigit macro and switch for sign
219 new strtod (stripped-down) version included below
222 above + RESTRICT_EXP (prevents exponent overflow)
299 above + WANT_ENDPTR (handles endptr properly)
317 above + RESTRICT_DIGITS (smarter mantissa processing)
329 above + LOG_SCALING (smarter scaling)
389 above + STRTOD_ERRNO (sets errno - needs zoicheck.o)
45 zoicheck.o (needed for errno support)
268 new strtod + RESTRICT_EXP + RESTRICT_DIGITS
281 above + LOG_SCALING
What do you think?
Also, should these kinds of build options be moved to features.h?
Or perhaps something like _uClibc.h which is included by features?
Manuel
------------------- misc/internals/zoicheck.c --------------------
/*
* Copyright (C) 2000 Manuel Novoa III
*
* This is a utility routine for strtod errno support.
* As the name implies, it checks if a double is either 0 or +/-infinity.
* Doing this inline doesn't work on i386 because of excess precission
* stored in the FPU.
*
* TODO: Check bitmasks directly?
*/
int _zero_or_inf_check(double x)
{
return ( x == x/4 );
}
------------------- stdlib/strtod.c ------------------------------
/*
* Copyright (C) 2000 Manuel Novoa III
*
* Notes:
*
* The primary objective of this implementation was minimal size while
* providing robustness and resonable accuracy.
*
* This implementation depends on IEEE floating point behavior and expects
* to be able to generate +/- infinity as a result.
*
* There are a number of compile-time options below.
*
*/
/*****************************************************************************/
/* OPTIONS */
/*****************************************************************************/
/* Set if we want to scale with a O(log2(exp)) multiplications. */
#define _STRTOD_LOG_SCALING 0
/* Set if we want strtod to set errno appropriately. */
/* NOTE: Implies all options below and pulls in _zero_or_inf_check. */
#define _STRTOD_ERRNO 0
/* Set if we want support for the endptr arg. */
/* Implied by _STRTOD_ERRNO. */
#define _STRTOD_ENDPTR 0
/* Set if we want to prevent overflow in accumulating the exponent. */
#define _STRTOD_RESTRICT_EXP 0
/* Set if we want to process mantissa digits more intelligently. */
/* Implied by _STRTOD_ERRNO. */
#define _STRTOD_RESTRICT_DIGITS 0
/* Set if we want to skip scaling 0 for the exponent. */
/* Implied by _STRTOD_ERRNO. */
#define _STRTOD_ZERO_CHECK 0
/*****************************************************************************/
/* Don't change anything that follows. */
/*****************************************************************************/
#if _STRTOD_ERRNO
#undef _STRTOD_ENDPTR
#undef _STRTOD_RESTRICT_EXP
#undef _STRTOD_RESTRICT_DIGITS
#undef _STRTOD_ZERO_CHECK
#define _STRTOD_ENDPTR 1
#define _STRTOD_RESTRICT_EXP 1
#define _STRTOD_RESTRICT_DIGITS 1
#define _STRTOD_ZERO_CHECK 1
#endif
/*****************************************************************************/
#include <stdlib.h>
#include <float.h>
#if _STRTOD_RESTRICT_DIGITS
#define MAX_SIG_DIGITS 20
#define EXP_DENORM_ADJUST MAX_SIG_DIGITS
#define MAX_ALLOWED_EXP (MAX_SIG_DIGITS + EXP_DENORM_ADJUST - DBL_MIN_10_EXP)
#if DBL_DIG > MAX_SIG_DIGITS
#error need to adjust MAX_SIG_DIGITS
#endif
#include <limits.h>
#if MAX_ALLOWED_EXP > INT_MAX
#error size assumption violated for MAX_ALLOWED_EXP
#endif
#else
/* We want some excess if we're not restricting mantissa digits. */
#define MAX_ALLOWED_EXP ((20 - DBL_MIN_10_EXP) * 2)
#endif
/* Note: For i386 the macro resulted in smaller code than the function call. */
#if 1
#undef isdigit
#define isdigit(x) ( (x >= '0') && (x <= '9') )
#else
#include <ctype.h>
#endif
#if _STRTOD_ERRNO
#include <errno.h>
extern int _zero_or_inf_check(double x);
#endif
double strtod(const char *str, char **endptr)
{
double number;
#if _STRTOD_LOG_SCALING
double p10;
#endif
char *pos0;
#if _STRTOD_ENDPTR
char *pos1;
#endif
char *pos = (char *) str;
int exponent_power;
int exponent_temp;
int negative;
#if _STRTOD_RESTRICT_DIGITS || _STRTOD_ENDPTR
int num_digits;
#endif
while (isspace(*pos)) { /* skip leading whitespace */
++pos;
}
negative = 0;
switch(*pos) { /* handle optional sign */
case '-': negative = 1; /* fall through to increment position */
case '+': ++pos;
}
number = 0.;
#if _STRTOD_RESTRICT_DIGITS || _STRTOD_ENDPTR
num_digits = -1;
#endif
exponent_power = 0;
pos0 = NULL;
LOOP:
while (isdigit(*pos)) { /* process string of digits */
#if _STRTOD_RESTRICT_DIGITS
if (num_digits < 0) { /* first time through? */
++num_digits; /* we've now seen a digit */
}
if (num_digits || (*pos != '0')) { /* had/have nonzero */
++num_digits;
if (num_digits <= MAX_SIG_DIGITS) { /* is digit significant */
number = number * 10. + (*pos - '0');
}
}
#else
#if _STRTOD_ENDPTR
++num_digits;
#endif
number = number * 10. + (*pos - '0');
#endif
++pos;
}
if ((*pos == '.') && !pos0) { /* is this the first decimal point? */
pos0 = ++pos; /* save position of decimal point */
goto LOOP; /* and process rest of digits */
}
#if _STRTOD_ENDPTR
if (num_digits<0) { /* must have at least one digit */
pos = (char *) str;
goto DONE;
}
#endif
#if _STRTOD_RESTRICT_DIGITS
if (num_digits > MAX_SIG_DIGITS) { /* adjust exponent for skipped digits */
exponent_power += num_digits - MAX_SIG_DIGITS;
}
#endif
if (pos0) {
exponent_power += pos0 - pos; /* adjust exponent for decimal point */
}
if (negative) { /* correct for sign */
number = -number;
negative = 0; /* reset for exponent processing below */
}
/* process an exponent string */
if (*pos == 'e' || *pos == 'E') {
#if _STRTOD_ENDPTR
pos1 = pos;
#endif
switch(*++pos) { /* handle optional sign */
case '-': negative = 1; /* fall through to increment pos */
case '+': ++pos;
}
pos0 = pos;
exponent_temp = 0;
while (isdigit(*pos)) { /* process string of digits */
#if _STRTOD_RESTRICT_EXP
if (exponent_temp < MAX_ALLOWED_EXP) { /* overflow check */
exponent_temp = exponent_temp * 10 + (*pos - '0');
}
#else
exponent_temp = exponent_temp * 10 + (*pos - '0');
#endif
++pos;
}
#if _STRTOD_ENDPTR
if (pos == pos0) { /* were there no digits? */
pos = pos1; /* back up to e|E */
} /* else */
#endif
if (negative) {
exponent_power -= exponent_temp;
} else {
exponent_power += exponent_temp;
}
}
#if _STRTOD_ZERO_CHECK
if (number == 0.) {
goto DONE;
}
#endif
/* scale the result */
#if _STRTOD_LOG_SCALING
exponent_temp = exponent_power;
p10 = 10.;
if (exponent_temp < 0) {
exponent_temp = -exponent_temp;
}
while (exponent_temp) {
if (exponent_temp & 1) {
if (exponent_power < 0) {
number /= p10;
} else {
number *= p10;
}
}
exponent_temp >>= 1;
p10 *= p10;
}
#else
while (exponent_power) {
if (exponent_power < 0) {
number /= 10.;
exponent_power++;
} else {
number *= 10.;
exponent_power--;
}
}
#endif
#if _STRTOD_ERRNO
if (_zero_or_inf_check(number)) {
errno=ERANGE;
}
#endif
DONE:
#if _STRTOD_ENDPTR
if (endptr) {
*endptr = pos;
}
#endif
return number;
}
More information about the uClibc
mailing list