diff options
Diffstat (limited to 'src/strtod.c')
| -rw-r--r-- | src/strtod.c | 151 |
1 files changed, 151 insertions, 0 deletions
diff --git a/src/strtod.c b/src/strtod.c new file mode 100644 index 00000000..1a6b3e19 --- /dev/null +++ b/src/strtod.c @@ -0,0 +1,151 @@ +#ifndef lint +static const char SCCSID[]="@(#)strtod.c 4.5 93/12/03 GIE REL"; +#endif +/* THIS CODE HAS BEEN MODIFIED from the distribution made by the FSF. +** However, "licensing" and header information are retained. +*/ +/* Copyright (C) 1991, 1992 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C 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. + +The GNU C 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. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <errno.h> +#include <float.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> +/* Convert NPTR to a double. If ENDPTR is not NULL, a pointer to the + character after the last one used in the number is put in *ENDPTR. */ +#ifdef DOS +typedef int wchar_t; +#ifndef NULL +#define NULL 0 +#endif +#endif + double +strtod(const char *nptr, char **endptr) { + const char *s; + short int sign; + /* The number so far. */ + double num; + int got_dot; /* Found a decimal point. */ + int got_digit; /* Seen any digits. */ + /* The exponent of the number. */ + long int exponent; + + if (nptr == NULL) { + errno = EINVAL; + goto noconv; + } + s = nptr; + /* Eat whitespace. */ + while (isspace(*s)) + ++s; + /* Get the sign. */ + sign = *s == '-' ? -1 : 1; + if (*s == '-' || *s == '+') + ++s; + num = 0.0; + got_dot = 0; + got_digit = 0; + exponent = 0; + for (; ; ++s) { + if (isdigit (*s)) { + got_digit = 1; + /* Make sure that multiplication by 10 will not overflow. */ + if (num > DBL_MAX * 0.1) + /* The value of the digit doesn't matter, since we have already + gotten as many digits as can be represented in a `double'. + This doesn't necessarily mean the result will overflow. + The exponent may reduce it to within range. + + We just need to record that there was another + digit so that we can multiply by 10 later. */ + ++exponent; + else + num = (num * 10.0) + (*s - '0'); + /* Keep track of the number of digits after the decimal point. + If we just divided by 10 here, we would lose precision. */ + if (got_dot) + --exponent; + } else if (!got_dot && (wchar_t) * s == '.') + /* Record that we have found the decimal point. */ + got_dot = 1; + else + /* Any other character terminates the number. */ + break; + } + if (!got_digit) + goto noconv; + if (tolower(*s) == 'e') { + /* Get the exponent specified after the `e' or `E'. */ + int save = errno; + char *end; + long int exp; + + errno = 0; + ++s; + exp = strtol(s, &end, 10); + if (errno == ERANGE) { + /* The exponent overflowed a `long int'. It is probably a safe + assumption that an exponent that cannot be represented by + a `long int' exceeds the limits of a `double'. */ + if (endptr != NULL) + *endptr = end; + if (exp < 0) + goto underflow; + else + goto overflow; + } else if (end == s) + /* There was no exponent. Reset END to point to + the 'e' or 'E', so *ENDPTR will be set there. */ + end = (char *) s - 1; + errno = save; + s = end; + exponent += exp; + } + if (endptr != NULL) + *endptr = (char *) s; + if (num == 0.0) + return 0.0; + /* Multiply NUM by 10 to the EXPONENT power, + checking for overflow and underflow. */ + if (exponent < 0) { + if (num < DBL_MIN * pow(10.0, (double) -exponent)) + goto underflow; + } else if (exponent > 0) { + if (num > DBL_MAX * pow(10.0, (double) -exponent)) + goto overflow; + } + num *= pow(10.0, (double) exponent); + return num * sign; +overflow: + /* Return an overflow error. */ + errno = ERANGE; + return HUGE_VAL * sign; +underflow: + /* Return an underflow error. */ + if (endptr != NULL) + *endptr = (char *) nptr; + errno = ERANGE; + return 0.0; +noconv: + /* There was no number. */ + if (endptr != NULL) + *endptr = (char *) nptr; + return 0.0; +} |
