changeset 20766:a34e5739538c stable

randi.m: corrected warning policy for int and single ranges.
author Kai T. Ohlhus <k.ohlhus@gmail.com>
date Fri, 27 Nov 2015 09:46:40 +0100
parents 28eae9b7aa30
children e0e55ce86ba0
files scripts/general/randi.m
diffstat 1 files changed, 98 insertions(+), 30 deletions(-) [+]
line wrap: on
line diff
--- a/scripts/general/randi.m	Thu Nov 26 08:24:20 2015 -0800
+++ b/scripts/general/randi.m	Fri Nov 27 09:46:40 2015 +0100
@@ -46,8 +46,7 @@
 ## Implementation Note: @code{randi} relies internally on @code{rand} which
 ## uses class @qcode{"double"} to represent numbers.  This limits the maximum
 ## integer (@var{imax}) and range (@var{imax} - @var{imin}) to the value
-## returned by the @code{bitmax} function.  For IEEE floating point numbers
-## this value is @w{@math{2^{53} - 1}}.
+## returned by the @code{flintmax} function.
 ##
 ## @seealso{rand}
 ## @end deftypefn
@@ -59,20 +58,22 @@
   if (nargin < 1)
     print_usage ();
   endif
+  nargoutchk (0, 1);
 
-  if (! (isnumeric (bounds) && isreal (bounds)))
-    error ("randi: IMIN and IMAX must be real numeric bounds");
+  if (! (isnumeric (bounds) && all (bounds == fix (bounds))))
+    error ("randi: IMIN and IMAX must be integer bounds");
   endif
 
+  bounds = real (double (bounds));
   if (isscalar (bounds))
     imin = 1;
-    imax = fix (bounds);
+    imax = bounds;
     if (imax < 1)
       error ("randi: require IMAX >= 1");
     endif
   else
-    imin = fix (bounds(1));
-    imax = fix (bounds(2));
+    imin = bounds(1);
+    imax = bounds(2);
     if (imax < imin)
       error ("randi: require IMIN <= IMAX");
     endif
@@ -85,27 +86,35 @@
     rclass = "double";
   endif
 
-  if (strfind (rclass, "int"))
-    if (imax > intmax (rclass))
-      error ("randi: require IMAX < intmax (CLASS)");
-    endif
-  elseif (strcmp (rclass, "single"))
-    if (imax > bitmax (rclass))
-      error ("randi: require IMAX < bitmax (CLASS)");
-    endif
+  ## Limit set by use of class double in rand(): Any consecutive integer in the
+  ## range [-flintmax(), flintmax()] can be represented by a double.
+  if ((abs (imax) >= flintmax ()) && (abs (imin) >= flintmax ()))
+    error ("randi: IMIN and IMAX must be smaller than flintmax()");
   endif
-  ## Limit set by use of class double in rand()
-  if (imax > bitmax)
-    error ("randi: maximum integer IMAX must be smaller than bitmax ()");
-  endif
-  if ((imax - imin) > bitmax)
-    error ("randi: maximum integer range must be smaller than bitmax ()");
+  if ((imax - imin) >= (flintmax () - 1))
+    error ("randi: integer range must be smaller than flintmax()-1");
   endif
 
-
-  ri = imin + floor ( (imax-imin+1)*rand (varargin{:}) );
+  ri = imin + floor ((imax - imin + 1) * rand (varargin{:}));
 
   if (! strcmp (rclass, "double"))
+    if (strfind (rclass, "int"))
+      maxval = double (intmax (rclass));
+      minval = double (intmin (rclass));
+    elseif (strcmp (rclass, "single"))
+      maxval = double (flintmax (rclass));
+      minval = -maxval;
+    else
+      error ("randi: unknown requested output class");
+    endif
+    if (imax > maxval)
+      warning (["randi: integer IMAX exceeds requested type.  Values ", ...
+        "might be truncated to requested type."]);
+    elseif (imin < minval)
+      warning (["randi: integer IMIN exceeds requested type.  Values ", ...
+        "might be truncated to requested type."]);
+    endif
+
     ri = cast (ri, rclass);
   endif
 
@@ -121,22 +130,81 @@
 %! assert (columns (ri), 1);
 %! assert (class (ri), "double");
 %!test
+%! ri = randi (int64 (100), 1, 1000);
+%! assert (ri, fix (ri));
+%! assert (min (ri), 1);
+%! assert (max (ri), 100);
+%! assert (rows (ri), 1);
+%! assert (columns (ri), 1000);
+%! assert (class (ri), "double");
+%!test
 %! ri = randi ([-5, 10], 1000, 1, "int8");
 %! assert (ri, fix (ri));
 %! assert (min (ri), int8 (-5));
 %! assert (max (ri), int8 (10));
 %! assert (class (ri), "int8");
+%!test
+%! ri = randi ([-5; 10], 1000, 1, "single");
+%! assert (ri, fix (ri));
+%! assert (min (ri), single (-5));
+%! assert (max (ri), single (10));
+%! assert (class (ri), "single");
 %!
-%!assert (size (randi (10, 3,1,2)), [3, 1, 2])
+%!assert (size (randi (10, 3, 1, 2)), [3, 1, 2])
+
+%!shared max_int8, min_int8, max_uint8, min_uint8, max_single
+%!  max_int8 = double (intmax ("int8"));
+%!  min_int8 = double (intmin ("int8"));
+%!  max_uint8 = double (intmax ("uint8"));
+%!  min_uint8 = double (intmin ("uint8"));
+%!  max_single = double (flintmax ("single"));
+
+## Test no warning being thrown if on the limits of the range
+%!function test_no_warning (func, varargin)
+%! state = warning ("query");
+%! unwind_protect
+%!   warning ("error", "all");
+%!   func (varargin{:});
+%! unwind_protect_cleanup
+%!   warning (state);
+%! end_unwind_protect
+%!endfunction
+%!test test_no_warning (@randi, max_int8, "int8");
+%!test test_no_warning (@randi, max_uint8, "uint8");
+%!test test_no_warning (@randi, max_single, "single");
+%!test test_no_warning (@randi, [min_int8, max_int8], "int8");
+%!test test_no_warning (@randi, [min_uint8, max_uint8], "uint8");
+%!test test_no_warning (@randi, [-max_single, max_single], "single");
+
+## Test range exceedings
+%!warning <exceeds requested type>
+%! randi ([min_int8-1, max_int8], "int8");
+%!warning <exceeds requested type>
+%! randi ([min_uint8-1, max_uint8], "uint8");
+%!warning <exceeds requested type>
+%! randi ([min_int8, max_int8 + 1], "int8");
+%!warning <exceeds requested type>
+%! randi ([min_uint8, max_uint8 + 1], "uint8");
+%!warning <exceeds requested type>
+%! randi ([0, max_single + 1], "single");
+%!warning <exceeds requested type>
+%! ri = randi ([-5, 10], 1000, 1, "uint8");
+%! assert (ri, fix (ri));
+%! assert (min (ri), uint8 (-5));
+%! assert (max (ri), uint8 (10));
+%! assert (class (ri), "uint8");
+
 
 ## Test input validation
 %!error (randi ())
 %!error (randi ("test"))
-%!error (randi (10+2i))
+%!error (randi (struct ("a", 1)))
 %!error (randi (0))
+%!error (randi (1.5))
+%!error (randi ([1.5, 2.5]))
+%!error (randi ([1, 2.5]))
+%!error (randi ([1.5, 2]))
 %!error (randi ([10, 1]))
-%!error (randi (256, "uint8"))
-%!error (randi (2^25, "single"))
-%!error (randi (bitmax () + 1))
-%!error (randi ([-1, bitmax()]))
-
+%!error (randi (flintmax ()))
+%!error (randi ([-1, flintmax() - 1]))
+%!error ([r1, r2] = randi ())