1/* strtod.c - convert string to double-precision floating-point value. */ 2 3/* Copyright (C) 1991, 1992 Free Software Foundation, Inc. 4 5 This file is part of GNU Bash, the Bourne Again SHell. 6 7 Bash is free software: you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation, either version 3 of the License, or 10 (at your option) any later version. 11 12 Bash is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with Bash. If not, see <http://www.gnu.org/licenses/>. 19*/ 20 21#if HAVE_CONFIG_H 22# include <config.h> 23#endif 24 25#ifndef HAVE_STRTOD 26 27#include <errno.h> 28#ifndef errno 29extern int errno; 30#endif 31 32#include <chartypes.h> 33#include <math.h> 34 35#if HAVE_FLOAT_H 36# include <float.h> 37#else 38# define DBL_MAX 1.7976931348623159e+308 39# define DBL_MIN 2.2250738585072010e-308 40#endif 41 42#include <bashansi.h> 43 44#ifndef NULL 45# define NULL 0 46#endif 47 48#ifndef HUGE_VAL 49# define HUGE_VAL HUGE 50#endif 51 52/* Convert NPTR to a double. If ENDPTR is not NULL, a pointer to the 53 character after the last one used in the number is put in *ENDPTR. */ 54double 55strtod (nptr, endptr) 56 const char *nptr; 57 char **endptr; 58{ 59 register const char *s; 60 short sign; 61 62 /* The number so far. */ 63 double num; 64 65 int got_dot; /* Found a decimal point. */ 66 int got_digit; /* Seen any digits. */ 67 68 /* The exponent of the number. */ 69 long int exponent; 70 71 if (nptr == NULL) 72 { 73 errno = EINVAL; 74 goto noconv; 75 } 76 77 s = nptr; 78 79 /* Eat whitespace. */ 80 while (ISSPACE ((unsigned char)*s)) 81 ++s; 82 83 /* Get the sign. */ 84 sign = *s == '-' ? -1 : 1; 85 if (*s == '-' || *s == '+') 86 ++s; 87 88 num = 0.0; 89 got_dot = 0; 90 got_digit = 0; 91 exponent = 0; 92 for (;; ++s) 93 { 94 if (DIGIT (*s)) 95 { 96 got_digit = 1; 97 98 /* Make sure that multiplication by 10 will not overflow. */ 99 if (num > DBL_MAX * 0.1) 100 /* The value of the digit doesn't matter, since we have already 101 gotten as many digits as can be represented in a `double'. 102 This doesn't necessarily mean the result will overflow. 103 The exponent may reduce it to within range. 104 105 We just need to record that there was another 106 digit so that we can multiply by 10 later. */ 107 ++exponent; 108 else 109 num = (num * 10.0) + (*s - '0'); 110 111 /* Keep track of the number of digits after the decimal point. 112 If we just divided by 10 here, we would lose precision. */ 113 if (got_dot) 114 --exponent; 115 } 116 else if (!got_dot && *s == '.') 117 /* Record that we have found the decimal point. */ 118 got_dot = 1; 119 else 120 /* Any other character terminates the number. */ 121 break; 122 } 123 124 if (!got_digit) 125 goto noconv; 126 127 if (TOLOWER ((unsigned char)*s) == 'e') 128 { 129 /* Get the exponent specified after the `e' or `E'. */ 130 int save = errno; 131 char *end; 132 long int exp; 133 134 errno = 0; 135 ++s; 136 exp = strtol (s, &end, 10); 137 if (errno == ERANGE) 138 { 139 /* The exponent overflowed a `long int'. It is probably a safe 140 assumption that an exponent that cannot be represented by 141 a `long int' exceeds the limits of a `double'. */ 142 if (endptr != NULL) 143 *endptr = end; 144 if (exp < 0) 145 goto underflow; 146 else 147 goto overflow; 148 } 149 else if (end == s) 150 /* There was no exponent. Reset END to point to 151 the 'e' or 'E', so *ENDPTR will be set there. */ 152 end = (char *) s - 1; 153 errno = save; 154 s = end; 155 exponent += exp; 156 } 157 158 if (endptr != NULL) 159 *endptr = (char *) s; 160 161 if (num == 0.0) 162 return 0.0; 163 164 /* Multiply NUM by 10 to the EXPONENT power, 165 checking for overflow and underflow. */ 166 167 if (exponent < 0) 168 { 169 if (num < DBL_MIN * pow (10.0, (double) -exponent)) 170 goto underflow; 171 } 172 else if (exponent > 0) 173 { 174 if (num > DBL_MAX * pow (10.0, (double) -exponent)) 175 goto overflow; 176 } 177 178 num *= pow (10.0, (double) exponent); 179 180 return num * sign; 181 182overflow: 183 /* Return an overflow error. */ 184 errno = ERANGE; 185 return HUGE_VAL * sign; 186 187underflow: 188 /* Return an underflow error. */ 189 if (endptr != NULL) 190 *endptr = (char *) nptr; 191 errno = ERANGE; 192 return 0.0; 193 194noconv: 195 /* There was no number. */ 196 if (endptr != NULL) 197 *endptr = (char *) nptr; 198 return 0.0; 199} 200 201#endif /* !HAVE_STRTOD */ 202