changeset 18650:491b0adfec95

compatibility fixes for printf integer format specifiers Attempt to handle automatic conversion from integer floating point format in a way that is more compatible with Matlab behavior, including working properly for 64-bit integer values. * ov-base-diag.cc, ov-base-diag.h, ov-base-scalar.cc, ov-base-scalar.h, ov-base-sparse.cc, ov-base-sparse.h, ov-perm.cc, ov-perm.h, ov-range.cc, ov-range.h: Provide fast_elem_extract member function. * oct-stream.cc, oct-stream.h (printf_format_list::process_conversion): Ignore modifiers for integer formats. (printf_value_cache::curr_val): Store octave_value instead of NDArray. (printf_value_cache::data): Delete. (printf_value_cache::have_data): New member variable. (printf_value_cache::get_next_value): Rename from double_value, return individual value as an octave_value object instead of a double. (is_nan_or_inf, ok_for_signed_int_conv, ok_for_unsigned_int_conv, switch_to_g_format): New static functions. (DO_DOUBLE_CONV_1, DO_DOUBLE_CONV): Delete macros. (octave_base_stream::do_numeric_printf_conv): New function. (octave_base_stream::do_printf): Move code for handling numeric formats to do_numeric_printf_conv. * datestr.m: Round value for %d format.
author John W. Eaton <jwe@octave.org>
date Tue, 15 Apr 2014 14:12:56 -0400
parents 7485f8a8e431
children 3647db1a37d7
files libinterp/corefcn/oct-stream.cc libinterp/corefcn/oct-stream.h libinterp/octave-value/ov-base-diag.cc libinterp/octave-value/ov-base-diag.h libinterp/octave-value/ov-base-scalar.cc libinterp/octave-value/ov-base-scalar.h libinterp/octave-value/ov-base-sparse.cc libinterp/octave-value/ov-base-sparse.h libinterp/octave-value/ov-perm.cc libinterp/octave-value/ov-perm.h libinterp/octave-value/ov-range.cc libinterp/octave-value/ov-range.h scripts/time/datestr.m
diffstat 13 files changed, 299 insertions(+), 124 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/corefcn/oct-stream.cc	Wed Apr 16 05:57:06 2014 -0700
+++ b/libinterp/corefcn/oct-stream.cc	Tue Apr 15 14:12:56 2014 -0400
@@ -785,11 +785,14 @@
 
   if (i < n)
     {
+      // Accept and record modifier, but don't place it in the format
+      // item text.  All integer conversions are handled as 64-bit
+      // integers.
+
       switch (s[i])
         {
         case 'h': case 'l': case 'L':
-          modifier = s[i];
-          *buf << s[i++];
+          modifier = s[i++];
           break;
 
         default:
@@ -2160,7 +2163,7 @@
 
   printf_value_cache (const octave_value_list& args, const std::string& who)
     : values (args), val_idx (0), elt_idx (0),
-      n_vals (values.length ()), n_elts (0), data (0),
+      n_vals (values.length ()), n_elts (0), have_data (false),
       curr_state (ok)
   {
     for (octave_idx_type i = 0; i < values.length (); i++)
@@ -2178,7 +2181,7 @@
   ~printf_value_cache (void) { }
 
   // Get the current value as a double and advance the internal pointer.
-  double double_value (void);
+  octave_value get_next_value (void);
 
   // Get the current value as an int and advance the internal pointer.
   int int_value (void);
@@ -2197,8 +2200,8 @@
   int elt_idx;
   int n_vals;
   int n_elts;
-  const double *data;
-  NDArray curr_val;
+  bool have_data;
+  octave_value curr_val;
   state curr_state;
 
   // Must create value cache with values!
@@ -2212,29 +2215,27 @@
   printf_value_cache& operator = (const printf_value_cache&);
 };
 
-double
-printf_value_cache::double_value (void)
+octave_value
+printf_value_cache::get_next_value (void)
 {
-  double retval = 0.0;
+  octave_value retval;
 
   if (exhausted ())
     curr_state = conversion_error;
 
   while (! exhausted ())
     {
-      if (! data)
+      if (! have_data)
         {
-          octave_value tmp_val = values (val_idx);
+          curr_val = values (val_idx);
 
           // Force string conversion here for compatibility.
 
-          curr_val = tmp_val.array_value (true);
-
           if (! error_state)
             {
               elt_idx = 0;
-              n_elts = curr_val.length ();
-              data = curr_val.data ();
+              n_elts = curr_val.numel ();
+              have_data = true;
             }
           else
             {
@@ -2245,13 +2246,13 @@
 
       if (elt_idx < n_elts)
         {
-          retval = data[elt_idx++];
+          retval = curr_val.fast_elem_extract (elt_idx++);
 
           if (elt_idx >= n_elts)
             {
               elt_idx = 0;
               val_idx++;
-              data = 0;
+              have_data = false;
             }
 
           break;
@@ -2259,7 +2260,7 @@
       else
         {
           val_idx++;
-          data = 0;
+          have_data = false;
 
           if (n_elts == 0 && exhausted ())
             curr_state = conversion_error;
@@ -2276,14 +2277,19 @@
 {
   int retval = 0;
 
-  double dval = double_value ();
+  octave_value val = get_next_value ();
 
   if (! error_state)
     {
-      if (D_NINT (dval) == dval)
-        retval = NINT (dval);
-      else
-        curr_state = conversion_error;
+      double dval = val.double_value ();
+
+      if (! error_state)
+        {
+          if (D_NINT (dval) == dval)
+            retval = NINT (dval);
+          else
+            curr_state = conversion_error;
+        }
     }
 
   return retval;
@@ -2358,37 +2364,195 @@
   return retval;
 }
 
-#define DO_DOUBLE_CONV_1(TYPE) \
-  do \
-    { \
-      if (val > std::numeric_limits<TYPE>::max () \
-          || val < std::numeric_limits<TYPE>::min ()) \
-        { \
-          std::string tfmt = fmt; \
- \
-          tfmt.replace (tfmt.rfind (elt->type), 1, ".g"); \
- \
-          if (elt->modifier == 'l') \
-            tfmt.replace (tfmt.rfind (elt->modifier), 1, ""); \
- \
-          retval += do_printf_conv (os, tfmt.c_str (), nsa, sa_1, sa_2, \
-                                    val, who); \
-        } \
-      else \
-        retval += do_printf_conv (os, fmt, nsa, sa_1, sa_2, \
-                                  static_cast<TYPE> (val), who); \
-    } \
-  while (0)
-
-#define DO_DOUBLE_CONV(TQUAL) \
-  do \
-    { \
-       if (elt->modifier == 'l') \
-         DO_DOUBLE_CONV_1 (TQUAL long); \
-       else \
-         DO_DOUBLE_CONV_1 (TQUAL int); \
-    } \
-  while (0)
+static bool
+is_nan_or_inf (const octave_value& val)
+{
+  octave_value ov_isnan = val.isnan ();
+  octave_value ov_isinf = val.isinf ();
+
+  return (ov_isnan.is_true () || ov_isinf.is_true ());
+}
+
+static bool
+ok_for_signed_int_conv (const octave_value& val)
+{
+  uint64_t limit = std::numeric_limits<int64_t>::max ();
+
+  if (val.is_integer_type ())
+    {
+      if (val.is_uint64_type ())
+        {
+          octave_uint64 ival = val.uint64_scalar_value ();
+
+          if (ival.value () <= limit)
+            return true;
+        }
+      else
+        return true;
+    }
+  else
+    {
+      double dval = val.double_value ();
+
+      if (dval == xround (dval) && dval <= limit)
+        return true;
+    }
+
+  return false;
+}
+
+static bool
+ok_for_unsigned_int_conv (const octave_value& val)
+{
+  if (val.is_integer_type ())
+    return true;
+  else
+    {
+      double dval = val.double_value ();
+
+      uint64_t limit = std::numeric_limits<uint64_t>::max ();
+
+      if (dval == xround (dval) && dval <= limit)
+        return true;
+    }
+
+  return false;
+}
+
+static std::string
+switch_to_g_format (const printf_format_elt *elt)
+{
+  std::string tfmt = elt->text;
+
+  tfmt.replace (tfmt.rfind (elt->type), 1, "g");
+
+  return tfmt;
+}
+
+int
+octave_base_stream::do_numeric_printf_conv (std::ostream& os,
+                                            const printf_format_elt *elt,
+                                            int nsa, int sa_1, int sa_2,
+                                            const octave_value& val,
+                                            const std::string& who)
+{
+  int retval = 0;
+
+  const char *fmt = elt->text;
+
+  if (is_nan_or_inf (val))
+    {
+      double dval = val.double_value ();
+
+      std::string tfmt = fmt;
+      std::string::size_type i1, i2;
+
+      tfmt.replace ((i1 = tfmt.rfind (elt->type)),
+                   1, 1, 's');
+
+      if ((i2 = tfmt.rfind ('.')) != std::string::npos
+          && i2 < i1)
+        {
+          tfmt.erase (i2, i1-i2);
+          if (elt->prec < 0)
+            nsa--;
+        }
+
+      const char *tval;
+      if (lo_ieee_isinf (dval))
+        {
+          if (elt->flags.find ('+') != std::string::npos)
+            tval = (dval < 0 ? "-Inf" : "+Inf");
+          else
+            tval = (dval < 0 ? "-Inf" : "Inf");
+        }
+      else
+        {
+          if (elt->flags.find ('+') != std::string::npos)
+            tval = (lo_ieee_is_NA (dval) ? "+NA" : "+NaN");
+          else
+            tval = (lo_ieee_is_NA (dval) ? "NA" : "NaN");
+        }
+
+      retval += do_printf_conv (os, tfmt.c_str (), nsa, sa_1, sa_2, tval, who);
+    }
+  else
+    {
+      static std::string llmod
+        = sizeof (long) == sizeof (int64_t) ? "l" : "ll";
+
+      char type = elt->type;
+
+      switch (type)
+        {
+        case 'd': case 'i': case 'c':
+          if (ok_for_signed_int_conv (val))
+            {
+              octave_int64 tval = val.int64_scalar_value ();
+
+              // Insert "long" modifier.
+              std::string tfmt = fmt;
+              tfmt.replace (tfmt.rfind (type), 1, llmod + type);
+
+              retval += do_printf_conv (os, tfmt.c_str (), nsa, sa_1, sa_2,
+                                        tval.value (), who);
+            }
+          else
+            {
+              std::string tfmt = switch_to_g_format (elt);
+
+              double dval = val.double_value ();
+
+              if (! error_state)
+                retval += do_printf_conv (os, tfmt.c_str (), nsa,
+                                          sa_1, sa_2, dval, who);
+            }
+          break;
+
+        case 'o': case 'x': case 'X': case 'u':
+          if (ok_for_unsigned_int_conv (val))
+            {
+              octave_uint64 tval = val.uint64_scalar_value ();
+
+              // Insert "long" modifier.
+              std::string tfmt = fmt;
+              tfmt.replace (tfmt.rfind (type), 1, llmod + type);
+
+              retval += do_printf_conv (os, tfmt.c_str (), nsa, sa_1, sa_2,
+                                        tval.value (), who);
+            }
+          else
+            {
+              std::string tfmt = switch_to_g_format (elt);
+
+              double dval = val.double_value ();
+
+              if (! error_state)
+                retval += do_printf_conv (os, tfmt.c_str (), nsa,
+                                          sa_1, sa_2, dval, who);
+            }
+          break;
+
+        case 'f': case 'e': case 'E':
+        case 'g': case 'G':
+          {
+            double dval = val.double_value ();
+
+            if (! error_state)
+              retval += do_printf_conv (os, fmt, nsa, sa_1, sa_2, dval, who);
+          }
+          break;
+
+        default:
+          error ("%s: invalid format specifier",
+                 who.c_str ());
+          return -1;
+          break;
+        }
+    }
+
+  return retval;
+}
 
 int
 octave_base_stream::do_printf (printf_format_list& fmt_list,
@@ -2443,8 +2607,6 @@
                     }
                 }
 
-              const char *fmt = elt->text;
-
               if (elt->type == '%')
                 {
                   os << "%";
@@ -2460,81 +2622,18 @@
                   std::string val = val_cache.string_value ();
 
                   if (val_cache)
-                    retval += do_printf_conv (os, fmt, nsa, sa_1,
+                    retval += do_printf_conv (os, elt->text, nsa, sa_1,
                                               sa_2, val.c_str (), who);
                   else
                     break;
                 }
               else
                 {
-                  double val = val_cache.double_value ();
+                  octave_value val = val_cache.get_next_value ();
 
                   if (val_cache)
-                    {
-                      if (lo_ieee_isnan (val) || xisinf (val))
-                        {
-                          std::string tfmt = fmt;
-                          std::string::size_type i1, i2;
-
-                          tfmt.replace ((i1 = tfmt.rfind (elt->type)),
-                                        1, 1, 's');
-
-                          if ((i2 = tfmt.rfind ('.')) != std::string::npos
-                              && i2 < i1)
-                            {
-                              tfmt.erase (i2, i1-i2);
-                              if (elt->prec < 0)
-                                nsa--;
-                            }
-
-                          const char *tval;
-                          if (xisinf (val))
-                            {
-                              if (elt->flags.find ('+') != std::string::npos)
-                                tval = (val < 0 ? "-Inf" : "+Inf");
-                              else
-                                tval = (val < 0 ? "-Inf" : "Inf");
-                            }
-                          else
-                            {
-                              if (elt->flags.find ('+') != std::string::npos)
-                                tval = (lo_ieee_is_NA (val) ? "+NA" : "+NaN");
-                              else
-                                tval = (lo_ieee_is_NA (val) ? "NA" : "NaN");
-                            }
-
-                          retval += do_printf_conv (os, tfmt.c_str (),
-                                                    nsa, sa_1, sa_2,
-                                                    tval, who);
-                        }
-                      else
-                        {
-                          char type = elt->type;
-
-                          switch (type)
-                            {
-                            case 'd': case 'i': case 'c':
-                              DO_DOUBLE_CONV (OCTAVE_EMPTY_CPP_ARG);
-                              break;
-
-                            case 'o': case 'x': case 'X': case 'u':
-                              DO_DOUBLE_CONV (unsigned);
-                              break;
-
-                            case 'f': case 'e': case 'E':
-                            case 'g': case 'G':
-                              retval += do_printf_conv (os, fmt, nsa,
-                                                        sa_1, sa_2, val, who);
-                              break;
-
-                            default:
-                              error ("%s: invalid format specifier",
-                                     who.c_str ());
-                              return -1;
-                              break;
-                            }
-                        }
-                    }
+                    retval += do_numeric_printf_conv (os, elt, nsa, sa_1,
+                                                      sa_2, val, who);
                   else
                     break;
                 }
--- a/libinterp/corefcn/oct-stream.h	Wed Apr 16 05:57:06 2014 -0700
+++ b/libinterp/corefcn/oct-stream.h	Tue Apr 15 14:12:56 2014 -0400
@@ -479,6 +479,11 @@
 
   int flush (void);
 
+  int do_numeric_printf_conv (std::ostream& os, const printf_format_elt *elt,
+                              int nsa, int sa_1, int sa_2,
+                              const octave_value& val,
+                              const std::string& who);
+
   int do_printf (printf_format_list& fmt_list, const octave_value_list& args,
                  const std::string& who /* = "printf" */);
 
--- a/libinterp/octave-value/ov-base-diag.cc	Wed Apr 16 05:57:06 2014 -0700
+++ b/libinterp/octave-value/ov-base-diag.cc	Tue Apr 15 14:12:56 2014 -0400
@@ -530,6 +530,23 @@
 
 template <class DMT, class MT>
 octave_value
+octave_base_diag<DMT, MT>::fast_elem_extract (octave_idx_type n) const
+{
+  if (n < matrix.numel ())
+    {
+      octave_idx_type nr = matrix.rows ();
+
+      octave_idx_type r = n % nr;
+      octave_idx_type c = n / nr;
+
+      return octave_value (matrix.elem (r, c));
+    }
+  else
+    return octave_value ();
+}
+
+template <class DMT, class MT>
+octave_value
 octave_base_diag<DMT, MT>::to_dense (void) const
 {
   if (! dense_cache.is_defined ())
--- a/libinterp/octave-value/ov-base-diag.h	Wed Apr 16 05:57:06 2014 -0700
+++ b/libinterp/octave-value/ov-base-diag.h	Tue Apr 15 14:12:56 2014 -0400
@@ -207,6 +207,8 @@
 
   void print_info (std::ostream& os, const std::string& prefix) const;
 
+  octave_value fast_elem_extract (octave_idx_type n) const;
+
 protected:
 
   DMT matrix;
--- a/libinterp/octave-value/ov-base-scalar.cc	Wed Apr 16 05:57:06 2014 -0700
+++ b/libinterp/octave-value/ov-base-scalar.cc	Tue Apr 15 14:12:56 2014 -0400
@@ -180,6 +180,13 @@
 }
 
 template <class ST>
+octave_value
+octave_base_scalar<ST>::fast_elem_extract (octave_idx_type n) const
+{
+  return (n == 0) ? octave_value (scalar) : octave_value ();
+}
+
+template <class ST>
 bool
 octave_base_scalar<ST>::fast_elem_insert_self (void *where,
                                                builtin_type_t btyp) const
--- a/libinterp/octave-value/ov-base-scalar.h	Wed Apr 16 05:57:06 2014 -0700
+++ b/libinterp/octave-value/ov-base-scalar.h	Tue Apr 15 14:12:56 2014 -0400
@@ -148,6 +148,8 @@
 
   ST& scalar_ref (void) { return scalar; }
 
+  octave_value fast_elem_extract (octave_idx_type n) const;
+
   bool fast_elem_insert_self (void *where, builtin_type_t btyp) const;
 
 protected:
--- a/libinterp/octave-value/ov-base-sparse.cc	Wed Apr 16 05:57:06 2014 -0700
+++ b/libinterp/octave-value/ov-base-sparse.cc	Tue Apr 15 14:12:56 2014 -0400
@@ -436,6 +436,20 @@
   return success;
 }
 
+
+template <class T>
+octave_value
+octave_base_sparse<T>::fast_elem_extract (octave_idx_type n) const
+{
+  octave_idx_type nr = matrix.rows ();
+  octave_idx_type nc = matrix.cols ();
+
+  octave_idx_type i = n % nr;
+  octave_idx_type j = n / nr;
+
+  return (i < nr && j < nc) ? octave_value (matrix(i,j)) : octave_value ();
+}
+
 template <class T>
 octave_value
 octave_base_sparse<T>::map (octave_base_value::unary_mapper_t umap) const
--- a/libinterp/octave-value/ov-base-sparse.h	Wed Apr 16 05:57:06 2014 -0700
+++ b/libinterp/octave-value/ov-base-sparse.h	Tue Apr 15 14:12:56 2014 -0400
@@ -165,6 +165,8 @@
 
   octave_idx_type *mex_get_jc (void) const { return matrix.mex_get_jc (); }
 
+  octave_value fast_elem_extract (octave_idx_type n) const;
+
 protected:
 
   octave_value map (octave_base_value::unary_mapper_t umap) const;
--- a/libinterp/octave-value/ov-perm.cc	Wed Apr 16 05:57:06 2014 -0700
+++ b/libinterp/octave-value/ov-perm.cc	Tue Apr 15 14:12:56 2014 -0400
@@ -448,3 +448,18 @@
   return retval;
 }
 
+octave_value
+octave_perm_matrix::fast_elem_extract (octave_idx_type n) const
+{
+  if (n < matrix.numel ())
+    {
+      octave_idx_type nr = matrix.rows ();
+
+      octave_idx_type r = n % nr;
+      octave_idx_type c = n / nr;
+
+      return octave_value (matrix.elem (r, c));
+    }
+  else
+    return octave_value ();
+}
--- a/libinterp/octave-value/ov-perm.h	Wed Apr 16 05:57:06 2014 -0700
+++ b/libinterp/octave-value/ov-perm.h	Tue Apr 15 14:12:56 2014 -0400
@@ -218,6 +218,8 @@
   octave_value map (unary_mapper_t umap) const
   { return to_dense ().map (umap); }
 
+  octave_value fast_elem_extract (octave_idx_type n) const;
+
 protected:
 
   PermMatrix matrix;
--- a/libinterp/octave-value/ov-range.cc	Wed Apr 16 05:57:06 2014 -0700
+++ b/libinterp/octave-value/ov-range.cc	Tue Apr 15 14:12:56 2014 -0400
@@ -677,6 +677,13 @@
   return retval;
 }
 
+octave_value
+octave_range::fast_elem_extract (octave_idx_type n) const
+{
+  return (n < range.nelem ())
+    ? octave_value (range.elem (n)) : octave_value ();
+}
+
 DEFUN (allow_noninteger_range_as_index, args, nargout,
        "-*- texinfo -*-\n\
 @deftypefn  {Built-in Function} {@var{val} =} allow_noninteger_range_as_index ()\n\
--- a/libinterp/octave-value/ov-range.h	Wed Apr 16 05:57:06 2014 -0700
+++ b/libinterp/octave-value/ov-range.h	Tue Apr 15 14:12:56 2014 -0400
@@ -290,6 +290,8 @@
     return m.map (umap);
   }
 
+  octave_value fast_elem_extract (octave_idx_type n) const;
+
 private:
 
   Range range;
--- a/scripts/time/datestr.m	Wed Apr 16 05:57:06 2014 -0700
+++ b/scripts/time/datestr.m	Tue Apr 15 14:12:56 2014 -0400
@@ -252,7 +252,8 @@
 
     df = regexprep (df, '[Ss][Ss]', "%S");
 
-    df = strrep (df, "FFF", sprintf ("%03d", 1000 * (v(i,6) - fix (v(i,6)))));
+    df = strrep (df, "FFF", sprintf ("%03d",
+                                     round (1000 * (v(i,6) - fix (v(i,6))))));
 
     df = strrep (df, 'QQ', sprintf ("Q%d", fix ((v(i,2) + 2) / 3)));