# HG changeset patch # User John W. Eaton # Date 1397585576 14400 # Node ID 491b0adfec957983032846a20d993160362c57db # Parent 7485f8a8e4312a9824176db3887a05e69c93a7aa 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. diff -r 7485f8a8e431 -r 491b0adfec95 libinterp/corefcn/oct-stream.cc --- 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::max () \ - || val < std::numeric_limits::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 (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::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::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; } diff -r 7485f8a8e431 -r 491b0adfec95 libinterp/corefcn/oct-stream.h --- 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" */); diff -r 7485f8a8e431 -r 491b0adfec95 libinterp/octave-value/ov-base-diag.cc --- 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 octave_value +octave_base_diag::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 +octave_value octave_base_diag::to_dense (void) const { if (! dense_cache.is_defined ()) diff -r 7485f8a8e431 -r 491b0adfec95 libinterp/octave-value/ov-base-diag.h --- 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; diff -r 7485f8a8e431 -r 491b0adfec95 libinterp/octave-value/ov-base-scalar.cc --- 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 +octave_value +octave_base_scalar::fast_elem_extract (octave_idx_type n) const +{ + return (n == 0) ? octave_value (scalar) : octave_value (); +} + +template bool octave_base_scalar::fast_elem_insert_self (void *where, builtin_type_t btyp) const diff -r 7485f8a8e431 -r 491b0adfec95 libinterp/octave-value/ov-base-scalar.h --- 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: diff -r 7485f8a8e431 -r 491b0adfec95 libinterp/octave-value/ov-base-sparse.cc --- 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 +octave_value +octave_base_sparse::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 octave_value octave_base_sparse::map (octave_base_value::unary_mapper_t umap) const diff -r 7485f8a8e431 -r 491b0adfec95 libinterp/octave-value/ov-base-sparse.h --- 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; diff -r 7485f8a8e431 -r 491b0adfec95 libinterp/octave-value/ov-perm.cc --- 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 (); +} diff -r 7485f8a8e431 -r 491b0adfec95 libinterp/octave-value/ov-perm.h --- 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; diff -r 7485f8a8e431 -r 491b0adfec95 libinterp/octave-value/ov-range.cc --- 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\ diff -r 7485f8a8e431 -r 491b0adfec95 libinterp/octave-value/ov-range.h --- 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; diff -r 7485f8a8e431 -r 491b0adfec95 scripts/time/datestr.m --- 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)));