1

I'm looking for an explanation of how 1 decimal place rounding works for a sequence like this in R:

seq(1.05, 2.95, by = .1)

At high school, I'd round this up, i.e. 2.05 becomes 2.1. But R rounds it to 2 for 1 decimal place rounding.

Round up from .5

The following rounding function from the above stackoverflow answer consistently achieves the high school rounding:

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
}

This code compares the R rounding and rounding from above.

data.frame(cbind(
  Number = seq(1.05, 2.95, by = .1), 
  Popular.Round = round2(seq(1.05, 2.95, by = .1), 1),
  R.Round = round(seq(1.05, 2.95, by = .1), 1)))

With R rounding, 1.05 is rounded up to 1.1 whereas 2.05 is rounded down to 2. Then again 1.95 is rounded up to 2 and 2.95 is rounded up to 3 as well.

If it is "round to even", why is it 3, i.e. odd number.

Is there a better response than "just deal with it" when asked about this behavior?

Sanjid
  • 67
  • 6
  • 2
    This is related to [R FAQ-7.31](https://cran.r-project.org/doc/FAQ/R-FAQ.html#Why-doesn_0027t-R-think-these-numbers-are-equal_003f), a floating-point problem (there are several questions about this on SO, they pop up periodically). This is not unique to R. Mitigate: add an "epsilon" (number smaller than your expected significance): `round(1e-9 + seq(1.05, 2.95, by = .1), 1)` produces expected output. – r2evans Jun 07 '19 at 03:20
  • I don't think that mitigation works @r2evans, because for any epsilon you give me I can find a number less than the x.x5 that will incorrectly be rounded up. For example, the number 1.0499999 will incorrectly be rounded up for epsilon of 0.000001 – PaulB Dec 28 '21 at 16:40

1 Answers1

3

Too long to read? Scroll below

This was an interesting study for me personally. According to documentation:

Note that for rounding off a 5, the IEC 60559 standard (see also ‘IEEE 754’) 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).

Rounding to a negative number of digits means rounding to a power of ten, so for example round(x, digits = -2) rounds to the nearest hundred.

For signif the recognized values of digits are 1...22, and non-missing values are rounded to the nearest integer in that range. Complex numbers are rounded to retain the specified number of digits in the larger of the components. Each element of the vector is rounded individually, unlike printing.

Firstly, you asked "If it is "round to even", why is it 3, i.e. odd number." To be clear, the round to even rule applies for rounding off a 5. If you run round(2.5) or round(3.5), then R returns 2 and 4, respectively.

If you go here, https://stat.ethz.ch/pipermail/r-help/2008-June/164927.html, then you see this response:

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.

Short Answer: If you always round 5s upward, then your data biases upward. But if you round by evens, then your rounded-data, at large, becomes balanced.

Let's test this using your data:

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
}

x <- data.frame(cbind(
  Number = seq(1.05, 2.95, by = .1), 
  Popular.Round = round2(seq(1.05, 2.95, by = .1), 1),
  R.Round = round(seq(1.05, 2.95, by = .1), 1)))

> mean(x$Popular.Round)
[1] 2.05
> mean(x$R.Round)
[1] 2.02

Using a bigger sample:

x <- data.frame(cbind(
  Number = seq(1.05, 6000, by = .1), 
  Popular.Round = round2(seq(1.05, 6000, by = .1), 1),
  R.Round = round(seq(1.05, 6000, by = .1), 1)))

> mean(x$Popular.Round)
[1] 3000.55
> mean(x$R.Round)
[1] 3000.537
Jim O.
  • 1,091
  • 12
  • 31