/********** Copyright 1990 Regents of the University of California. All rights reserved. Author: 1985 Wayne A. Christopher, U. C. Berkeley CAD Group **********/ /* This routine parses a number. */ #include #include #include #include "ngspice/ngspice.h" #include "ngspice/bool.h" #include "ngspice/ftedefs.h" #include "numparse.h" bool ft_strictnumparse = FALSE; static int get_decimal_number(const char **p_str, double *p_val); /* Parse a number. This will handle things like 10M, etc... If the number * must not end before the end of the string, then whole is TRUE. * If whole is FALSE and there is more left to the number, the argument * is advanced to the end of the word. Returns -1. * if no number can be found or if there are trailing characters when * whole is TRUE. * * If ft_strictnumparse is TRUE, and whole is FALSE, the first of the * trailing characters must be a '_'. * * Return codes * +1: String represented an integer number that was converted to a double * but which can be stored as an int without loss of data * 0: String represented a non-integer number that was converted to a double * that may not be expressed as an integer. * -1: Conversion failure */ int ft_numparse(char **p_str, bool whole, double *p_val) { double mant; double expo; const char *p_cur = *p_str; /* position in string */ /* Parse the mantissa (or decimal number if no exponent) */ if (get_decimal_number(&p_cur, &mant) < 0) { return -1; } /* Now look for the scale factor or the exponent (can't have both). */ switch (*p_cur) { case 'e': case 'E': /* Parse another number. Note that a decimal number such as 1.23 * is allowed as the exponent */ ++p_cur; if (get_decimal_number(&p_cur, &expo) < 0) { expo = 0.0; --p_cur; /* The "E" was not part of the number */ } break; case 't': case 'T': expo = 12.0; ++p_cur; break; case 'g': case 'G': expo = 9.0; ++p_cur; break; case 'k': case 'K': expo = 3.0; ++p_cur; break; case 'u': case 'U': expo = -6.0; ++p_cur; break; case 'n': case 'N': expo = -9.0; ++p_cur; break; case 'p': case 'P': expo = -12.0; ++p_cur; break; case 'f': case 'F': expo = -15.0; ++p_cur; break; case 'a': case 'A': expo = -18.0; ++p_cur; break; case 'm': case 'M': { char ch_cur; /* Can be either m, mil, or meg. */ if (((ch_cur = p_cur[1]) == 'e' || ch_cur == 'E') && (((ch_cur = p_cur[2]) == 'g') || ch_cur == 'G')) { expo = 6.0; p_cur += 3; } else if (((ch_cur = p_cur[1]) == 'i' || ch_cur == 'I') && (((ch_cur = p_cur[2]) == 'l') || ch_cur == 'L')) { expo = -6.0; mant *= 25.4; p_cur += 3; } else { /* plain m for milli */ expo = -3.0; ++p_cur; } break; } default: expo = 0.0; } /* p_cur is now pointing to the fist char after the number */ { /* If whole is true, it must be the end of the string */ const char ch_cur = *p_cur; if (whole && ch_cur != '\0') { return -1; } /* If ft_strictnumparse is true, the first character after the * string representing the number, if any, must be '_' */ if (ft_strictnumparse && ch_cur != '\0' && ch_cur != '_') { return -1; } } /* Remove the alpha and '_' characters after the number */ for ( ; ; ++p_cur) { const char ch_cur = *p_cur; if (!isalpha(ch_cur) && ch_cur != '_') { break; } } /* Return results */ { /* Value of number. Ternary operator used to prevent avoidable * calls to pow(). */ const double val = *p_val = mant * (expo == 0.0 ? 1.0 : pow(10.0, expo)); *p_str = (char *) p_cur; /* updated location in string */ if (ft_parsedb) { /* diagnostics for parsing the number */ fprintf(cp_err, "numparse: got %e, left = \"%s\"\n", val, p_cur); } /* Test if the number can be represented as an intger */ return (double) (int) val == val; } } /* end of function ft_numparse */ /* This function converts the string form of a decimal number at *p_str to * its value and returns it in *p_val. The location in *p_str is advanced * to the first character after the number if the conversion is OK and * is unchanged otherwise. * * Return codes * -1: Conversion failure. *p_val is unchanged * 0: Conversion OK. The string was not the representation of an integer * +1: Conversion OK. The string was an integer */ static int get_decimal_number(const char **p_str, double *p_val) { double sign = 1.0; /* default sign multiplier if missing is 1.0 */ const char *p_cur = *p_str; char ch_cur = *p_cur; /* 1st char */ bool f_is_integer = TRUE; /* assume integer */ /* Test for a sign */ if (ch_cur == '+') { /* Advance position in string. Sign unchanged */ ch_cur = *++p_cur; } else if (ch_cur == '-') { /* Advance position in string. Sign = -1 */ ch_cur = *++p_cur; sign = -1.0; } /* Ensure string either starts with a digit or a decimal point followed * by a digit */ if ((!isdigit(ch_cur) && ch_cur != '.') || ((ch_cur == '.') && !isdigit_c(p_cur[1]))) { return -1; } /* Parse and compute the number. Assuming 0-9 digits are contiguous and * increasing in char representation (true for ASCII and EBCDIC) */ double val = 0.0; for ( ; ; p_cur++) { const unsigned int digit = (unsigned int) *p_cur - (unsigned int) '0'; if (digit > 9) { /* not digit */ break; } val = val * 10.0 + (double) digit; } /* Handle fraction, if any */ if (*p_cur == '.') { const char *p0 = ++p_cur; /* start of fraction */ double numerator = 0.0; /* Not an integer expression (even if no fraction after the '.') */ f_is_integer = FALSE; /* Add the fractional part of the number */ for ( ; ; p_cur++) { const unsigned int digit = (unsigned int) *p_cur - (unsigned int) '0'; if (digit > 9) { /* not digit */ /* Add fractional part to intergral part from earlier */ val += numerator * pow(10, (double) (p0 - p_cur)); break; } numerator = numerator * 10.0 + (double) digit; } } /* end of case of fraction */ /* Return the value and update the position in the string */ *p_val = sign * val; *p_str = p_cur; return (int) f_is_integer; } /* end of function get_decimal_number */