Mercurial > octave-nkf
changeset 20409:0cefba1a1030
Make mod() and rem() Matlab compatible for corner cases (bug #45587).
* data.cc(Frem): Update docstring. Update and add BIST tests.
* data.cc(Fmod): Update docstring. Update and add BIST tests.
* lo-mappers.h: Add #include lo-ieee.h to get octave_NaN definition.
* lo-mappers.h(xrem): Return NaN if second input is 0. Always use signbit of
first input argument to decide sign of output.
* lo-mappers.h(xmod): Always use signbit of second input argument to decide
sign of output.
author | Rik <rik@octave.org> |
---|---|
date | Wed, 22 Jul 2015 09:46:42 -0700 |
parents | 3c70050faa1e |
children | 31f89b12aaf7 |
files | libinterp/corefcn/data.cc liboctave/numeric/lo-mappers.h |
diffstat | 2 files changed, 62 insertions(+), 16 deletions(-) [+] |
line wrap: on
line diff
--- a/libinterp/corefcn/data.cc Wed Jul 22 01:01:03 2015 -0400 +++ b/libinterp/corefcn/data.cc Wed Jul 22 09:46:42 2015 -0700 @@ -590,7 +590,26 @@ @end example\n\ \n\ An error message is printed if the dimensions of the arguments do not agree,\n\ -or if either of the arguments is complex.\n\ +or if either argument is complex.\n\ +\n\ +Programming Notes: Floating point numbers within a few eps of an integer will\n\ +be rounded to an integer before computation for compatibility with\n\ +@sc{matlab}.\n\ +\n\ +By convention,\n\ +\n\ +@example\n\ +@group\n\ +rem (@var{x}, 0) = NaN if @var{x} is a floating point variable\n\ +rem (@var{x}, 0) = 0 if @var{x} is an integer variable\n\ +rem (@var{x}, @var{y}) returns a value with the signbit from @var{x}\n\ +@end group\n\ +@end example\n\ +\n\ +For the opposite conventions see the @code{mod} function. In general,\n\ +@code{rem} is best when computing the remainder after division of two\n\ +@emph{positive} numbers. For negative numbers, or when the values are\n\ +periodic, @code{mod} is a better choice.\n\ @seealso{mod}\n\ @end deftypefn") { @@ -689,20 +708,22 @@ %!assert (rem ([1, 2, 3; -1, -2, -3], 2), [1, 0, 1; -1, 0, -1]) %!assert (rem ([1, 2, 3; -1, -2, -3], 2 * ones (2, 3)),[1, 0, 1; -1, 0, -1]) +%!assert (rem ([0, 1, 2], [0, 0, 1]), [NaN, NaN, 0]); %!assert (rem (uint8 ([1, 2, 3; -1, -2, -3]), uint8 (2)), uint8 ([1, 0, 1; -1, 0, -1])) %!assert (uint8 (rem ([1, 2, 3; -1, -2, -3], 2 * ones (2, 3))),uint8 ([1, 0, 1; -1, 0, -1])) +%!assert (rem (uint8 ([0, 1, 2]), [0, 0, 1]), uint8 ([0, 0, 0])); ## Test sparse implementations %!shared xs %! xs = sparse (0:3); %!test %! y = rem (11, xs); -%! assert (nnz (y), 3); +%! assert (isnan (y(1))); %! assert (y, sparse (rem (11, 0:3))); %!test %! y = rem (0, xs); -%! assert (nnz (y), 0); -%! assert (y, sparse (zeros (1,4))); +%! assert (nnz (y), 1); +%! assert (y, sparse ([NaN 0 0 0])); %!test %! y = rem (xs, 2); %! assert (nnz (y), 2); @@ -717,8 +738,15 @@ %! assert (y, sparse (rem (11, 0:3))); %!test %! y = rem (sparse ([0 0 0 0]), xs); -%! assert (nnz (y), 0); -%! assert (y, sparse (zeros (1,4))); +%! assert (nnz (y), 1); +%! assert (y, sparse ([NaN 0 0 0])); + +## Bug #45587 +%!assert (signbit (rem (-0, 1))) +%!assert (! signbit (rem (0, 1))) + +## bug #42627 +%!assert (rem (0.94, 0.01), 0.0) %!error rem (uint (8), int8 (5)) %!error rem (uint8 ([1, 2]), uint8 ([3, 4, 5])) @@ -726,9 +754,6 @@ %!error rem (1, 2, 3) %!error rem ([1, 2], [3, 4, 5]) %!error rem (i, 1) - -# bug 42627 -%!assert (rem (0.94, 0.01), 0.0); */ DEFUN (mod, args, , @@ -745,11 +770,27 @@ @noindent\n\ and is written such that the correct modulus is returned for integer types.\n\ This function handles negative values correctly. That is,\n\ -@code{mod (-1, 3)} is 2, not -1, as @code{rem (-1, 3)} returns.\n\ -@code{mod (@var{x}, 0)} returns @var{x}.\n\ +@w{@code{mod (-1, 3)}} is 2, not -1, as @w{@code{rem (-1, 3)}} returns.\n\ \n\ An error results if the dimensions of the arguments do not agree, or if\n\ either of the arguments is complex.\n\ +\n\ +Programming Notes: Floating point numbers within a few eps of an integer will\n\ +be rounded to an integer before computation for compatibility with\n\ +@sc{matlab}.\n\ +\n\ +By convention,\n\ +\n\ +@example\n\ +@group\n\ +mod (@var{x}, 0) = @var{x}\n\ +mod (@var{x}, @var{y}) returns a value with the signbit from @var{y}\n\ +@end group\n\ +@end example\n\ +\n\ +For the opposite conventions see the @code{rem} function. In general,\n\ +@code{mod} is a better choice than @code{rem} when any of the inputs are\n\ +negative numbers or when the values are periodic.\n\ @seealso{rem}\n\ @end deftypefn") { @@ -881,8 +922,12 @@ %!assert (mod (2.1, 0.1), 0) %!assert (mod (2.1, 0.2), 0.1, eps) -# bug 42627 -%!assert (mod (0.94, 0.01), 0.0); +## Bug #45587 +%!assert (signbit (mod (-0, 0))) +%!assert (! signbit (mod (0, -0))) + +## Bug #42627 +%!assert (mod (0.94, 0.01), 0.0) */ // FIXME: Need to convert reduction functions of this file for single precision
--- a/liboctave/numeric/lo-mappers.h Wed Jul 22 01:01:03 2015 -0400 +++ b/liboctave/numeric/lo-mappers.h Wed Jul 22 09:46:42 2015 -0700 @@ -28,6 +28,7 @@ #include "oct-cmplx.h" #include "lo-math.h" +#include "lo-ieee.h" // Double Precision extern OCTAVE_API double xtrunc (double x); @@ -334,7 +335,7 @@ } } - if (x != y && y != 0 && retval != 0) + if (x != y && y != 0) retval = xcopysign (retval, y); return retval; @@ -347,7 +348,7 @@ T retval; if (y == 0) - retval = x; + retval = octave_NaN; else { T q = x / y; @@ -367,7 +368,7 @@ } } - if (x != y && y != 0 && retval != 0) + if (x != y && y != 0) retval = xcopysign (retval, x); return retval;