28

From R help function: Note that for rounding off a 5, the IEC 60559 standard is expected to be used, ‘go to the even digit’. Therefore round(0.5) is 0 and round(-1.5) is -2.

> round(0.5)
[1] 0
> round(1.5)
[1] 2
> round(2.5)
[1] 2
> round(3.5)
[1] 4
> round(4.5)
[1] 4

But I need all values ending with .5 to be rounded down. All other values should be rounded as it they are done by round() function. Example:

round(3.5) = 3
round(8.6) = 9
round(8.1) = 8
round(4.5) = 4

Is there a fast way to do it?

svick
  • 236,525
  • 50
  • 385
  • 514
Alina
  • 2,191
  • 3
  • 33
  • 68

5 Answers5

27

Per Dietrich Epp's comment, you can use the ceiling() function with an offset to get a fast, vectorized, correct solution:

round_down <- function(x) ceiling(x - 0.5)
round_down(seq(-2, 3, by = 0.5))
## [1] -2 -2 -1 -1  0  0  1  1  2  2  3

I think this is faster and much simpler than many of the other solutions shown here.

As noted by Carl Witthoft, this adds much more bias to your data than simple rounding. Compare:

mean(round_down(seq(-2, 3, by = 0.5)))
## [1] 0.2727273
mean(round(seq(-2, 3, by = 0.5)))
## [1] 0.4545455
mean(seq(-2, 3, by = 0.5))
## [1] 0.5

What is the application for such a rounding procedure?

krlmlr
  • 25,056
  • 14
  • 120
  • 217
19

Check if the remainder of x %% 1 is equal to .5 and then floor or round the numbers:

x <- seq(1, 3, 0.1)
ifelse(x %% 1 == 0.5, floor(x), round(x))
> 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3
Martin Schmelzer
  • 23,283
  • 6
  • 73
  • 98
14

I'll join the circus too:

rndflr <- function(x) {
  sel <- vapply(x - floor(x), function(y) isTRUE(all.equal(y, 0.5)), FUN.VALUE=logical(1))
  x[sel] <- floor(x[sel])
  x[!sel] <- round(x[!sel])  
  x
}

rndflr(c(3.5,8.6,8.1,4.5))
#[1] 3 9 8 4
thelatemail
  • 91,185
  • 12
  • 128
  • 188
  • 1
    +1 from me. The only answer using `all.equal()`, which IMHO is the proper way of handling such situations. – RHertel Jan 18 '16 at 08:23
10

This function works by finding elements that have decimal part equal to 0.5, and adding a small negative number to to them before rounding, ensuring that they'll be rounded downwards. (It relies -- harmlessly but in slightly obfuscated manner --- on the fact that a Boolean vector in R will be converted to a vector of 0's and 1's when multiplied by a numeric vector.)

f <- function(x) {
    round(x - .1*(x%%1 == .5))
}

x <- c(0.5,1,1.5,2,2.5,2.01,2.99)
f(x)
[1] 0 1 1 2 2 2 3
Josh O'Brien
  • 159,210
  • 26
  • 366
  • 455
8

The function (not golfed) is very simple and checks whether the decimals that are left are .5 or less. In effect you could easily make it more useful and take 0.5 as an argument:

nice.round <- function(x, myLimit = 0.5) {
  bX <- x
  intX <- as.integer(x)
  decimals <- x%%intX
  if(is.na(decimals)) {
    decimals <- 0
  }
  if(decimals <= myLimit) {
    x <- floor(x)
  } else {
    x <- round(x)
  }
  if (bX > 0.5 & bX < 1) {
    x <- 1
  }
  return(x)
}

Tests

Currently, this function does not work properly with values between 0.5 and 1.0.

> nice.round(1.5)
[1] 1
> nice.round(1.6)
[1] 2
> nice.round(10000.624541)
[1] 10001
> nice.round(0.4)
[1] 0
> nice.round(0.6)
[1] 1
Konrad
  • 17,740
  • 16
  • 106
  • 167
  • 1
    "not golfed"—is this supposed to be a *bad* thing?? – wchargin Jan 18 '16 at 14:02
  • @WChargin Absolutely not, I was only indicating that the function could easily be made shorter. – Konrad Jan 18 '16 at 14:23
  • Here is a remark from @Kreuvf who attend to edit this post to comment: "Currently, this function does not work properly with values between 0.5 and 1.0. `nice.round(0.6)` returns 0." – vard Jan 18 '16 at 14:33