diff liboctave/array/Range.cc @ 16169:0303fda3e929

Fix range behavior with -0 endpoints (bug #38423) * libinterp/interpfcn/pr-output.cc(octave_print_internal): print base or limit of range rather than using expression base+i*increment which can destroy the signbit of base/limit. * liboctave/array/Range.cc(Range constructor): Move trivial 2-line constructor to .h file. * liboctave/array/Range.cc(matrix_value, checkelem): Return base for first element of array. Return limit of range for end of array if appropriate. * liboctave/array/Range.cc(elem): Move function from Range.h Return base for first element of array. Return limit of range for end of array if appropriate. * liboctave/array/Range.cc(_rangeindex_helper, index): Return base for first element of array. Return limit of range for end of array if appropriate. * liboctave/array/Range.cc(min, max): Use '<=' or '>=' tests to return base or limit if appropriate. * liboctave/array/Range.cc(is_sorted): Place more common test first in if/else if/else tree. * liboctave/array/Range.cc(operator <<): Return base for first element of array. Return limit of range for end of array if appropriate. liboctave/array/Range.h(Range constructor): Put trivial 2-line constructor in .h file. liboctave/array/Range.h(elem): Move function which has become more complicated to Range.cc. * test/range.tst: Add %!tests for corner cases of base and limit of range.
author Rik <rik@octave.org>
date Fri, 01 Mar 2013 14:06:02 -0800
parents 648dabbb4c6b
children b4a6895a9863
line wrap: on
line diff
--- a/liboctave/array/Range.cc	Fri Mar 01 12:46:56 2013 -0800
+++ b/liboctave/array/Range.cc	Fri Mar 01 14:06:02 2013 -0800
@@ -36,14 +36,6 @@
 #include "lo-utils.h"
 #include "Array-util.h"
 
-Range::Range (double b, double i, octave_idx_type n)
-  : rng_base (b), rng_limit (b + (n-1) * i), rng_inc (i),
-    rng_nelem (n), cache ()
-{
-  if (! xfinite (b) || ! xfinite (i))
-    rng_nelem = -2;
-}
-
 bool
 Range::all_elements_are_ints (void) const
 {
@@ -64,17 +56,24 @@
       cache.resize (1, rng_nelem);
       double b = rng_base;
       double increment = rng_inc;
-      for (octave_idx_type i = 0; i < rng_nelem; i++)
-        cache(i) = b + i * increment;
+      if (rng_nelem > 0)
+        {
+          // The first element must always be *exactly* the base.
+          // E.g, -0 would otherwise become +0 in the loop (-0 + 0*increment).
+          cache(0) = b; 
+          for (octave_idx_type i = 1; i < rng_nelem; i++)
+            cache(i) = b + i * increment;
+        }
 
       // On some machines (x86 with extended precision floating point
       // arithmetic, for example) it is possible that we can overshoot
       // the limit by approximately the machine precision even though
       // we were very careful in our calculation of the number of
-      // elements.
+      // elements.  The tests need equality (>= rng_limit or <= rng_limit)
+      // to have expressions like -5:1:-0 result in a -0 endpoint.
 
-      if ((rng_inc > 0 && cache(rng_nelem-1) > rng_limit)
-          || (rng_inc < 0 && cache(rng_nelem-1) < rng_limit))
+      if ((rng_inc > 0 && cache(rng_nelem-1) >= rng_limit)
+          || (rng_inc < 0 && cache(rng_nelem-1) <= rng_limit))
         cache(rng_nelem-1) = rng_limit;
     }
 
@@ -87,16 +86,68 @@
   if (i < 0 || i >= rng_nelem)
     gripe_index_out_of_range (1, 1, i+1, rng_nelem);
 
-  return rng_base + rng_inc * i;
+  if (i == 0)
+    return rng_base;
+  else if (i < rng_nelem - 1) 
+    return rng_base + i * rng_inc;
+  else
+    {
+      double end = rng_base + i * rng_inc;
+      if ((rng_inc > 0 && end >= rng_limit)
+          || (rng_inc < 0 && end <= rng_limit))
+        return rng_limit;
+      else
+        return end;
+    }
 }
 
+double
+Range::elem (octave_idx_type i) const
+{
+#if defined (BOUNDS_CHECKING)
+  return checkelem (i);
+#else
+  if (i == 0)
+    return rng_base;
+  else if (i < rng_nelem - 1) 
+    return rng_base + i * rng_inc;
+  else
+    {
+      double end = rng_base + i * rng_inc;
+      if ((rng_inc > 0 && end >= rng_limit)
+          || (rng_inc < 0 && end <= rng_limit))
+        return rng_limit;
+      else
+        return end;
+    }
+#endif
+}
+
+// Pseudo-class used for idx_vector.loop () function call
 struct _rangeidx_helper
 {
-  double *array, base, inc;
-  _rangeidx_helper (double *a, double b, double i)
-    : array (a), base (b), inc (i) { }
+  double *array, base, inc, limit;
+  octave_idx_type nmax;
+
+  _rangeidx_helper (double *a, double b, double i, double l, octave_idx_type n)
+    : array (a), base (b), inc (i), limit (l), nmax (n-1) { }
+
   void operator () (octave_idx_type i)
-    { *array++ = base + i * inc; }
+    {
+      if (i == 0)
+        *array++ = base;
+      else if (i < nmax) 
+        *array++ = base + i * inc;
+      else
+        {
+          double end = base + i * inc;
+          if ((inc > 0 && end >= limit)
+              || (inc < 0 && end <= limit))
+            *array++ = limit;
+          else
+            *array++ = end;
+        }
+    }
 };
 
 Array<double>
@@ -119,13 +170,14 @@
       octave_idx_type il = i.length (n);
 
       // taken from Array.cc.
-
       if (n != 1 && rd.is_vector ())
         rd = dim_vector (1, il);
 
       retval.clear (rd);
 
-      i.loop (n, _rangeidx_helper (retval.fortran_vec (), rng_base, rng_inc));
+      // idx_vector loop across all values in i, executing _rangeidx_helper (i) foreach i
+      i.loop (n, _rangeidx_helper (retval.fortran_vec (),
+                                   rng_base, rng_inc, rng_limit, rng_nelem));
     }
 
   return retval;
@@ -146,8 +198,7 @@
           retval = rng_base + (rng_nelem - 1) * rng_inc;
 
           // See the note in the matrix_value method above.
-
-          if (retval < rng_limit)
+          if (retval <= rng_limit)
             retval = rng_limit;
         }
 
@@ -166,8 +217,7 @@
           retval = rng_base + (rng_nelem - 1) * rng_inc;
 
           // See the note in the matrix_value method above.
-
-          if (retval > rng_limit)
+          if (retval >= rng_limit)
             retval = rng_limit;
         }
       else
@@ -268,7 +318,7 @@
   if (dim == 1)
     {
       if (mode == ASCENDING)
-          retval.sort_internal (sidx, true);
+        retval.sort_internal (sidx, true);
       else if (mode == DESCENDING)
         retval.sort_internal (sidx, false);
     }
@@ -281,10 +331,10 @@
 sortmode
 Range::is_sorted (sortmode mode) const
 {
-  if (rng_nelem > 1 && rng_inc < 0)
+  if (rng_nelem > 1 && rng_inc > 0)
+    mode = (mode == DESCENDING) ? UNSORTED : ASCENDING;
+  else if (rng_nelem > 1 && rng_inc < 0)
     mode = (mode == ASCENDING) ? UNSORTED : DESCENDING;
-  else if (rng_nelem > 1 && rng_inc > 0)
-    mode = (mode == DESCENDING) ? UNSORTED : ASCENDING;
   else
     mode = mode ? mode : ASCENDING;
 
@@ -298,12 +348,15 @@
   double increment = a.inc ();
   octave_idx_type num_elem = a.nelem ();
 
-  for (octave_idx_type i = 0; i < num_elem-1; i++)
-    os << b + i * increment << " ";
+  if (num_elem > 1)
+    {
+      // First element must be the base *exactly* (-0).
+      os << b << " ";
+      for (octave_idx_type i = 1; i < num_elem-1; i++)
+        os << b + i * increment << " ";
+    }
 
-  // Prevent overshoot.  See comment in the matrix_value method
-  // above.
-
+  // Prevent overshoot.  See comment in the matrix_value method above.
   os << (increment > 0 ? a.max () : a.min ()) << "\n";
 
   return os;
@@ -502,7 +555,7 @@
             n_elt++;
         }
 
-      retval = (n_elt >= std::numeric_limits<octave_idx_type>::max () - 1) ? -1 : n_elt;
+      retval = (n_elt < std::numeric_limits<octave_idx_type>::max () - 1) ? n_elt : -1;
     }
 
   return retval;