[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