diff liboctave/array/Range.h @ 29353:715344f405f0

improve handling of nan, infinite value, and empty ranges (bug #59229) All ranges with NaN as their base, limit, or increment should produce a single NaN value. Ranges with infinite base, limit, or increment values may be empty, or produce NaN, or an infinite number of elements. For compatibility with Matlab, ranges constructed normally in the interpreter with an increment of zero are emtpy. We still provide a special constructor to allow a range with zero increment to represent a row vector with a constant value. * Range.h, Range.cc (xall_elements_re_ints): Handle NaN and inf ranges. (xinit): Correctly handle NaN ranges, empty ranges, and ranges with an infinite number of values. (xis_storable): New function. (range<T>::is_storable): New function with specializations for double and float. (range<T>::array_value): If number of elements is 1, reeturn final value. (range<T>::checkelem, range<T>::elem): If number of elements is 1 and index is 0, Return final value instead of base value. (class rangeidx_helper): Likewise. Prefix data members with m_. * ov-base.h (octave_base_value::is_storable): New virtual function. * ov-magic-int.h (octave_base_magic_int::is_storable): New function. * ov-null-mat.h (octave_null_matrix::is_storable, octave_null_str::is_storable, octave_null_sq_str::is_storable): New functions. * ov-range.h (octave_range::is_storable): New function. * ov.cc (octave_value::storable_value, octave_value::make_storable_value): Error if range is not storable. * test/range.tst: New tests for double and float ranges.
author John W. Eaton <jwe@octave.org>
date Sat, 06 Feb 2021 09:27:26 -0500
parents 03c283f73b9a
children 7854d5752dd2
line wrap: on
line diff
--- a/liboctave/array/Range.h	Fri Feb 05 14:03:27 2021 -0500
+++ b/liboctave/array/Range.h	Sat Feb 06 09:27:26 2021 -0500
@@ -46,23 +46,28 @@
   {
   public:
 
-    rangeidx_helper (T *a, T b, T i, T l, octave_idx_type n)
-      : array (a), base (b), inc (i), limit (l), nmax (n-1) { }
+    rangeidx_helper (T *array, T base, T increment, T final_value,
+                     octave_idx_type numel)
+      : m_array (array), m_base (base), m_increment (increment),
+        m_final (final_value),
+        m_nmax (numel-1)
+    { }
 
     void operator () (octave_idx_type i)
     {
       if (i == 0)
-        *array++ = base;
-      else if (i < nmax)
-        *array++ = base + T (i) * inc;
+        // Required for proper NaN handling.
+        *m_array++ = m_nmax == 0 ? m_final : m_base;
+      else if (i < m_nmax)
+        *m_array++ = m_base + T (i) * m_increment;
       else
-        *array++ = limit;
+        *m_array++ = m_final;
     }
 
   private:
 
-    T *array, base, inc, limit;
-    octave_idx_type nmax;
+    T *m_array, m_base, m_increment, m_final;
+    octave_idx_type m_nmax;
 
   };
 
@@ -174,6 +179,16 @@
 
     octave_idx_type numel (void) const { return m_numel; }
 
+    // To support things like "for i = 1:Inf; ...; end" that are
+    // required for Matlab compatibility, creation of a range object
+    // like 1:Inf is allowed with m_numel set to
+    // numeric_limits<octave_idx_type>::max().  However, it is not
+    // possible to store these ranges.  The following function allows
+    // us to easily distinguish ranges with an infinite number of
+    // elements.  There are specializations for double and float.
+
+    bool is_storable (void) const { return true; }
+
     dim_vector dims (void) const { return dim_vector (1, m_numel); }
 
     octave_idx_type rows (void) const { return 1; }
@@ -207,7 +222,8 @@
         err_index_out_of_range (2, 2, i+1, m_numel, dims ());
 
       if (i == 0)
-        return m_base;
+        // Required for proper NaN handling.
+        return m_numel == 1 ? final_value () : m_base;
       else if (i < m_numel - 1)
         return m_base + T (i) * m_increment;
       else
@@ -226,7 +242,8 @@
     T elem (octave_idx_type i) const
     {
       if (i == 0)
-        return m_base;
+        // Required for proper NaN handling.
+        return m_numel == 1 ? final_value () : m_base;
       else if (i < m_numel - 1)
         return m_base + T (i) * m_increment;
       else
@@ -293,7 +310,10 @@
 
       Array<T> retval (dim_vector (1, nel));
 
-      if (nel > 0)
+      if (nel == 1)
+        // Required for proper NaN handling.
+        retval(0) = final_value ();
+      else if (nel > 1)
         {
           // The first element must always be *exactly* the base.
           // E.g, -0 would otherwise become +0 in the loop (-0 + 0*increment).
@@ -343,6 +363,9 @@
   template <> OCTAVE_API void range<double>::init (void);
   template <> OCTAVE_API void range<float>::init (void);
 
+  template <> OCTAVE_API bool range<double>::is_storable (void) const;
+  template <> OCTAVE_API bool range<float>::is_storable (void) const;
+
   template <> OCTAVE_API octave_idx_type range<double>::nnz (void) const;
   template <> OCTAVE_API octave_idx_type range<float>::nnz (void) const;
 }