[Rd] Modification-proposal for %% (modulo) when supplied with double

Emil Bode emil@bode @ending from d@n@@kn@w@nl
Tue Sep 11 17:23:23 CEST 2018


Hi all,



Could we modify the "%%" (modulo)-operator to include some tolerance for rounding-errors when supplied with doubles?

It's not much work (patch supplied on the bottom), and I don't think it would break anything, only if you were really interested in analysing rounding differences.

Any ideas about implementing this and overwriting base::`%%`, or would we want another method (as I've done for the moment)?



Background

I was writing some code where something has to happen at a certain interval, with progress indicated, something like this:

interval <- .001

progress <- .1

for(i in 1:1000*interval) {myFun(i); Sys.sleep(interval); if(i %% progress, 0))) cat(i, '\n')}

without interval and progress being known in advance. I could work around it and make i integer, or do something like

isTRUE(all.equal(i %% progress,0)) || isTRUE(all.equal(i %% progress, progress),

but I think my code is clearer as it is. And I like the idea behind all.equal: we want double to approximately identical.



So my patch (with roxygen2-markup):

#' Modulo-operator with near-equality

#'

#' The \code{\link[base:Arithmetic]{`\%\%`}} operator calculates the modulo, but sometimes has rounding errors, e.g. "\code{(9.1/.1) \%\% 1}" gives ~ 1, instead of 0.\cr

#' Comparable to what all.equal does, this operator has some tolerance for small rounding errors.\cr

#' If the answer would be equal to the divisor within a small tolerance, 0 is returned instead.

#'

#' For integer x and y, the normal \%\%-operator is used

#'

#' @usage `\%mod\%`(x, y, tolerance = sqrt(.Machine$double.eps))

#' x \%mod\% y

#' @param x,y numeric vectors, similar to those passed on to \%\%

#' @param tolerance numeric, maximum difference, see \code{\link[base]{all.equal}}. The default is ~ \code{1.5e-8}

#' @return identical to the result for \%\%, unless the answer would be really close to y, in which case 0 is returned

#' @note To specify tolerance, use the call \code{`\%mod\%`(x,y,tolerance)}

#' @note The precedence for \code{\%mod\%} is the same as that for \code{\%\%}

#'

#' @name mod

#' @rdname mod

#'

#' @export

`%mod%` <- function(x,y, tolerance = sqrt(.Machine$double.eps)) {

  stopifnot(is.numeric(x), is.numeric(y), is.numeric(tolerance),

            !is.na(tolerance), length(tolerance)==1, tolerance>=0)

  if(is.integer(x) && is.integer(y)) {

    return(x %% y)

  } else {

    ans <- x %% y

    return(ifelse(abs(ans-y)<tolerance | abs(ans)<tolerance, 0, ans))

  }

}



Best regards,

Emil Bode

	[[alternative HTML version deleted]]



More information about the R-devel mailing list