Have you ever noticed that both `base::round(2.5)`

and `base::round(1.5)`

return 2 in R? It’s strange, isn’t it? At least I learned only one rounding rule in school that is *“四舍五入”* in Chinese. It means we should round up if the decimal is five and down if four.

By reading the documentation of `base::round()`

we know that there’s a standard called *“IEEE 754”* and the rounding rule that `base::round()`

uses is called *“go to the even digit”*.

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’.

Googling *“IEEE 754”* leads us to the Wikipedia page where lays five round rules. *“To nearest, ties away from zero”* is exactly the one that we’re familiar with. Due to unknow reasons, `base::round()`

opts the first rule *“to nearest, ties to even”*, unfortunately.

Mode / Example Value | 11.5 | 12.5 | −11.5 | −12.5 |
---|---|---|---|---|

to nearest, ties to even | 12 | 12 | −12.0 | −12.0 |

to nearest, ties away from zero | 12 | 13 | −12.0 | −13.0 |

toward 0 | 11 | 12 | −11.0 | −12.0 |

toward +∞ | 12 | 13 | −11.0 | −12.0 |

toward −∞ | 11 | 12 | −12.0 | −13.0 |

Anyway, the only important question left is *how to implement the human_round() in R?* Despite the users won’t notice the “strange” rounding rule most of the time, I can imagine it could be very difficult to explain when asked. So we’d better have a solution. I’ll share it here.

## The R version (recommend)

The rational here is that computers can only represent a fractional number in finite precision, meaning it’s safe to say two numbers are equal if the difference is smaller than a certain const. Usually, we choose `.Machine$double.eps^0.5`

as that const (Why this number? I steal it from `dplyr::near()`

:P).

```
human_round_r <- function(x, digits = 0) {
eps <- .Machine$double.eps^0.5
flag_pos <- !is.na(x) & (x > 0)
x[flag_pos] <- x[flag_pos] + eps
flag_neg <- !is.na(x) & (x < 0)
x[flag_neg] <- x[flag_neg] - eps
round(x, digits = digits)
}
human_round_r(c(2.5, 1.5, -1.5, -2.5, 1.5 - .Machine$double.eps^0.5))
```

`## [1] 3 2 -2 -3 2`

Yes, it may not work as expected for the corner case like `1.5 - .Machine$double.eps^0.5`

but the chance to get a number just equals to that in the real world closes to zero. Moreover, I can argue that `1.5 - .Machine$double.eps^0.5`

and `1.5`

is basically the same because of the precision mentioned above. However, if you insist a “perfect” one, please use the Rcpp version below.

## The Rcpp version

It takes advantage that the `std::round()`

in C++ uses the rounding rule we familiar with.

```
Rcpp::cppFunction("
NumericVector human_round_cpp(const NumericVector x, const int digits = 0)
{
const double multiplier = std::pow(10, digits);
NumericVector y(x.size());
std::transform(x.begin(), x.end(), y.begin(),
[multiplier](const double x) {
if (!R_FINITE(x)) return x;
return std::round(x * multiplier) / multiplier;
});
return y;
}")
human_round_cpp(c(2.5, 1.5, -1.5, -2.5, 1.5 - .Machine$double.eps^0.5))
```

`## [1] 3 2 -2 -3 1`