Round up from .5

RRoundingR Faq

R Problem Overview


Yes I know why we always round to the nearest even number if we are in the exact middle (i.e. 2.5 becomes 2) of two numbers. But when I want to evaluate data for some people they don't want this behaviour. What is the simplest method to get this:

x <- seq(0.5,9.5,by=1)
round(x)

to be 1,2,3,...,10 and not 0,2,2,4,4,...,10.

Edit: To clearify: 1.4999 should be 1 after rounding. (I thought this would be obvious)

R Solutions


Solution 1 - R

This is not my own function, and unfortunately, I can't find where I got it at the moment (originally found as an anonymous comment at the Statistically Significant blog), but it should help with what you need.

round2 = function(x, n) {
  posneg = sign(x)
  z = abs(x)*10^n
  z = z + 0.5 + sqrt(.Machine$double.eps)
  z = trunc(z)
  z = z/10^n
  z*posneg
}

x is the object you want to round, and n is the number of digits you are rounding to.

An Example

x = c(1.85, 1.54, 1.65, 1.85, 1.84)
round(x, 1)
# [1] 1.8 1.5 1.6 1.8 1.8
round2(x, 1)
# [1] 1.9 1.5 1.7 1.9 1.8

(Thanks @Gregor for the addition of + sqrt(.Machine$double.eps).)

Solution 2 - R

If you want something that behaves exactly like round except for those xxx.5 values, try this:

x <- seq(0, 1, 0.1)
x
# [1] 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0
floor(0.5 + x)
# [1] 0 0 0 0 0 1 1 1 1 1 1

Solution 3 - R

As @CarlWitthoft said in the comments, this is the IEC 60559 standard as mentioned in ?round:

> 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. However, this is dependent on OS services and on representation error (since e.g. 0.15 is not represented exactly, the rounding rule applies to the represented number and not to the printed number, and so round(0.15, 1) could be either 0.1 or 0.2).

An additional explanation by Greg Snow:

> The logic behind the round to even rule is that we are trying to > represent an underlying continuous value and if x comes from a truly > continuous distribution, then the probability that x==2.5 is 0 and the > 2.5 was probably already rounded once from any values between 2.45 and 2.54999999999999..., if we use the round up on 0.5 rule that we learned in grade school, then the double rounding means that values > between 2.45 and 2.50 will all round to 3 (having been rounded first > to 2.5). This will tend to bias estimates upwards. To remove the > bias we need to either go back to before the rounding to 2.5 (which is > often impossible to impractical), or just round up half the time and > round down half the time (or better would be to round proportional to > how likely we are to see values below or above 2.5 rounded to 2.5, but > that will be close to 50/50 for most underlying distributions). The > stochastic approach would be to have the round function randomly > choose which way to round, but deterministic types are not > comforatable with that, so "round to even" was chosen (round to odd > should work about the same) as a consistent rule that rounds up and > down about 50/50. > > If you are dealing with data where 2.5 is likely to represent an exact > value (money for example), then you may do better by multiplying all > values by 10 or 100 and working in integers, then converting back only > for the final printing. Note that 2.50000001 rounds to 3, so if you > keep more digits of accuracy until the final printing, then rounding > will go in the expected direction, or you can add 0.000000001 (or > other small number) to your values just before rounding, but that can > bias your estimates upwards.

Solution 4 - R

This appears to work:

rnd <- function(x) trunc(x+sign(x)*0.5)

Ananda Mahto's response seems to do this and more - I am not sure what the extra code in his response is accounting for; or, in other words, I can't figure out how to break the rnd() function defined above.

Example:

seq(-2, 2, by=0.5)
#  [1] -2.0 -1.5 -1.0 -0.5  0.0  0.5  1.0  1.5  2.0
round(x)
#  [1] -2 -2 -1  0  0  0  1  2  2
rnd(x)
#  [1] -2 -2 -1 -1  0  1  1  2  2

Solution 5 - R

Depending on how comfortable you are with jiggling your data, this works:

round(x+10*.Machine$double.eps)
# [1]  1  2  3  4  5  6  7  8  9 10

Solution 6 - R

This method:

round2 = function(x, n) {
  posneg = sign(x)
  z = abs(x)*10^n
  z = z + 0.5
  z = trunc(z)
  z = z/10^n
  z*posneg
}

does not seem to work well when we have numbers with many digits. E.g. doing round2(2436.845, 2) will give us 2436.84. The issue seems to occur with the trunc(z) function.

Overall, I think it has something to do with the way R stores numbers and thus the trunc and float function doesn't always work. I was able to get around it in not the most elegant way:

round2 = function(x, n) {
  posneg = sign(x)
  z = abs(x)*10^n
  z = z + 0.5
  z = trunc(as.numeric(as.character(z)))
  z = z/10^n
  (z)*posneg
}

Solution 7 - R

This mimics the rounding away from zero at .5:

round_2 <- function(x, digits = 0) {
  x = x + abs(x) * sign(x) * .Machine$double.eps
  round(x, digits = digits)
}

round_2(.5 + -2:4)
-2 -1  1  2  3  4  5

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionjakob-rView Question on Stackoverflow
Solution 1 - RA5C1D2H2I1M1N2O1R2T1View Answer on Stackoverflow
Solution 2 - RflodelView Answer on Stackoverflow
Solution 3 - RJaapView Answer on Stackoverflow
Solution 4 - Ruser1854990View Answer on Stackoverflow
Solution 5 - RMichaelChiricoView Answer on Stackoverflow
Solution 6 - RBryan ChiaView Answer on Stackoverflow
Solution 7 - RmpschrammView Answer on Stackoverflow