# HG changeset patch # User John W. Eaton # Date 1647538825 14400 # Node ID 1a4a3ba925c81bba0bd57c8e5b78be906e778395 # Parent 4d2da8a3dca69327150b4ecc5e6b7bb9630877e5 don't use double precision increment when computing integer range values * ov.cc (range_increment): New template. (range_numel, make_int_range): Use it to compute unsigned range increment from signed integer or double values. (make_int_range): Avoid mixing integer and double operands when computing values for integer ranges. diff -r 4d2da8a3dca6 -r 1a4a3ba925c8 libinterp/octave-value/ov.cc --- a/libinterp/octave-value/ov.cc Thu Mar 17 16:41:42 2022 +0100 +++ b/libinterp/octave-value/ov.cc Thu Mar 17 13:40:25 2022 -0400 @@ -3102,6 +3102,49 @@ return static_cast (nel_m1) + 1; } + // Convert signed range increment to unsigned. + + template ::type, + typename std::enable_if<(std::is_integral::value + && std::is_signed::value), + bool>::type = true> + UT + range_increment (ST increment) + { + return (increment < 0 + ? UT (0) - static_cast (increment) + : static_cast (increment)); + } + + // "Convert" unsigned range increment to unsigned. A no-op, but + // needed to provide a consistent interface for other template + // functions. + + template ::type, + typename std::enable_if<(std::is_integral::value + && std::is_unsigned::value), + bool>::type = true> + UT + range_increment (UT increment) + { + return increment; + } + + // Convert double range increment to unsigned. Enable by return type. + + template ::type> + typename std::enable_if<(std::is_integral::value + && std::is_unsigned::value), UT>::type + range_increment (double increment) + { + double abs_increment = std::abs (increment); + + return static_cast (abs_increment); + } + // Number of elements in an integer range base:increment:limit. Base, // increment, and limit are of the same signed type. @@ -3119,9 +3162,7 @@ || (increment < 0 && base < limit)) return 0; - UT unsigned_increment = (increment < 0 - ? UT (0) - static_cast (increment) - : static_cast (increment)); + UT unsigned_increment = range_increment (increment); return range_numel_aux (base, unsigned_increment, limit); } @@ -3169,7 +3210,9 @@ if (abs_increment > max_val) return 1; - return range_numel_aux (base, static_cast (abs_increment), limit); + UT unsigned_increment = range_increment (increment); + + return range_numel_aux (base, unsigned_increment, limit); } // Make a range from integer values. Increment may be integer or double. @@ -3191,12 +3234,28 @@ if (nel > 0) { + typedef typename std::make_unsigned::type UT; + + UT unsigned_increment = range_increment (increment); + T val = base; result.xelem (0) = val; - for (octave_idx_type i = 1; i < nel; i++) + + if (limit > base) { - val += increment; - result.xelem (i) = val; + for (octave_idx_type i = 1; i < nel; i++) + { + val += unsigned_increment; + result.xelem (i) = val; + } + } + else + { + for (octave_idx_type i = 1; i < nel; i++) + { + val -= unsigned_increment; + result.xelem (i) = val; + } } } @@ -3210,7 +3269,7 @@ // defined in this file we could do that in a reasonable way? // Regardless of that, it might be good to provide special treatment // of colon expressions in FOR loops so that we can eliminate the - // "is_for_cmd_expr / force_rage" flag from the parser and the + // "is_for_cmd_expr / force_range" flag from the parser and the // octave_value constructors for range objects. // NOTE: We define this function separately for float and double so