Exact Decimal Rounding via Rationals

Martin Mächler

July 23, 2020; rendered on 2024-01-10

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