Mercurial > octave
diff liboctave/util/oct-string.cc @ 26110:b543cf12c63f
Move octave_str2double to liboctave (patch #9084).
* libinterp/corefcn/strfns.cc: Move "Fstr2double" from "str2double.cc".
* liboctave/util/oct-string.cc: Move functions from "str2double.cc".
* libinterp/util/oct-string.h: Add function "octave_str2double".
* libinterp/corefcn/str2double.cc: Delete file.
author | Markus Mützel <markus.muetzel@gmx.de> |
---|---|
date | Sun, 18 Nov 2018 15:44:36 +0100 |
parents | 8b548f2f8086 |
children | 3e44ed9d50b6 |
line wrap: on
line diff
--- a/liboctave/util/oct-string.cc Wed Nov 21 10:35:43 2018 -0800 +++ b/liboctave/util/oct-string.cc Sun Nov 18 15:44:36 2018 +0100 @@ -25,6 +25,7 @@ #include "oct-string.h" +#include <algorithm> #include <cctype> #include <cstring> @@ -224,3 +225,259 @@ INSTANTIATE_OCTAVE_STRING(Array<char>) #undef INSTANTIATE_OCTAVE_STRING + +static inline bool +is_imag_unit (int c) +{ return c == 'i' || c == 'j'; } + +static double +single_num (std::istringstream& is) +{ + double num = 0.0; + + char c = is.peek (); + + // Skip spaces. + while (isspace (c)) + { + is.get (); + c = is.peek (); + } + + if (std::toupper (c) == 'I') + { + // It's infinity. + is.get (); + char c1 = is.get (); + char c2 = is.get (); + if (std::tolower (c1) == 'n' && std::tolower (c2) == 'f') + { + num = octave::numeric_limits<double>::Inf (); + is.peek (); // May set EOF bit. + } + else + is.setstate (std::ios::failbit); // indicate that read has failed. + } + else if (c == 'N') + { + // It's NA or NaN + is.get (); + char c1 = is.get (); + if (c1 == 'A') + { + num = octave_NA; + is.peek (); // May set EOF bit. + } + else + { + char c2 = is.get (); + if (c1 == 'a' && c2 == 'N') + { + num = octave::numeric_limits<double>::NaN (); + is.peek (); // May set EOF bit. + } + else + is.setstate (std::ios::failbit); // indicate that read has failed. + } + } + else + is >> num; + + return num; +} + +static std::istringstream& +extract_num (std::istringstream& is, double& num, bool& imag, bool& have_sign) +{ + have_sign = imag = false; + + char c = is.peek (); + + // Skip leading spaces. + while (isspace (c)) + { + is.get (); + c = is.peek (); + } + + bool negative = false; + + // Accept leading sign. + if (c == '+' || c == '-') + { + have_sign = true; + negative = c == '-'; + is.get (); + c = is.peek (); + } + + // Skip spaces after sign. + while (isspace (c)) + { + is.get (); + c = is.peek (); + } + + // Imaginary number (i*num or just i), or maybe 'inf'. + if (c == 'i') + { + // possible infinity. + is.get (); + c = is.peek (); + + if (is.eof ()) + { + // just 'i' and string is finished. Return immediately. + imag = true; + num = (negative ? -1.0 : 1.0); + return is; + } + else + { + if (std::tolower (c) != 'n') + imag = true; + is.unget (); + } + } + else if (c == 'j') + imag = true; + + // It's i*num or just i + if (imag) + { + is.get (); + c = is.peek (); + // Skip spaces after imaginary unit. + while (isspace (c)) + { + is.get (); + c = is.peek (); + } + + if (c == '*') + { + // Multiplier follows, we extract it as a number. + is.get (); + num = single_num (is); + if (is.good ()) + c = is.peek (); + } + else + num = 1.0; + } + else + { + // It's num, num*i, or numi. + num = single_num (is); + if (is.good ()) + { + c = is.peek (); + + // Skip spaces after number. + while (isspace (c)) + { + is.get (); + c = is.peek (); + } + + if (c == '*') + { + is.get (); + c = is.peek (); + + // Skip spaces after operator. + while (isspace (c)) + { + is.get (); + c = is.peek (); + } + + if (is_imag_unit (c)) + { + imag = true; + is.get (); + c = is.peek (); + } + else + is.setstate (std::ios::failbit); // indicate read has failed. + } + else if (is_imag_unit (c)) + { + imag = true; + is.get (); + c = is.peek (); + } + } + } + + if (is.good ()) + { + // Skip trailing spaces. + while (isspace (c)) + { + is.get (); + c = is.peek (); + } + } + + if (negative) + num = -num; + + return is; +} + +static inline void +set_component (Complex& c, double num, bool imag) +{ +#if defined (HAVE_CXX_COMPLEX_SETTERS) + if (imag) + c.imag (num); + else + c.real (num); +#elif defined (HAVE_CXX_COMPLEX_REFERENCE_ACCESSORS) + if (imag) + c.imag () = num; + else + c.real () = num; +#else + if (imag) + c = Complex (c.real (), num); + else + c = Complex (num, c.imag ()); +#endif +} + +Complex +octave_str2double (const std::string& str_arg) +{ + Complex val (0.0, 0.0); + + std::string str = str_arg; + + // FIXME: removing all commas doesn't allow actual parsing. + // Example: "1,23.45" is wrong, but passes Octave. + str.erase (std::remove (str.begin (), str.end(), ','), str.end ()); + std::istringstream is (str); + + double num; + bool i1, i2, s1, s2; + + if (is.eof ()) + val = octave::numeric_limits<double>::NaN (); + else if (! extract_num (is, num, i1, s1)) + val = octave::numeric_limits<double>::NaN (); + else + { + set_component (val, num, i1); + + if (! is.eof ()) + { + if (! extract_num (is, num, i2, s2) || i1 == i2 || ! s2) + val = octave::numeric_limits<double>::NaN (); + else + set_component (val, num, i2); + } + } + + return val; +}