The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.

Title: Fast Relative Comparisons of Floating Point Numbers in 'C++'
Version: 0.4.0
Description: Compare double-precision floating point vectors using relative differences. All equality operations are calculated using 'cpp11'.
License: MIT + file LICENSE
BugReports: https://github.com/NicChr/cppdoubles/issues
Depends: R (≥ 3.5.0)
Suggests: bench, testthat (≥ 3.0.0)
LinkingTo: cpp11
Config/testthat/edition: 3
Encoding: UTF-8
RoxygenNote: 7.3.2
NeedsCompilation: yes
Packaged: 2025-06-09 13:54:57 UTC; Nmc5
Author: Nick Christofides ORCID iD [aut, cre]
Maintainer: Nick Christofides <nick.christofides.r@gmail.com>
Repository: CRAN
Date/Publication: 2025-06-09 14:20:02 UTC

Relative comparison of double-precision floating point numbers

Description

Fast and efficient methods for comparing floating point numbers using relative differences.

Usage

x %~==% y

x %~>=% y

x %~>% y

x %~<=% y

x %~<% y

double_equal(x, y, tol = get_tolerance())

double_gte(x, y, tol = get_tolerance())

double_gt(x, y, tol = get_tolerance())

double_lte(x, y, tol = get_tolerance())

double_lt(x, y, tol = get_tolerance())

Arguments

x

A double vector.

y

A double vector.

tol

A double vector of tolerances.

Details

When either x[i] or y[i] contain a number very close to zero, absolute differences are used, otherwise relative differences are used.

The output of double_equal() is commutative, which means the order of arguments don't matter whereas this is not the case for all.equal.numeric().

The calculation is done in C++ and is quite efficient. Recycling follows the usual R rules and is done without allocating additional memory.

Value

A logical vector.

Examples

library(cppdoubles)

### Basic usage ###

# Standard equality operator
sqrt(2)^2 == 2

# approximate equality operator
sqrt(2)^2 %~==% 2

sqrt(2)^2 %~>=% 2
sqrt(2)^2 %~<=% 2
sqrt(2)^2 %~>% 2
sqrt(2)^2 %~<% 2

# Alternatively
double_equal(2, sqrt(2)^2)
double_gte(2, sqrt(2)^2)
double_lte(2, sqrt(2)^2)
double_gt(2, sqrt(2)^2)
double_lt(2, sqrt(2)^2)

rel_diff(1, 1 + 2e-10)
double_equal(1, 1 + 2e-10, tol = sqrt(.Machine$double.eps))
double_equal(1, 1 + 2e-10, tol = 1e-10)

# Optionally set a threshold for all comparison
options(cppdoubles.tolerance = 1e-10)
double_equal(1, 1 + 2e-10)

# Floating point errors magnified example

x1 <- 1.1 * 100 * 10^200
x2 <- 110 * 10^200

abs_diff(x1, x2) # Large absolute difference
rel_diff(x1, x2) # Very small relative difference as expected

double_equal(x1, x2)

# all.equal is not commutative but double_equal is
all.equal(10^-8, 2 * 10^-8)
all.equal(2 * 10^-8, 10^-8)

double_equal(10^-8, 2 * 10^-8)
double_equal(2 * 10^-8, 10^-8)

# All comparisons are vectorised and recycled

double_equal(sqrt(1:10),
             sqrt(1:5),
             tol = c(-Inf, 1e-10, Inf))

# One can check for whole numbers like so
whole_number <- function(x, tol = get_tolerance()){
  double_equal(x, round(x))
}
whole_number(seq(-5, 5, 0.25))

Are all values of x nearly equal (within a tolerance) to all values of y?

Description

A memory-efficient alternative to all.equal.numeric().

Usage

all_equal(x, y, tol = get_tolerance(), na.rm = FALSE)

Arguments

x

A double vector.

y

A double vector.

tol

A double vector of tolerances.

na.rm

Should NA values be ignored? Default is FALSE.

Details

all_equal compares each pair of double-precision floating point numbers in the same way as double_equal. If any numbers differ, the algorithm breaks immediately, which can offer significant speed when there are differences at the start of a vector. All arguments are recycled except na.rm.

Value

A logical vector of length 1.

The result should match all(double_equal(x, y)), including the way NA values are handled.

Examples

library(cppdoubles)
library(bench)
x <- seq(0, 1, 0.2)
y <- sqrt(x)^2

all_equal(x, y)

# Comparison to all.equal
z <- runif(10^4, 1, 100)
ones <- rep(1, length(z))
mark(base = isTRUE(all.equal(z, z)),
            cppdoubles = all_equal(z, z),
            iterations = 100)
mark(base = isTRUE(all.equal(z, ones)),
            cppdoubles = all_equal(z, ones),
            iterations = 100)


Absolute and relative difference

Description

Calculate absolute differences with abs_diff() and relative differences with rel_diff()

Usage

rel_diff(x, y, scale = NA_real_)

abs_diff(x, y)

Arguments

x

A double vector.

y

A double vector.

scale

A double vector. When NA, the scale is calculated as max(abs(x), abs(y)).

Details

Relative difference

The relative difference in this package is calculated as abs_diff(x / scale, y / scale) except in the case that both x and y are approximately 0 which results in 0.

The scale is calculated as max(abs(x), abs(y)) by default when scale is NA. This has the nice property of making rel_diff() a commutative function in which the order of the arguments doesn't matter. You can of course supply your own scale.

For info, an R way to calculate the relative difference is as follows

  r_rel_diff <- function(x, y){
    ax <- abs(x)
    ay <- abs(y)
    scale <- pmax(ax, ay)
    ifelse(
      ax < sqrt(.Machine$double.eps) & ay < sqrt(.Machine$double.eps),
      0,
      abs_diff(x / scale, y / scale)
    )
  }

This is much slower than the C++ written rel_diff.

Comparison with all.equal()

As mentioned above, unlike base::all.equal(), rel_diff() is commutative. To match the relative difference calculation used by all.equal(), simply set scale = x.

Therefore, to make a vectorised binary version of all.equal(), we can write for example the following:

  all.equal2 <- \(x, y, tol = get_tolerance()) rel_diff(x, y, scale = x) < tol

Value

A numeric vector.


Get and set package-wide tolerance

Description

Get and set package-wide tolerance

Usage

get_tolerance()

set_tolerance(x)

Arguments

x

⁠[double(1)]⁠ - Tolerance to be used across all cppdoubles functions.

Value

Either sets or gets the tolerance to be used package-wide.

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.