changeset 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 31739d91ee49
children 3e44ed9d50b6
files libinterp/corefcn/module.mk libinterp/corefcn/str2double.cc libinterp/corefcn/strfns.cc liboctave/util/oct-string.cc liboctave/util/oct-string.h
diffstat 5 files changed, 393 insertions(+), 427 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/corefcn/module.mk	Wed Nov 21 10:35:43 2018 -0800
+++ b/libinterp/corefcn/module.mk	Sun Nov 18 15:44:36 2018 +0100
@@ -232,7 +232,6 @@
   %reldir%/sparse.cc \
   %reldir%/spparms.cc \
   %reldir%/sqrtm.cc \
-  %reldir%/str2double.cc \
   %reldir%/strfind.cc \
   %reldir%/strfns.cc \
   %reldir%/sub2ind.cc \
--- a/libinterp/corefcn/str2double.cc	Wed Nov 21 10:35:43 2018 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,426 +0,0 @@
-/*
-
-Copyright (C) 2010-2018 Jaroslav Hajek
-Copyright (C) 2010 VZLU Prague
-
-This file is part of Octave.
-
-Octave 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 3 of the License, or
-(at your option) any later version.
-
-Octave 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 Octave; see the file COPYING.  If not, see
-<https://www.gnu.org/licenses/>.
-
-*/
-
-#if defined (HAVE_CONFIG_H)
-#  include "config.h"
-#endif
-
-#include <string>
-#include <cctype>
-#include <sstream>
-#include <algorithm>
-
-#include "lo-ieee.h"
-
-#include "Cell.h"
-#include "ov.h"
-#include "defun.h"
-#include "errwarn.h"
-#include "utils.h"
-
-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
-}
-
-static Complex
-str2double1 (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;
-}
-
-DEFUN (str2double, args, ,
-       doc: /* -*- texinfo -*-
-@deftypefn {} {} str2double (@var{s})
-Convert a string to a real or complex number.
-
-The string must be in one of the following formats where a and b are real
-numbers and the complex unit is @qcode{'i'} or @qcode{'j'}:
-
-@itemize
-@item a + bi
-
-@item a + b*i
-
-@item a + i*b
-
-@item bi + a
-
-@item b*i + a
-
-@item i*b + a
-@end itemize
-
-If present, a and/or b are of the form @nospell{[+-]d[,.]d[[eE][+-]d]} where
-the brackets indicate optional arguments and @qcode{'d'} indicates zero or
-more digits.  The special input values @code{Inf}, @code{NaN}, and @code{NA}
-are also accepted.
-
-@var{s} may be a character string, character matrix, or cell array.  For
-character arrays the conversion is repeated for every row, and a double or
-complex array is returned.  Empty rows in @var{s} are deleted and not
-returned in the numeric array.  For cell arrays each character string
-element is processed and a double or complex array of the same dimensions as
-@var{s} is returned.
-
-For unconvertible scalar or character string input @code{str2double} returns
-a NaN@.  Similarly, for character array input @code{str2double} returns a
-NaN for any row of @var{s} that could not be converted.  For a cell array,
-@code{str2double} returns a NaN for any element of @var{s} for which
-conversion fails.  Note that numeric elements in a mixed string/numeric
-cell array are not strings and the conversion will fail for these elements
-and return NaN.
-
-@code{str2double} can replace @code{str2num}, and it avoids the security
-risk of using @code{eval} on unknown data.
-@seealso{str2num}
-@end deftypefn */)
-{
-  if (args.length () != 1)
-    print_usage ();
-
-  octave_value retval;
-
-  if (args(0).is_string ())
-    {
-      if (args(0).rows () == 0 || args(0).columns () == 0)
-        retval = Matrix (1, 1, octave::numeric_limits<double>::NaN ());
-      else if (args(0).rows () == 1 && args(0).ndims () == 2)
-        retval = str2double1 (args(0).string_value ());
-      else
-        {
-          const string_vector sv = args(0).string_vector_value ();
-
-          retval = sv.map<Complex> (str2double1);
-        }
-    }
-  else if (args(0).iscell ())
-    {
-      const Cell cell = args(0).cell_value ();
-
-      ComplexNDArray output (cell.dims (), octave::numeric_limits<double>::NaN ());
-
-      for (octave_idx_type i = 0; i < cell.numel (); i++)
-        {
-          if (cell(i).is_string ())
-            output(i) = str2double1 (cell(i).string_value ());
-        }
-      retval = output;
-    }
-  else
-    retval = Matrix (1, 1, octave::numeric_limits<double>::NaN ());
-
-  return retval;
-}
-
-/*
-%!assert (str2double ("1"), 1)
-%!assert (str2double ("-.1e-5"), -1e-6)
-%!testif ; ! ismac ()
-%! assert (str2double (char ("1", "2 3", "4i")), [1; NaN; 4i]);
-%!xtest <47413>
-%! ## Same test code as above, but intended only for test statistics on Mac.
-%! if (! ismac ()), return; endif
-%! assert (str2double (char ("1", "2 3", "4i")), [1; NaN; 4i]);
-%!assert (str2double ("1,222.5"), 1222.5)
-%!assert (str2double ("i"), i)
-%!assert (str2double ("2j"), 2i)
-%!assert (str2double ("2 + j"), 2+j)
-%!assert (str2double ("i*2 + 3"), 3+2i)
-%!assert (str2double (".5*i + 3.5"), 3.5+0.5i)
-%!assert (str2double ("1e-3 + i*.25"), 1e-3 + 0.25i)
-%!assert (str2double (char ("2 + j","1.25e-3","-05")), [2+i; 1.25e-3; -5])
-%!assert (str2double ({"2 + j","1.25e-3","-05"}), [2+i, 1.25e-3, -5])
-%!assert (str2double (1), NaN)
-%!assert (str2double ("1 2 3 4"), NaN)
-%!assert (str2double ("Hello World"), NaN)
-%!assert (str2double ("NaN"), NaN)
-%!assert (str2double ("NA"), NA)
-%!assert (str2double ("Inf"), Inf)
-%!assert (str2double ("iNF"), Inf)
-%!assert (str2double ("-Inf"), -Inf)
-%!assert (str2double ("Inf*i"), complex (0, Inf))
-%!assert (str2double ("iNF*i"), complex (0, Inf))
-%!assert (str2double ("NaN + Inf*i"), complex (NaN, Inf))
-%!assert (str2double ("Inf - Inf*i"), complex (Inf, -Inf))
-%!assert (str2double ("-i*NaN - Inf"), complex (-Inf, -NaN))
-%!testif ; ! ismac ()
-%! assert (str2double ({"abc", "4i"}), [NaN + 0i, 4i]);
-%!xtest <47413>
-%! if (! ismac ()), return; endif
-%! assert (str2double ({"abc", "4i"}), [NaN + 0i, 4i]);
-%!testif ; ! ismac ()
-%! assert (str2double ({2, "4i"}), [NaN + 0i, 4i])
-%!xtest <47413>
-%! if (! ismac ()), return; endif
-%! assert (str2double ({2, "4i"}), [NaN + 0i, 4i])
-%!assert (str2double (zeros (3,1,2)), NaN)
-%!assert (str2double (''), NaN)
-%!assert (str2double ([]), NaN)
-%!assert (str2double (char(zeros(3,0))), NaN)
-*/
--- a/libinterp/corefcn/strfns.cc	Wed Nov 21 10:35:43 2018 -0800
+++ b/libinterp/corefcn/strfns.cc	Sun Nov 18 15:44:36 2018 +0100
@@ -739,6 +739,137 @@
 %!assert <*54373> (strncmpi ("abc", "abC", 100))
 */
 
+DEFUN (str2double, args, ,
+       doc: /* -*- texinfo -*-
+@deftypefn {} {} str2double (@var{s})
+Convert a string to a real or complex number.
+
+The string must be in one of the following formats where a and b are real
+numbers and the complex unit is @qcode{'i'} or @qcode{'j'}:
+
+@itemize
+@item a + bi
+
+@item a + b*i
+
+@item a + i*b
+
+@item bi + a
+
+@item b*i + a
+
+@item i*b + a
+@end itemize
+
+If present, a and/or b are of the form @nospell{[+-]d[,.]d[[eE][+-]d]} where
+the brackets indicate optional arguments and @qcode{'d'} indicates zero or
+more digits.  The special input values @code{Inf}, @code{NaN}, and @code{NA}
+are also accepted.
+
+@var{s} may be a character string, character matrix, or cell array.  For
+character arrays the conversion is repeated for every row, and a double or
+complex array is returned.  Empty rows in @var{s} are deleted and not
+returned in the numeric array.  For cell arrays each character string
+element is processed and a double or complex array of the same dimensions as
+@var{s} is returned.
+
+For unconvertible scalar or character string input @code{str2double} returns
+a NaN@.  Similarly, for character array input @code{str2double} returns a
+NaN for any row of @var{s} that could not be converted.  For a cell array,
+@code{str2double} returns a NaN for any element of @var{s} for which
+conversion fails.  Note that numeric elements in a mixed string/numeric
+cell array are not strings and the conversion will fail for these elements
+and return NaN.
+
+@code{str2double} can replace @code{str2num}, and it avoids the security
+risk of using @code{eval} on unknown data.
+@seealso{str2num}
+@end deftypefn */)
+{
+  if (args.length () != 1)
+    print_usage ();
+
+  octave_value retval;
+
+  if (args(0).is_string ())
+    {
+      if (args(0).rows () == 0 || args(0).columns () == 0)
+        retval = Matrix (1, 1, octave::numeric_limits<double>::NaN ());
+      else if (args(0).rows () == 1 && args(0).ndims () == 2)
+        retval = octave_str2double (args(0).string_value ());
+      else
+        {
+          const string_vector sv = args(0).string_vector_value ();
+
+          retval = sv.map<Complex> (octave_str2double);
+        }
+    }
+  else if (args(0).iscell ())
+    {
+      const Cell cell = args(0).cell_value ();
+
+      ComplexNDArray output (cell.dims (), octave::numeric_limits<double>::NaN ());
+
+      for (octave_idx_type i = 0; i < cell.numel (); i++)
+        {
+          if (cell(i).is_string ())
+            output(i) = octave_str2double (cell(i).string_value ());
+        }
+      retval = output;
+    }
+  else
+    retval = Matrix (1, 1, octave::numeric_limits<double>::NaN ());
+
+  return retval;
+}
+
+/*
+%!assert (str2double ("1"), 1)
+%!assert (str2double ("-.1e-5"), -1e-6)
+%!testif ; ! ismac ()
+%! assert (str2double (char ("1", "2 3", "4i")), [1; NaN; 4i]);
+%!xtest <47413>
+%! ## Same test code as above, but intended only for test statistics on Mac.
+%! if (! ismac ()), return; endif
+%! assert (str2double (char ("1", "2 3", "4i")), [1; NaN; 4i]);
+%!assert (str2double ("1,222.5"), 1222.5)
+%!assert (str2double ("i"), i)
+%!assert (str2double ("2j"), 2i)
+%!assert (str2double ("2 + j"), 2+j)
+%!assert (str2double ("i*2 + 3"), 3+2i)
+%!assert (str2double (".5*i + 3.5"), 3.5+0.5i)
+%!assert (str2double ("1e-3 + i*.25"), 1e-3 + 0.25i)
+%!assert (str2double (char ("2 + j","1.25e-3","-05")), [2+i; 1.25e-3; -5])
+%!assert (str2double ({"2 + j","1.25e-3","-05"}), [2+i, 1.25e-3, -5])
+%!assert (str2double (1), NaN)
+%!assert (str2double ("1 2 3 4"), NaN)
+%!assert (str2double ("Hello World"), NaN)
+%!assert (str2double ("NaN"), NaN)
+%!assert (str2double ("NA"), NA)
+%!assert (str2double ("Inf"), Inf)
+%!assert (str2double ("iNF"), Inf)
+%!assert (str2double ("-Inf"), -Inf)
+%!assert (str2double ("Inf*i"), complex (0, Inf))
+%!assert (str2double ("iNF*i"), complex (0, Inf))
+%!assert (str2double ("NaN + Inf*i"), complex (NaN, Inf))
+%!assert (str2double ("Inf - Inf*i"), complex (Inf, -Inf))
+%!assert (str2double ("-i*NaN - Inf"), complex (-Inf, -NaN))
+%!testif ; ! ismac ()
+%! assert (str2double ({"abc", "4i"}), [NaN + 0i, 4i]);
+%!xtest <47413>
+%! if (! ismac ()), return; endif
+%! assert (str2double ({"abc", "4i"}), [NaN + 0i, 4i]);
+%!testif ; ! ismac ()
+%! assert (str2double ({2, "4i"}), [NaN + 0i, 4i])
+%!xtest <47413>
+%! if (! ismac ()), return; endif
+%! assert (str2double ({2, "4i"}), [NaN + 0i, 4i])
+%!assert (str2double (zeros (3,1,2)), NaN)
+%!assert (str2double (''), NaN)
+%!assert (str2double ([]), NaN)
+%!assert (str2double (char(zeros(3,0))), NaN)
+*/
+
 DEFUN (__native2unicode__, args, ,
        doc: /* -*- texinfo -*-
 @deftypefn {} {@var{utf8_str} =} __native2unicode__ (@var{native_bytes}, @var{codepage})
--- 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;
+}
--- a/liboctave/util/oct-string.h	Wed Nov 21 10:35:43 2018 -0800
+++ b/liboctave/util/oct-string.h	Sun Nov 18 15:44:36 2018 +0100
@@ -24,6 +24,8 @@
 
 #include "octave-config.h"
 
+#include "oct-cmplx.h"
+
 namespace octave
 {
   //! Octave string utility functions.
@@ -123,4 +125,7 @@
   }
 }
 
+extern OCTAVE_API Complex
+octave_str2double (const std::string& str_arg);
+
 #endif