reround() has a rounding argument that
controls how numbers are assumed to have been rounded, and so do
unround(), reround_to_fraction(),
rounding_bias(), and the many functions in downstream
packages such as scrutiny that call them.
Specify rounding as any of the strings below.
For consistency testing, the default is almost always the right choice, and there is usually no need to identify the software behind a paper.
rounding = "up_or_down" counts a value set as consistent
if the reported number matches either of the two rounded results. This
covers every way of breaking a tie at 5: up, down, to even, away from
zero, and toward zero. A value set is therefore never rejected because
the wrong tie-breaking rule was assumed.
rounding = "ceiling_or_floor" is even more permissive and
covers every rounding procedure, including truncation and custom
thresholds.
Specifying a single procedure narrows the range of values the test
accepts, but does not simply make it stricter. "up" and
"down" each admit one half of the default’s ties, so they
can only reject what the default accepts. "even" leads to
the same results as the default; see the note on rounding to even below.
"trunc", "ceiling", "floor",
"anti_trunc", and the "*_from" methods with a
threshold other than 5 shift the range instead, so they may
accept value sets that the default rejects, and vice versa. A single
procedure is appropriate if there is positive evidence about how the
numbers were produced. Without such evidence, it may lead to false
positives or negatives, so in case of doubt, keep the default.
These options return two rounded numbers for each input number. In consistency tests, this means counting a value set as consistent if the reported number matches either of the two rounded reconstructed numbers.
"up_or_down" (the default): one number rounded up from
5, one down from 5; in this order."up_from_or_down_from": one number rounded up, one down
— but not from 5. Instead, specify threshold as the number
from which the input should be rounded. Like "up_or_down",
it spans exactly one rounding step, wherever you put the threshold."ceiling_or_floor": one number ceiled, one
floored.By contrast, these options only return one rounded number per input:
"even" rounds to the nearest number, breaking a tie
toward the even one, using base R’s own round(). See the
note below."up" or "down" rounds in the specified
direction, starting at 5."up_from" or "down_from" rounds in the
specified direction, starting at some number other than 5. Specify this
number via the threshold argument."ceiling" or "floor" always rounds to the
next higher or lower decimal place, respectively."trunc" and "anti_trunc" always round
towards zero or away from it.Two arguments modify these procedures:
symmetric applies the procedure to the absolute value
of a negative number and then restores the sign. For the methods that
round from 5, this amounts to deciding how ties in negative numbers are
broken. The next section explains why this matters.threshold moves the point at which
"up_from" and "down_from" switch direction. It
means the same thing in both: "up_from" goes up when the
part cut off by rounding is at least threshold tenths of a
step, and "down_from" goes down when it is at most that
many. See round_up_from().Finally, five options name a complete tie-breaking procedure,
including for negative numbers, so symmetric has nothing
left to decide:
"ties_up" sends a tie to the higher number,
"ties_down" to the lower one."ties_away" sends a tie away from zero,
"ties_zero" toward it."ties_even" sends a tie to whichever of the two is
even."ties_even" is another name for "even".
Unlike the other four, it does not correspond to any combination with
symmetric, because rounding to even treats negative numbers
the same way in either case. It is included because
roundTiesToEven is IEEE 754’s default direction.
"ties_up" is "up" with
symmetric = FALSE; "ties_away" is
"up" with symmetric = TRUE. These names exist
because the translation between the two spellings is easy to confuse,
and because “up” means away from zero in Excel, Java, and Python but
refers to the number line in roundwork. The matching functions are
round_ties_up(), round_ties_down(),
round_ties_away(), round_ties_zero(), and
round_ties_even().
Combining the two spellings with symmetric = TRUE is an
error. rounding = "ties_up", symmetric = TRUE looks like a
request to round ties away from zero, but "ties_up" already
determines the direction. Write rounding = "ties_away"
instead, or rounding = "up", symmetric = TRUE.
reround(-2.5, digits = 0, rounding = "ties_up", symmetric = TRUE)
#> Error in `reround()`:
#> ! `symmetric` must not be `TRUE` with `rounding = "ties_up"`.
#> ✖ It is TRUE.
#> ℹ A "ties_*" string names a complete tie-breaking procedure, so it already
#> determines which way ties go for negative numbers.
#> ℹ For ties away from zero, set `rounding` to "ties_away".Between them, these five names and "ceiling",
"floor", and "trunc" cover every rounding
direction IEEE 754 defines:
| IEEE 754 | roundwork |
|---|---|
| roundTiesToEven (the standard’s default) | "even", "ties_even" |
| roundTiesToAway | "ties_away" ("up" with
symmetric = TRUE) |
| roundTowardPositive | "ceiling" |
| roundTowardNegative | "floor" |
| roundTowardZero | "trunc" |
The standard has no name for "ties_up",
"ties_zero", or "anti_trunc", which are
Stata’s, Python’s ROUND_HALF_DOWN, and Excel’s
ROUNDUP() respectively.
symmetricsymmetric matters whenever your data contain negative
values: difference scores, z-scores, effect sizes, temperature
scales.
By default (symmetric = FALSE), roundwork reads “up” and
“down” on the number line, so a negative tie goes up toward zero:
With symmetric = TRUE, a negative number is rounded like
its absolute value, so a tie moves away from zero:
Most statistical software does the second thing. Excel, SPSS, SAS,
and Matlab all round -2.5 to -3; in IEEE 754
terms, this is roundTiesToAway. Stata is the exception, as the
table below shows. So if you set rounding to a single
direction on data with negative values, you almost always want
symmetric = TRUE as well.
If you would rather not have to remember which way round that is, say it directly:
symmetric only affects negative numbers. With
"up", "down", and "up_or_down",
it only affects ties among them, because the midpoint between two
candidates is the same whether or not the sign is removed first. With
"up_from" and "down_from" at a
threshold other than 5, it affects more than ties, because
the point at which these procedures switch direction is not the
midpoint:
round_up_from(-4.28, digits = 1, threshold = 9)
#> [1] -4.3
round_up_from(-4.28, digits = 1, threshold = 9, symmetric = TRUE)
#> [1] -4.2Under the default rounding = "up_or_down", both results
are accepted, so symmetric cannot change a test result.
If the part cut off by rounding is exactly 5, how do these programs round, and which roundwork settings reconstruct them?
The rounding and symmetric columns are one
way to say it; the "ties_*" equivalent is in brackets. Use
one or the other, not both: a "ties_*" string cannot be
combined with symmetric = TRUE.
| Software | Function | rounding |
symmetric |
Note |
|---|---|---|---|---|
| Excel | ROUND() |
"up" ("ties_away") |
TRUE |
Absolute value is rounded, then the sign is reapplied |
| Excel | ROUNDUP() |
"anti_trunc" |
— | Away from zero, not ceiling |
| Excel | ROUNDDOWN() |
"trunc" |
— | Toward zero |
| Google Sheets | ROUND(), ROUNDUP(),
ROUNDDOWN() |
as Excel | as Excel | Follows Excel |
| SPSS | RND() |
"up" ("ties_away") |
TRUE |
Ties away from zero; the fuzz is user-settable
(SET FUZZBITS) |
| SPSS | TRUNC() |
"trunc" |
— | |
| SAS | ROUND() |
"up" ("ties_away") |
TRUE |
“Rounds up the absolute value and restores the original sign”, with fuzzing |
| SAS | ROUNDE(), ROUNDZ() |
"even" |
— | ROUNDE() fuzzes, ROUNDZ()
does not |
| Matlab | round() |
"up" ("ties_away") |
TRUE |
“Rounds away from zero to the nearest integer with
larger magnitude”; a TieBreaker option overrides this |
| Stata | round() |
"up" ("ties_up") |
FALSE |
“x is always rounded up to the larger value \[...\] round(-4.5) is −4” |
| R (>= 4.0.0) | round() |
"even" |
— | Measures which neighbor is closer on the binary double |
| Python | round() |
"even" |
— | Own algorithm for inexact ties; can differ from R |
| Python | decimal
ROUND_HALF_UP |
"up" ("ties_away") |
TRUE |
In this vocabulary, “up” means away from zero |
| NumPy | np.round() |
"even" |
— | Own algorithm for inexact ties |
Three points in the table require explanation.
Stata is the exception. Its manual is explicit that ties go to the
larger value on the number line, so round(4.5) is 5 and
round(-4.5) is −4. That is roundwork’s default,
symmetric = FALSE. Every other program in the table rounds
negative numbers like their absolute values.
“Rounding up” means two different things. Excel’s and Google Sheets’
ROUNDUP() round away from zero:
ROUNDUP(-3.14159, 1) is -3.2. That is
roundwork’s "anti_trunc", not "ceiling", which
would give -3.1; the two coincide for positive numbers
only. Likewise, ROUNDDOWN() is "trunc", not
"floor". The same pair appears as Java’s
RoundingMode.UP and DOWN and as Python’s
decimal.ROUND_UP and ROUND_DOWN.
Several programs apply a small tolerance to counter floating-point
error, and so does roundwork. SAS’s ROUND() performs “extra
computations, called fuzzing, to try to make the result agree with
decimal arithmetic”, and SPSS exposes the same idea as a user setting.
roundwork’s rounding functions nudge a value by about
1.5e-9, so that round_up(0.145, 2) is
0.15 rather than 0.14. See the
Floating-point tolerance section of ?round_up.
rounding = "even" calls base R’s round().
R, Python, and NumPy all round exact ties to even, but each settles
inexact cases with its own algorithm, so they can disagree: R rounds
1.05 to 1.0, Python to 1.1. Most
decimal fractions have no exact binary representation, so a number that
looks like a tie usually is not one, and the result can’t be predicted
from the number as printed. See
vignette("rounding-in-depth"), section To
even.
The consistency tests account for this by treating both bounds as
inclusive under "even". These are the same bounds as under
"up_or_down", so rounding = "even" gives the
same test results as the default. Knowing that a paper’s numbers were
computed in R therefore does not make the tests stricter.
roundwork’s "up" means rounding up from 5, not rounding
up in every case. The latter is what “rounding up” means in everyday
language, and what roundwork calls "ceiling". Read
"up" and "down" as short for “up from 5” and
“down from 5”. The "ties_*" options and
round_ties_*() functions name the procedure explicitly.
They are not called round_half_up() because
janitor::round_half_up() is roundTiesToAway
(janitor::round_half_up(-2.5) is -3), as are
Java’s RoundingMode.HALF_UP and Python’s
decimal.ROUND_HALF_UP. A roundwork function of the same
name that rounded ties toward +Inf would return the
opposite result for the same input. "ties_away" is
unambiguous.
Where the IEEE 754 standard has a name for a procedure, roundwork
offers that name too: round_ties_to_away() is
round_ties_away(), round_toward_positive() is
round_ceiling(), and so on. See ?ieee-754 for
the full mapping.