/* * Copyright (C) 1991, 1992 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id: strtod.c,v 1.1 1995/03/14 12:23:58 tom Exp $ * $Log: strtod.c,v $ * Revision 1.1 1995/03/14 12:23:58 tom * Initial revision */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #ifdef HAVE_FLOAT_H #include #else #define DBL_MAX 1.7976931348623159e+308 #define DBL_MIN 2.2250738585072010e-308 #endif #ifdef STDC_HEADERS #include #include #else #define NULL 0 extern int errno; #ifndef HUGE_VAL #define HUGE_VAL HUGE #endif #endif /* * 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. */ double strtod (nptr, endptr) const char *nptr; char **endptr; { register 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 && *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; }