Exact Decimal Rounding via Rationals

Intro

As shown in the “basic” vignette Rounding of package round, rounding to decimal digits of double precision numbers is not trivial, mostly because most rational numbers and even most rational numbers with a finite (small) number of decimal digits are not exactly representable as regular (double precision) numbers in R:

In communication with Steven Dirkse (@ GAMS), started on the R-devel mailing lists, then continued in private, we came to concluding his own proposal for correct rounding as the following, using the definitions (as in Rounding),

Rounding to the nearest integer, $\mathrm{round}(x) := r_0(x) := \mathrm{nearbyint}(x)$

and its generalization, rounding to $$d$$ digits, $\mathrm{round}(x, d) := r_0(x \cdot 10^d) / 10^d$ for all integer $$d \in \mathbb{Z}$$ (i.e., including negative digits $$d$$).

1. all double precision numbers are rational mathematically, i.e., $$\in \mathbb{Q}$$.
2. round(x, n) as a mathematical function is well defined unambigously on the rationals, i.e., for all $$x \in \mathbb{Q}$$ with the definitions above.

These two define exact rounding and he would want R to use that.

In the following, I will use that, via exact arithmetic with rational numbers via CRAN pkg gmp (which is based on the free C library GNU MP aka GMP.

d1 <- c(.1, .2, .3, .4, .5, .6, .7, .8, .9)
fivers <- list(
d1 =    .5
, d2 = c( .05, .15,  .25,  .35,  .45,  .55,  .65,  .75,  .85,  .95)
, d3 = c(.005, .015, .025, .035, .045, .055, .065, .075, .085, .095,
.105, .115, .125, .135, .145, .155, .165, .175, .185, .195,
.205, .215, .225, .235, .245, .255, .265, .275, .285, .295,
.305, .315, .325, .335, .345, .355, .365, .375, .385, .395,
.405, .415, .425, .435, .445, .455, .465, .475, .485, .495,
.505, .515, .525, .535, .545, .555, .565, .575, .585, .595,
.605, .615, .625, .635, .645, .655, .665, .675, .685, .695,
.705, .715, .725, .735, .745, .755, .765, .775, .785, .795,
.805, .815, .825, .835, .845, .855, .865, .875, .885, .895,
.905, .915, .925, .935, .945, .955, .965, .975, .985, .995)
)

Now, “obviously” (from what I wrote before), most of the decimal fractions above are not exactly representable as double precision numbers, but of course are representable as fractions, concretely as ratios of (arbitrarily long aka “big num”) integers:

require(gmp)
## Loading required package: gmp
##
## Attaching package: 'gmp'
## The following objects are masked from 'package:base':
##
##     %*%, apply, crossprod, matrix, tcrossprod
q5er <- lapply(fivers, as.bigq)
q5er
## $d1 ## Big Rational ('bigq') : ## [1] 1/2 ## ##$d2
## Big Rational ('bigq') object of length 10:
##  [1] 3602879701896397/72057594037927936 5404319552844595/36028797018963968
##  [3] 1/4                                3152519739159347/9007199254740992
##  [5] 8106479329266893/18014398509481984 2476979795053773/4503599627370496
##  [7] 5854679515581645/9007199254740992  3/4
##  [9] 7656119366529843/9007199254740992  4278419646001971/4503599627370496
##
## $d3 ## Big Rational ('bigq') object of length 100: ## [1] 5764607523034235/1152921504606846976 1080863910568919/72057594037927936 ## [3] 3602879701896397/144115188075855872 1261007895663739/36028797018963968 ## [5] 3242591731706757/72057594037927936 7926335344172073/144115188075855872 ## [7] 1170935903116329/18014398509481984 5404319552844595/72057594037927936 ## [9] 6124895493223875/72057594037927936 3422735716801577/36028797018963968 ## [11] 7566047373982433/72057594037927936 8286623314361713/72057594037927936 ## [13] 1/8 607985949695017/4503599627370496 ## [15] 5224175567749775/36028797018963968 5584463537939415/36028797018963968 ## [17] 5944751508129055/36028797018963968 3152519739159347/18014398509481984 ## [19] 3332663724254167/18014398509481984 3512807709348987/18014398509481984 ## [21] 7385903388887613/36028797018963968 7746191359077253/36028797018963968 ## [23] 8106479329266893/36028797018963968 2116691824864133/9007199254740992 ## [25] 2206763817411543/9007199254740992 2296835809958953/9007199254740992 ## [27] 2386907802506363/9007199254740992 2476979795053773/9007199254740992 ## [29] 5134103575202365/18014398509481984 5314247560297185/18014398509481984 ## [31] 5494391545392005/18014398509481984 5674535530486825/18014398509481984 ## [33] 5854679515581645/18014398509481984 6034823500676465/18014398509481984 ## [35] 1553741871442821/4503599627370496 799388933858263/2251799813685248 ## [37] 1643813863990231/4503599627370496 3/8 ## [39] 1733885856537641/4503599627370496 889460926405673/2251799813685248 ## [41] 1823957849085051/4503599627370496 7475975381435023/18014398509481984 ## [43] 7656119366529843/18014398509481984 7836263351624663/18014398509481984 ## [45] 8016407336719483/18014398509481984 8196551321814303/18014398509481984 ## [47] 8376695306909123/18014398509481984 4278419646001971/9007199254740992 ## [49] 4368491638549381/9007199254740992 4458563631096791/9007199254740992 ## [51] 4548635623644201/9007199254740992 4638707616191611/9007199254740992 ## [53] 4728779608739021/9007199254740992 4818851601286431/9007199254740992 ## [55] 4908923593833841/9007199254740992 4998995586381251/9007199254740992 ## [57] 1272266894732165/2251799813685248 2589569785738035/4503599627370496 ## [59] 658651445502935/1125899906842624 2679641778285445/4503599627370496 ## [61] 1362338887279575/2251799813685248 2769713770832855/4503599627370496 ## [63] 5/8 2859785763380265/4503599627370496 ## [65] 1452410879826985/2251799813685248 2949857755927675/4503599627370496 ## [67] 748723438050345/1125899906842624 3039929748475085/4503599627370496 ## [69] 1542482872374395/2251799813685248 6260003482044989/9007199254740992 ## [71] 6350075474592399/9007199254740992 6440147467139809/9007199254740992 ## [73] 6530219459687219/9007199254740992 6620291452234629/9007199254740992 ## [75] 6710363444782039/9007199254740992 6800435437329449/9007199254740992 ## [77] 6890507429876859/9007199254740992 6980579422424269/9007199254740992 ## [79] 7070651414971679/9007199254740992 7160723407519089/9007199254740992 ## [81] 7250795400066499/9007199254740992 1835216848153477/2251799813685248 ## [83] 3715469692580659/4503599627370496 940126422213591/1125899906842624 ## [85] 3805541685128069/4503599627370496 1925288840700887/2251799813685248 ## [87] 3895613677675479/4503599627370496 7/8 ## [89] 3985685670222889/4503599627370496 2015360833248297/2251799813685248 ## [91] 4075757662770299/4503599627370496 1030198414761001/1125899906842624 ## [93] 4165829655317709/4503599627370496 2105432825795707/2251799813685248 ## [95] 8511803295730237/9007199254740992 8601875288277647/9007199254740992 ## [97] 8691947280825057/9007199254740992 8782019273372467/9007199254740992 ## [99] 8872091265919877/9007199254740992 8962163258467287/9007199254740992 as.bigz(2)^60 == max(denominator(q5er$d3))
## [1] TRUE

Now the “exact rounding” has been available in CRAN package gmp since Feb.2020, but in its first versions, we usee “round up”, instead of nearbyint() which would “round to even” as desired, as it is the IEEE default also adhered to by itself. Only from gmp version 0.6-1 on (

if(print(packageVersion("gmp")) < "0.6-1") {
## From 'gmp's namespace, usually "hidden", needed here :
is.whole.bigq  <- gmp:::is.whole.bigq
biginteger_mod <- gmp:::biginteger_mod
.mod.bigz <- function(e1, e2) .Call(biginteger_mod, e1, e2)

##' rounding to integer a la "nearbyint()" -- i.e. "round to even"
round0 <- function(x) {
nU <- as.bigz.bigq(xU <- x + as.bigq(1, 2)) # traditional round: .5 rounded up
if(any(I <- is.whole.bigq(xU))) { # I <==>  x == <n>.5 : "hard case"
I[I] <- .mod.bigz(nU[I], 2L) == 1L # rounded up is odd  ==> round *down*
nU[I] <- nU[I] - 1L
}
nU
}

roundQ <- function(x, digits = 0, r0 = round0) {
## round(x * 10^d) / 10^d
p10 <- as.bigz(10) ^ digits
r0(x * p10) / p10
}

##' round() method ==>  arguments = (x, digits)
round.bigq <- function(x, digits = 0) roundQ(x, digits)
.S3method("round","bigq", round.bigq)

} else ## all the above is part of gmp >= 0.6-1 :
withAutoprint({
round0
roundQ
gmp:::round.bigq
})
## [1] '0.7.3'
## > round0
## function (x)
## {
##     nU <- as.bigz.bigq(xU <- x + as.bigq(1, 2))
##     if (any(I <- is.whole.bigq(xU))) {
##         I[I] <- .mod.bigz(nU[I], 2L) == 1L
##         nU[I] <- nU[I] - 1L
##     }
##     nU
## }
## <bytecode: 0xee7b5c0>
## <environment: namespace:gmp>
## > roundQ
## function (x, digits = 0, r0 = round0)
## {
##     p10 <- as.bigz(10)^digits
##     r0(x * p10)/p10
## }
## <bytecode: 0xee88b78>
## <environment: namespace:gmp>
## > gmp:::round.bigq
## function (x, digits = 0)
## roundQ(x, digits)
## <bytecode: 0xee8faa0>
## <environment: namespace:gmp>
## "simple round" was used in round.bigq() for several months in 2020:
round0s <- function(x) as.bigz.bigq(x + as.bigq(1, 2))

Gives here

round(q5er$d2, 1) ## Big Rational ('bigq') object of length 10: ## [1] 1/10 1/10 1/5 3/10 1/2 3/5 7/10 4/5 4/5 9/10 round(q5er$d3, 2)
## Big Rational ('bigq') object of length 100:
##   [1] 1/100  1/100  3/100  1/25   1/25   3/50   7/100  7/100  9/100  1/10
##  [11] 1/10   3/25   3/25   7/50   7/50   3/20   17/100 17/100 9/50   1/5
##  [21] 1/5    21/100 23/100 23/100 6/25   13/50  27/100 7/25   7/25   29/100
##  [31] 3/10   8/25   33/100 17/50  17/50  7/20   9/25   19/50  39/100 2/5
##  [41] 41/100 41/100 21/50  43/100 9/20   23/50  47/100 47/100 12/25  49/100
##  [51] 51/100 13/25  53/100 27/50  11/20  14/25  14/25  57/100 29/50  59/100
##  [61] 3/5    61/100 31/50  16/25  13/20  33/50  67/100 17/25  69/100 69/100
##  [71] 7/10   71/100 18/25  73/100 37/50  19/25  77/100 39/50  79/100 4/5
##  [81] 81/100 81/100 41/50  83/100 21/25  17/20  43/50  22/25  89/100 9/10
##  [91] 91/100 23/25  93/100 47/50  47/50  19/20  24/25  97/100 49/50  99/100

Now comparing these with all the round()ing methods from our package round:

require(round)

roundAllX <- function(x, digits, versions = roundVersions) {
xQ <- as.bigq(x)
M <- cbind(roundAll(x, digits, versions=versions)
, SxctQ = asNumeric(roundQ(xQ, digits, r0=round0s))
,  xctQ = asNumeric(roundQ(xQ, digits))
)
rownames(M) <- format(x)
M
}

(rA1 <- roundAllX(fivers$d2, 1)) ## sprintf r0.C r1.C r1a.C r2.C r2a.C r3.C r3d.C r3 SxctQ xctQ ## 0.05 0.1 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.1 0.1 ## 0.15 0.1 0.2 0.2 0.2 0.2 0.2 0.1 0.1 0.1 0.1 0.1 ## 0.25 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.3 0.2 ## 0.35 0.3 0.4 0.4 0.4 0.4 0.4 0.3 0.3 0.3 0.3 0.3 ## 0.45 0.5 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.5 0.5 ## 0.55 0.6 0.6 0.6 0.6 0.6 0.6 0.6 0.6 0.6 0.6 0.6 ## 0.65 0.7 0.6 0.6 0.6 0.6 0.6 0.7 0.7 0.7 0.7 0.7 ## 0.75 0.8 0.8 0.8 0.8 0.8 0.8 0.8 0.8 0.8 0.8 0.8 ## 0.85 0.8 0.8 0.8 0.8 0.8 0.8 0.8 0.8 0.8 0.8 0.8 ## 0.95 0.9 1.0 1.0 1.0 1.0 1.0 0.9 0.9 0.9 0.9 0.9 with some differences already, and more with digits=2 rounding: (rA2 <- roundAllX(fivers$d3, 2))
##       sprintf r0.C r1.C r1a.C r2.C r2a.C r3.C r3d.C   r3 SxctQ xctQ
## 0.005    0.01 0.00 0.00  0.00 0.00  0.00 0.00  0.00 0.00  0.01 0.01
## 0.015    0.01 0.02 0.02  0.02 0.02  0.02 0.01  0.01 0.01  0.01 0.01
## 0.025    0.03 0.02 0.02  0.02 0.02  0.02 0.03  0.03 0.03  0.03 0.03
## 0.035    0.04 0.04 0.04  0.04 0.04  0.04 0.04  0.04 0.04  0.04 0.04
## 0.045    0.04 0.04 0.04  0.04 0.04  0.04 0.04  0.04 0.04  0.04 0.04
## 0.055    0.06 0.06 0.06  0.06 0.06  0.06 0.06  0.06 0.06  0.06 0.06
## 0.065    0.07 0.06 0.06  0.06 0.06  0.06 0.06  0.06 0.06  0.07 0.07
## 0.075    0.07 0.08 0.08  0.08 0.08  0.08 0.07  0.07 0.07  0.07 0.07
## 0.085    0.09 0.08 0.08  0.08 0.08  0.08 0.09  0.09 0.09  0.09 0.09
## 0.095    0.10 0.10 0.10  0.10 0.10  0.10 0.10  0.10 0.10  0.10 0.10
## 0.105    0.10 0.10 0.10  0.10 0.10  0.10 0.10  0.10 0.10  0.10 0.10
## 0.115    0.12 0.12 0.12  0.12 0.12  0.12 0.12  0.12 0.12  0.12 0.12
## 0.125    0.12 0.12 0.12  0.12 0.12  0.12 0.12  0.12 0.12  0.13 0.12
## 0.135    0.14 0.14 0.14  0.14 0.14  0.14 0.14  0.14 0.14  0.14 0.14
## 0.145    0.14 0.14 0.14  0.14 0.14  0.14 0.14  0.14 0.14  0.14 0.14
## 0.155    0.15 0.16 0.16  0.16 0.16  0.16 0.16  0.16 0.16  0.15 0.15
## 0.165    0.17 0.16 0.16  0.16 0.16  0.16 0.16  0.16 0.16  0.17 0.17
## 0.175    0.17 0.18 0.18  0.18 0.18  0.18 0.17  0.17 0.17  0.17 0.17
## 0.185    0.18 0.18 0.18  0.18 0.18  0.18 0.18  0.18 0.18  0.18 0.18
## 0.195    0.20 0.20 0.20  0.20 0.20  0.20 0.20  0.20 0.20  0.20 0.20
## 0.205    0.20 0.20 0.20  0.20 0.20  0.20 0.20  0.20 0.20  0.20 0.20
## 0.215    0.21 0.22 0.22  0.22 0.22  0.22 0.22  0.22 0.22  0.21 0.21
## 0.225    0.23 0.22 0.22  0.22 0.22  0.22 0.22  0.22 0.22  0.23 0.23
## 0.235    0.23 0.24 0.24  0.24 0.24  0.24 0.23  0.23 0.23  0.23 0.23
## 0.245    0.24 0.24 0.24  0.24 0.24  0.24 0.24  0.24 0.24  0.24 0.24
## 0.255    0.26 0.26 0.26  0.26 0.26  0.26 0.26  0.26 0.26  0.26 0.26
## 0.265    0.27 0.26 0.26  0.26 0.26  0.26 0.26  0.26 0.26  0.27 0.27
## 0.275    0.28 0.28 0.28  0.28 0.28  0.28 0.28  0.28 0.28  0.28 0.28
## 0.285    0.28 0.28 0.28  0.28 0.28  0.28 0.28  0.28 0.28  0.28 0.28
## 0.295    0.29 0.30 0.30  0.30 0.30  0.30 0.30  0.30 0.30  0.29 0.29
## 0.305    0.30 0.30 0.30  0.30 0.30  0.30 0.30  0.30 0.30  0.30 0.30
## 0.315    0.32 0.32 0.32  0.32 0.32  0.32 0.32  0.32 0.32  0.32 0.32
## 0.325    0.33 0.32 0.32  0.32 0.32  0.32 0.32  0.32 0.32  0.33 0.33
## 0.335    0.34 0.34 0.34  0.34 0.34  0.34 0.34  0.34 0.34  0.34 0.34
## 0.345    0.34 0.34 0.34  0.34 0.34  0.34 0.34  0.34 0.34  0.34 0.34
## 0.355    0.35 0.36 0.36  0.36 0.36  0.36 0.36  0.36 0.36  0.35 0.35
## 0.365    0.36 0.36 0.36  0.36 0.36  0.36 0.36  0.36 0.36  0.36 0.36
## 0.375    0.38 0.38 0.38  0.38 0.38  0.38 0.38  0.38 0.38  0.38 0.38
## 0.385    0.39 0.38 0.38  0.38 0.38  0.38 0.38  0.38 0.38  0.39 0.39
## 0.395    0.40 0.40 0.40  0.40 0.40  0.40 0.40  0.40 0.40  0.40 0.40
## 0.405    0.41 0.40 0.40  0.40 0.40  0.40 0.41  0.41 0.41  0.41 0.41
## 0.415    0.41 0.42 0.42  0.42 0.42  0.42 0.42  0.42 0.42  0.41 0.41
## 0.425    0.42 0.42 0.42  0.42 0.42  0.42 0.42  0.42 0.42  0.42 0.42
## 0.435    0.43 0.44 0.44  0.44 0.44  0.44 0.44  0.44 0.44  0.43 0.43
## 0.445    0.45 0.44 0.44  0.44 0.44  0.44 0.44  0.44 0.44  0.45 0.45
## 0.455    0.46 0.46 0.46  0.46 0.46  0.46 0.46  0.46 0.46  0.46 0.46
## 0.465    0.47 0.46 0.46  0.46 0.46  0.46 0.47  0.47 0.47  0.47 0.47
## 0.475    0.47 0.48 0.48  0.48 0.48  0.48 0.48  0.48 0.48  0.47 0.47
## 0.485    0.48 0.48 0.48  0.48 0.48  0.48 0.48  0.48 0.48  0.48 0.48
## 0.495    0.49 0.50 0.50  0.50 0.50  0.50 0.50  0.50 0.50  0.49 0.49
## 0.505    0.51 0.50 0.50  0.50 0.50  0.50 0.50  0.50 0.50  0.51 0.51
## 0.515    0.52 0.52 0.52  0.52 0.52  0.52 0.52  0.52 0.52  0.52 0.52
## 0.525    0.53 0.52 0.52  0.52 0.52  0.52 0.52  0.52 0.52  0.53 0.53
## 0.535    0.54 0.54 0.54  0.54 0.54  0.54 0.54  0.54 0.54  0.54 0.54
## 0.545    0.55 0.55 0.55  0.55 0.55  0.55 0.54  0.54 0.54  0.55 0.55
## 0.555    0.56 0.56 0.56  0.56 0.56  0.56 0.56  0.56 0.56  0.56 0.56
## 0.565    0.56 0.56 0.56  0.56 0.56  0.56 0.56  0.56 0.56  0.56 0.56
## 0.575    0.57 0.57 0.57  0.57 0.57  0.57 0.58  0.58 0.58  0.57 0.57
## 0.585    0.58 0.58 0.58  0.58 0.58  0.58 0.58  0.58 0.58  0.58 0.58
## 0.595    0.59 0.60 0.60  0.60 0.60  0.60 0.60  0.60 0.60  0.59 0.59
## 0.605    0.60 0.60 0.60  0.60 0.60  0.60 0.60  0.60 0.60  0.60 0.60
## 0.615    0.61 0.62 0.62  0.62 0.62  0.62 0.62  0.62 0.62  0.61 0.61
## 0.625    0.62 0.62 0.62  0.62 0.62  0.62 0.62  0.62 0.62  0.63 0.62
## 0.635    0.64 0.64 0.64  0.64 0.64  0.64 0.64  0.64 0.64  0.64 0.64
## 0.645    0.65 0.64 0.64  0.64 0.64  0.64 0.64  0.64 0.64  0.65 0.65
## 0.655    0.66 0.66 0.66  0.66 0.66  0.66 0.66  0.66 0.66  0.66 0.66
## 0.665    0.67 0.66 0.66  0.66 0.66  0.66 0.66  0.66 0.66  0.67 0.67
## 0.675    0.68 0.68 0.68  0.68 0.68  0.68 0.68  0.68 0.68  0.68 0.68
## 0.685    0.69 0.68 0.68  0.68 0.68  0.68 0.69  0.69 0.69  0.69 0.69
## 0.695    0.69 0.70 0.70  0.70 0.70  0.70 0.70  0.70 0.70  0.69 0.69
## 0.705    0.70 0.70 0.70  0.70 0.70  0.70 0.70  0.70 0.70  0.70 0.70
## 0.715    0.71 0.72 0.72  0.72 0.72  0.72 0.72  0.72 0.72  0.71 0.71
## 0.725    0.72 0.72 0.72  0.72 0.72  0.72 0.72  0.72 0.72  0.72 0.72
## 0.735    0.73 0.74 0.74  0.74 0.74  0.74 0.74  0.74 0.74  0.73 0.73
## 0.745    0.74 0.74 0.74  0.74 0.74  0.74 0.74  0.74 0.74  0.74 0.74
## 0.755    0.76 0.76 0.76  0.76 0.76  0.76 0.76  0.76 0.76  0.76 0.76
## 0.765    0.77 0.76 0.76  0.76 0.76  0.76 0.76  0.76 0.76  0.77 0.77
## 0.775    0.78 0.78 0.78  0.78 0.78  0.78 0.78  0.78 0.78  0.78 0.78
## 0.785    0.79 0.78 0.78  0.78 0.78  0.78 0.78  0.78 0.78  0.79 0.79
## 0.795    0.80 0.80 0.80  0.80 0.80  0.80 0.80  0.80 0.80  0.80 0.80
## 0.805    0.81 0.80 0.80  0.80 0.80  0.80 0.80  0.80 0.80  0.81 0.81
## 0.815    0.81 0.82 0.82  0.82 0.82  0.82 0.81  0.81 0.81  0.81 0.81
## 0.825    0.82 0.82 0.82  0.82 0.82  0.82 0.82  0.82 0.82  0.82 0.82
## 0.835    0.83 0.84 0.84  0.84 0.84  0.84 0.84  0.84 0.84  0.83 0.83
## 0.845    0.84 0.84 0.84  0.84 0.84  0.84 0.84  0.84 0.84  0.84 0.84
## 0.855    0.85 0.86 0.86  0.86 0.86  0.86 0.86  0.86 0.86  0.85 0.85
## 0.865    0.86 0.86 0.86  0.86 0.86  0.86 0.86  0.86 0.86  0.86 0.86
## 0.875    0.88 0.88 0.88  0.88 0.88  0.88 0.88  0.88 0.88  0.88 0.88
## 0.885    0.89 0.88 0.88  0.88 0.88  0.88 0.88  0.88 0.88  0.89 0.89
## 0.895    0.90 0.90 0.90  0.90 0.90  0.90 0.90  0.90 0.90  0.90 0.90
## 0.905    0.91 0.90 0.90  0.90 0.90  0.90 0.90  0.90 0.90  0.91 0.91
## 0.915    0.92 0.92 0.92  0.92 0.92  0.92 0.92  0.92 0.92  0.92 0.92
## 0.925    0.93 0.92 0.92  0.92 0.92  0.92 0.92  0.92 0.92  0.93 0.93
## 0.935    0.94 0.94 0.94  0.94 0.94  0.94 0.94  0.94 0.94  0.94 0.94
## 0.945    0.94 0.94 0.94  0.94 0.94  0.94 0.94  0.94 0.94  0.94 0.94
## 0.955    0.95 0.96 0.96  0.96 0.96  0.96 0.96  0.96 0.96  0.95 0.95
## 0.965    0.96 0.96 0.96  0.96 0.96  0.96 0.96  0.96 0.96  0.96 0.96
## 0.975    0.97 0.98 0.98  0.98 0.98  0.98 0.98  0.98 0.98  0.97 0.97
## 0.985    0.98 0.98 0.98  0.98 0.98  0.98 0.98  0.98 0.98  0.98 0.98
## 0.995    0.99 1.00 1.00  1.00 1.00  1.00 1.00  1.00 1.00  0.99 0.99

Not sure, if exact equality is the correct test here. We should really check only if rounding up vs rounding down happens in each case, but the rounded numbers may actually differ at by a few bits (invisibly when printed by R!) :

symnum(rA1 == rA1[,"xctQ"])
##      s r0 r1.C r1a.C r2.C r2a.C r3.C r3d.C r3 S x
## 0.05 . .  .    .     .    .     .    .     .  | |
## 0.15 . .  .    .     .    .     .    .     .  | |
## 0.25 . .  .    .     .    .     .    .     .  . |
## 0.35 | .  .    .     .    .     |    |     |  | |
## 0.45 | .  .    .     .    .     .    .     .  | |
## 0.55 | |  |    |     |    |     |    |     |  | |
## 0.65 | .  .    .     .    .     |    |     |  | |
## 0.75 . .  .    .     .    .     .    .     .  | |
## 0.85 . .  .    .     .    .     .    .     .  | |
## 0.95 . .  .    .     .    .     .    .     .  | |
symnum(rA2 == rA2[,"xctQ"])
##       s r0 r1.C r1a.C r2.C r2a.C r3.C r3d.C r3 S x
## 0.005 . .  .    .     .    .     .    .     .  | |
## 0.015 . .  .    .     .    .     .    .     .  | |
## 0.025 | .  .    .     .    .     |    |     |  | |
## 0.035 . .  .    .     .    .     .    .     .  | |
## 0.045 . .  .    .     .    .     .    .     .  | |
## 0.055 | |  |    |     |    |     |    |     |  | |
## 0.065 . .  .    .     .    .     .    .     .  | |
## 0.075 . .  .    .     .    .     .    .     .  | |
## 0.085 | .  .    .     .    .     |    |     |  | |
## 0.095 . .  .    .     .    .     .    .     .  | |
## 0.105 . .  .    .     .    .     .    .     .  | |
## 0.115 | |  |    |     |    |     |    |     |  | |
## 0.125 | |  |    |     |    |     |    |     |  . |
## 0.135 . .  .    .     .    .     .    .     .  | |
## 0.145 . .  .    .     .    .     .    .     .  | |
## 0.155 | .  .    .     .    .     .    .     .  | |
## 0.165 . .  .    .     .    .     .    .     .  | |
## 0.175 . .  .    .     .    .     .    .     .  | |
## 0.185 | |  |    |     |    |     |    |     |  | |
## 0.195 . .  .    .     .    .     .    .     .  | |
## 0.205 . .  .    .     .    .     .    .     .  | |
## 0.215 | .  .    .     .    .     .    .     .  | |
## 0.225 . .  .    .     .    .     .    .     .  | |
## 0.235 . .  .    .     .    .     .    .     .  | |
## 0.245 | |  |    |     |    |     |    |     |  | |
## 0.255 . .  .    .     .    .     .    .     .  | |
## 0.265 . .  .    .     .    .     .    .     .  | |
## 0.275 . .  .    .     .    .     .    .     .  | |
## 0.285 . .  .    .     .    .     .    .     .  | |
## 0.295 | .  .    .     .    .     .    .     .  | |
## 0.305 | |  |    |     |    |     |    |     |  | |
## 0.315 . .  .    .     .    .     .    .     .  | |
## 0.325 . .  .    .     .    .     .    .     .  | |
## 0.335 . .  .    .     .    .     .    .     .  | |
## 0.345 . .  .    .     .    .     .    .     .  | |
## 0.355 | .  .    .     .    .     .    .     .  | |
## 0.365 | |  |    |     |    |     |    |     |  | |
## 0.375 . .  .    .     .    .     .    .     .  | |
## 0.385 . .  .    .     .    .     .    .     .  | |
## 0.395 . .  .    .     .    .     .    .     .  | |
## 0.405 | .  .    .     .    .     |    |     |  | |
## 0.415 | .  .    .     .    .     .    .     .  | |
## 0.425 | |  |    |     |    |     |    |     |  | |
## 0.435 | .  .    .     .    .     .    .     .  | |
## 0.445 . .  .    .     .    .     .    .     .  | |
## 0.455 . .  .    .     .    .     .    .     .  | |
## 0.465 | .  .    .     .    .     |    |     |  | |
## 0.475 | .  .    .     .    .     .    .     .  | |
## 0.485 | |  |    |     |    |     |    |     |  | |
## 0.495 | .  .    .     .    .     .    .     .  | |
## 0.505 . .  .    .     .    .     .    .     .  | |
## 0.515 . .  .    .     .    .     .    .     .  | |
## 0.525 . .  .    .     .    .     .    .     .  | |
## 0.535 . .  .    .     .    .     .    .     .  | |
## 0.545 . .  .    .     .    .     .    .     .  | |
## 0.555 . .  .    .     .    .     .    .     .  | |
## 0.565 . .  .    .     .    .     .    .     .  | |
## 0.575 | |  |    |     |    |     .    .     .  | |
## 0.585 | |  |    |     |    |     |    |     |  | |
## 0.595 | .  .    .     .    .     .    .     .  | |
## 0.605 | |  |    |     |    |     |    |     |  | |
## 0.615 | .  .    .     .    .     .    .     .  | |
## 0.625 | |  |    |     |    |     |    |     |  . |
## 0.635 . .  .    .     .    .     .    .     .  | |
## 0.645 . .  .    .     .    .     .    .     .  | |
## 0.655 . .  .    .     .    .     .    .     .  | |
## 0.665 . .  .    .     .    .     .    .     .  | |
## 0.675 . .  .    .     .    .     .    .     .  | |
## 0.685 | .  .    .     .    .     |    |     |  | |
## 0.695 | .  .    .     .    .     .    .     .  | |
## 0.705 | |  |    |     |    |     |    |     |  | |
## 0.715 | .  .    .     .    .     .    .     .  | |
## 0.725 | |  |    |     |    |     |    |     |  | |
## 0.735 | .  .    .     .    .     .    .     .  | |
## 0.745 | |  |    |     |    |     |    |     |  | |
## 0.755 . .  .    .     .    .     .    .     .  | |
## 0.765 . .  .    .     .    .     .    .     .  | |
## 0.775 . .  .    .     .    .     .    .     .  | |
## 0.785 . .  .    .     .    .     .    .     .  | |
## 0.795 . .  .    .     .    .     .    .     .  | |
## 0.805 . .  .    .     .    .     .    .     .  | |
## 0.815 . .  .    .     .    .     .    .     .  | |
## 0.825 | |  |    |     |    |     |    |     |  | |
## 0.835 | .  .    .     .    .     .    .     .  | |
## 0.845 | |  |    |     |    |     |    |     |  | |
## 0.855 | .  .    .     .    .     .    .     .  | |
## 0.865 | |  |    |     |    |     |    |     |  | |
## 0.875 . .  .    .     .    .     .    .     .  | |
## 0.885 . .  .    .     .    .     .    .     .  | |
## 0.895 . .  .    .     .    .     .    .     .  | |
## 0.905 . .  .    .     .    .     .    .     .  | |
## 0.915 . .  .    .     .    .     .    .     .  | |
## 0.925 . .  .    .     .    .     .    .     .  | |
## 0.935 | |  |    |     |    |     |    |     |  | |
## 0.945 | |  |    |     |    |     |    |     |  | |
## 0.955 | .  .    .     .    .     .    .     .  | |
## 0.965 | |  |    |     |    |     |    |     |  | |
## 0.975 | .  .    .     .    .     .    .     .  | |
## 0.985 | |  |    |     |    |     |    |     |  | |
## 0.995 | .  .    .     .    .     .    .     .  | |

Session information

(Note half a dozen non-standard packages present only as dependences of rmarkdown we use for rendering this vignette)

## R version 4.3.2 Patched (2024-01-06 r85796)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Fedora Linux 38 (Thirty Eight)
##
## Matrix products: default
## BLAS:   /u/maechler/R/D/r-patched/F38-64-inst/lib/libRblas.so
## LAPACK: /usr/lib64/liblapack.so.3.11.0
##
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base
##
## other attached packages:
## [1] gmp_0.7-3      round_0.21-0.2
##
## loaded via a namespace (and not attached):
##  [1] digest_0.6.33   R6_2.5.1        fastmap_1.1.1   xfun_0.41
##  [5] cachem_1.0.8    knitr_1.45      htmltools_0.5.7 rmarkdown_2.25
##  [9] lifecycle_1.0.4 cli_3.6.2       sass_0.4.8      jquerylib_0.1.4
## [13] compiler_4.3.2  tools_4.3.2     evaluate_0.23   bslib_0.6.1
## [17] yaml_2.3.8      rlang_1.1.2     jsonlite_1.8.7