summaryrefslogtreecommitdiff
path: root/libc/stdlib
diff options
context:
space:
mode:
authorEric Andersen <andersen@codepoet.org>2000-12-20 22:52:58 +0000
committerEric Andersen <andersen@codepoet.org>2000-12-20 22:52:58 +0000
commitc6218dbae579de0cd20f5a7f1e9877673e28225d (patch)
tree0fc8aaf54189b8ef6a2d130c12539814e0a724ee /libc/stdlib
parent97112ff6f4f2a1dcd4c7f8a7512e0a4a02a2a332 (diff)
A number of updates from Manuel Novoa III. Things look good...
Diffstat (limited to 'libc/stdlib')
-rw-r--r--libc/stdlib/Makefile6
-rw-r--r--libc/stdlib/strto_l.c192
-rw-r--r--libc/stdlib/strtod.c312
3 files changed, 442 insertions, 68 deletions
diff --git a/libc/stdlib/Makefile b/libc/stdlib/Makefile
index 8514d5c4a..4d16a8585 100644
--- a/libc/stdlib/Makefile
+++ b/libc/stdlib/Makefile
@@ -26,13 +26,15 @@ LIBC=$(TOPDIR)libc.a
DIRS = $(MALLOC)
+MSRC=strto_l.c
+MOBJ=strtol.o strtoul.o strto_l.o
MSRC2=atexit.c
MOBJ2=on_exit.o atexit.o __do_exit.o exit.o
-CSRC = abort.c getenv.c mktemp.c qsort.c realpath.c strtod.c strtoul.c \
- abs.c bsearch.c mkstemp.c putenv.c rand.c setenv.c strtol.c system.c
+CSRC = abort.c getenv.c mktemp.c qsort.c realpath.c strtod.c \
+ abs.c bsearch.c mkstemp.c putenv.c rand.c setenv.c system.c
COBJS=$(patsubst %.c,%.o, $(CSRC))
diff --git a/libc/stdlib/strto_l.c b/libc/stdlib/strto_l.c
new file mode 100644
index 000000000..a83cf4095
--- /dev/null
+++ b/libc/stdlib/strto_l.c
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2000 Manuel Novoa III
+ *
+ * Notes:
+ *
+ * The primary objective of this implementation was minimal size.
+ *
+ * Note: Assumes char layout 0-9.*A-Z.*a-z for ordinals values.
+ *
+ * There are a couple of compile-time options below.
+ *
+ */
+
+/*****************************************************************************/
+/* OPTIONS */
+/*****************************************************************************/
+
+/* Set if we want strtod to set errno appropriately. */
+/* NOTE: Implies _STRTO_ENDPTR below */
+#define _STRTO_ERRNO 0
+
+/* Set if we want support for the endptr arg. */
+/* Implied by _STRTO_ERRNO. */
+#define _STRTO_ENDPTR 1
+
+/*****************************************************************************/
+/* Don't change anything that follows. */
+/*****************************************************************************/
+
+#if _STRTO_ERRNO
+#undef _STRTO_ENDPTR
+#define _STRTO_ENDPTR 1
+#endif
+
+/*****************************************************************************/
+
+/* Are there actually any machines where this might fail? */
+#if 'A' > 'a'
+#error ordering assumption violated : 'A' > 'a'
+#endif
+
+#include <stdlib.h>
+#include <limits.h>
+#include <ctype.h>
+
+#if _STRTO_ERRNO
+#include <errno.h>
+#endif
+
+unsigned long _strto_l(const char *str, char **endptr, int base, int uflag);
+
+#if L_strto_l
+
+/*
+ * This is the main work fuction which handles both strtol (uflag = 0) and
+ * strtoul (uflag = 1).
+ */
+
+unsigned long _strto_l(const char *str, char **endptr, int base, int uflag)
+{
+ unsigned long number = 0;
+ unsigned long cutoff;
+ char *pos = (char *) str;
+#if _STRTO_ENDPTR
+ char *fail_char = (char *) str;
+#endif
+ int digit, cutoff_digit;
+ int negative;
+
+ while (isspace(*pos)) { /* skip leading whitespace */
+ ++pos;
+ }
+
+ /* handle optional sign */
+ negative = 0;
+ switch(*pos) {
+ case '-': negative = 1; /* fall through to increment pos */
+ case '+': ++pos;
+ }
+
+ if ((base == 16) && (*pos == '0')) { /* handle option prefix */
+ ++pos;
+#if _STRTO_ENDPTR
+ fail_char = pos;
+#endif
+ if ((*pos == 'x') || (*pos == 'X')) {
+ ++pos;
+ }
+ }
+
+ if (base == 0) { /* dynamic base */
+ base = 10; /* default is 10 */
+ if (*pos == '0') {
+ ++pos;
+ base -= 2; /* now base is 8 (or 16) */
+#if _STRTO_ENDPTR
+ fail_char = pos;
+#endif
+ if ((*pos == 'x') || (*pos == 'X')) {
+ base += 8; /* base is 16 */
+ ++pos;
+ }
+ }
+ }
+
+ if ((base < 2) || (base > 36)) { /* illegal base */
+ goto DONE;
+ }
+
+ cutoff = ULONG_MAX / base;
+ cutoff_digit = ULONG_MAX - cutoff * base;
+
+ while (1) {
+ digit = 40;
+ if ((*pos >= '0') && (*pos <= '9')) {
+ digit = (*pos - '0');
+ } else if (*pos >= 'a') {
+ digit = (*pos - 'a' + 10);
+ } else if (*pos >= 'A') {
+ digit = (*pos - 'A' + 10);
+ } else break;
+
+ if (digit >= base) {
+ break;
+ }
+
+ ++pos;
+#if _STRTO_ENDPTR
+ fail_char = pos;
+#endif
+
+ /* adjust number, with overflow check */
+ if ((number > cutoff)
+ || ((number == cutoff) && (digit > cutoff_digit))) {
+ number = ULONG_MAX;
+ if (uflag) {
+ negative = 0; /* since unsigned returns ULONG_MAX */
+ }
+#if _STRTO_ERRNO
+ errno = ERANGE;
+#endif
+ } else {
+ number = number * base + digit;
+ }
+
+ }
+
+ DONE:
+#if _STRTO_ENDPTR
+ if (endptr) {
+ *endptr = fail_char;
+ }
+#endif
+
+ if (negative) {
+ if (!uflag && (number > ((unsigned long)(-(1+LONG_MIN)))+1)) {
+#if _STRTO_ERRNO
+ errno = ERANGE;
+#endif
+ return (unsigned long) LONG_MIN;
+ }
+ return (unsigned long)(-((long)number));
+ } else {
+ if (!uflag && (number > (unsigned long) LONG_MAX)) {
+#if _STRTO_ERRNO
+ errno = ERANGE;
+#endif
+ return LONG_MAX;
+ }
+ return number;
+ }
+}
+
+#endif
+
+#if L_strtoul
+
+unsigned long strtoul(const char *str, char **endptr, int base)
+{
+ return _strto_l(str, endptr, base, 1);
+}
+
+#endif
+
+#if L_strtol
+
+long strtol(const char *str, char **endptr, int base)
+{
+ return _strto_l(str, endptr, base, 0);
+}
+
+#endif
diff --git a/libc/stdlib/strtod.c b/libc/stdlib/strtod.c
index de3aecbd3..dff5aeb73 100644
--- a/libc/stdlib/strtod.c
+++ b/libc/stdlib/strtod.c
@@ -1,83 +1,263 @@
/*
- * strtod.c - This file is part of the libc-8086 package for ELKS,
- * Copyright (C) 1995, 1996 Nat Friedman <ndf@linux.mit.edu>.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
+ * Copyright (C) 2000 Manuel Novoa III
*
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
+ * Notes:
*
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * 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 1
+
+/* 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 1
+
+/* Set if we want to prevent overflow in accumulating the exponent. */
+#define _STRTOD_RESTRICT_EXP 1
+
+/* Set if we want to process mantissa digits more intelligently. */
+/* Implied by _STRTOD_ERRNO. */
+#define _STRTOD_RESTRICT_DIGITS 1
+
+/* 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
+
#include <ctype.h>
+/* Note: For i386 the macro resulted in smaller code than the function call. */
+#if 1
+#undef isdigit
+#define isdigit(x) ( (x >= '0') && (x <= '9') )
+#endif
+
+#if _STRTOD_ERRNO
+#include <errno.h>
+extern int _zero_or_inf_check(double x);
+#endif
-float strtod(const char *nptr, char **endptr)
+double strtod(const char *str, char **endptr)
{
- unsigned short negative;
- float number;
- float fp_part;
- int exponent;
- unsigned short exp_negative;
-
- /* advance beyond any leading whitespace */
- while (isspace(*nptr))
- nptr++;
-
- /* check for optional '+' or '-' */
- negative = 0;
- if (*nptr == '-') {
- negative = 1;
- nptr++;
- } else if (*nptr == '+')
- nptr++;
-
- number = 0;
- while (isdigit(*nptr)) {
- number = number * 10 + (*nptr - '0');
- nptr++;
+ 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 */
+ }
- if (*nptr == '.') {
- nptr++;
- fp_part = 0;
- while (isdigit(*nptr)) {
- fp_part = fp_part / 10.0 + (*nptr - '0') / 10.0;
- nptr++;
- }
- number += fp_part;
+ /* 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;
}
- if (*nptr == 'e' || *nptr == 'E') {
- nptr++;
- exp_negative = 0;
- if (*nptr == '-') {
- exp_negative = 1;
- nptr++;
- } else if (*nptr == '+')
- nptr++;
-
- exponent = 0;
- while (isdigit(*nptr)) {
- exponent = exponent * 10 + (*nptr - '0');
- exponent++;
- }
+ 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;
}
- while (exponent) {
- if (exp_negative)
- number /= 10;
- else
- number *= 10;
- exponent--;
+#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;
}
- return (negative ? -number : number);
+ }
+
+#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;
}