Mercurial > hg > octave-nkf
changeset 18805: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 +++ b/libinterp/corefcn/oct-stream.cc @@ -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 +++ b/libinterp/corefcn/oct-stream.h @@ -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 +++ b/libinterp/octave-value/ov-base-diag.cc @@ -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 +++ b/libinterp/octave-value/ov-base-diag.h @@ -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 +++ b/libinterp/octave-value/ov-base-scalar.cc @@ -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 +++ b/libinterp/octave-value/ov-base-scalar.h @@ -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 +++ b/libinterp/octave-value/ov-base-sparse.cc @@ -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 +++ b/libinterp/octave-value/ov-base-sparse.h @@ -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 +++ b/libinterp/octave-value/ov-perm.cc @@ -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 +++ b/libinterp/octave-value/ov-perm.h @@ -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 +++ b/libinterp/octave-value/ov-range.cc @@ -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 +++ b/libinterp/octave-value/ov-range.h @@ -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 +++ b/scripts/time/datestr.m @@ -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)));