nest-open-source / nest-learning-thermostat / 5.1.1 / parted / refs/heads/master / . / parted-1.8.7 / lib / strtod.c

/* Copyright (C) 1991, 1992, 1997, 1999, 2003, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ | |

#include <config.h> | |

#include <errno.h> | |

#include <ctype.h> | |

#include <math.h> | |

#include <float.h> | |

#include <stdlib.h> | |

#include <string.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. */ | |

double | |

strtod (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 ((unsigned char) *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 ('0' <= *s && *s <= '9') | |

{ | |

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 ((unsigned char) *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; | |

} |