Skip to content

Instantly share code, notes, and snippets.

@nanxstats
Created November 20, 2025 18:16
Show Gist options
  • Select an option

  • Save nanxstats/598d8cbda027959040da66be2a9ba095 to your computer and use it in GitHub Desktop.

Select an option

Save nanxstats/598d8cbda027959040da66be2a9ba095 to your computer and use it in GitHub Desktop.
Compare two floating point number equivalence testing methods
library(testthat)
# Method A: round to 6 significant digits, then compare with default tiny tolerance
equal_after_signif <- function(x, y) {
expect_equal(signif(x, digits = 6), signif(y, digits = 6))
}
# Method B: compare with relative/absolute tolerance = 1e-6
equal_with_tolerance <- function(x, y) {
expect_equal(x, y, tolerance = 1e-6)
}
test_that("Example 1: signif() says equal, tolerance says not equal", {
x <- 1.000003
y <- 1
# Method A should succeed
equal_after_signif(x, y)
# Method B should fail
expect_failure(equal_with_tolerance(x, y))
})
test_that("Example 2: tolerance says equal, signif() says not equal", {
x <- 7.000006
y <- 7
# Method A should fail
expect_failure(equal_after_signif(x, y))
# Method B should succeed
equal_with_tolerance(x, y)
})
@jdblischak
Copy link

Thanks for putting together this nice example @nanxstats! My takeaway from your code is that signif() is sensitive to rounding.

To confirm my understanding, I put together an example with many randomly generated numbers. The majority of the time the two approaches agree, and when they don't, it appears to be due to rounding.

set.seed(20260108)

x <- rnorm(10^3)
e <- rnorm(10^3, mean = 1e-6, sd = 1e-6)
y <- x + e

all_equal_signif <- function(x, y) {
  isTRUE(all.equal(signif(x, digits = 6), signif(y, digits = 6)))
}

all_equal_tolerance <- function(x, y) {
  isTRUE(all.equal(x, y, tolerance = 1e-6))
}

results_signif <- mapply(all_equal_signif, x, y)
results_tolerance <- mapply(all_equal_tolerance, x, y)

table(results_signif, results_tolerance)
##               results_tolerance
## results_signif FALSE TRUE
##          FALSE   520   81
##          TRUE    115  284

# agreed equal
summary(abs(e)[ results_signif & results_signif == results_tolerance])
##      Min.   1st Qu.    Median      Mean   3rd Qu.      Max.
## 5.166e-09 1.677e-07 3.766e-07 4.964e-07 7.639e-07 1.969e-06

# agreed unequal
summary(abs(e)[!results_signif & results_signif == results_tolerance])
##      Min.   1st Qu.    Median      Mean   3rd Qu.      Max.
## 1.190e-07 1.025e-06 1.462e-06 1.534e-06 2.021e-06 4.345e-06

# signif equal, tolerance unequal
summary(abs(e)[ results_signif & results_signif != results_tolerance])
##      Min.   1st Qu.    Median      Mean   3rd Qu.      Max.
## 2.135e-07 8.464e-07 1.681e-06 1.608e-06 2.146e-06 3.502e-06

# signif unequal, tolerance equal
summary(abs(e)[!results_signif & results_signif != results_tolerance])
##      Min.   1st Qu.    Median      Mean   3rd Qu.      Max.
## 6.383e-08 3.465e-07 5.723e-07 6.634e-07 8.251e-07 2.615e-06

# signif equal, tolerance unequal
x[ results_signif & results_signif != results_tolerance][1:5]
## [1] -1.2000800  1.3032864 -1.2311049 -0.5810328 -1.0426949
y[ results_signif & results_signif != results_tolerance][1:5]
## [1] -1.2000783  1.3032882 -1.2311024 -0.5810334 -1.0426933
signif(x[ results_signif & results_signif != results_tolerance][1:5], digits = 6)
## [1] -1.200080  1.303290 -1.231100 -0.581033 -1.042690
signif(y[ results_signif & results_signif != results_tolerance][1:5], digits = 6)
## [1] -1.200080  1.303290 -1.231100 -0.581033 -1.042690

# signif unequal, tolerance equal
x[!results_signif & results_signif != results_tolerance][1:5]
## [1]  0.7441415 -0.5765637 -2.2040561  0.2985585  1.7900641
y[!results_signif & results_signif != results_tolerance][1:5]
## [1]  0.7441419 -0.5765633 -2.2040543  0.2985586  1.7900654
signif(x[!results_signif & results_signif != results_tolerance][1:5], digits = 6)
## [1]  0.744141 -0.576564 -2.204060  0.298558  1.790060
signif(y[!results_signif & results_signif != results_tolerance][1:5], digits = 6)
## [1]  0.744142 -0.576563 -2.204050  0.298559  1.790070

@nanxstats
Copy link
Author

Nice!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment