Given two arrays of numbers, I wish to test whether pairs of numbers are equal to the precision of the least precise of each pair of numbers.
This problem originates from validating the reproduction of presented numbers. I have been given a set of (rounded) numbers, an attempt to replicate them has produced more precise numbers. I need to report whether the less precise numbers are rounded versions of the more precise numbers.
For example, the following pair of vectors should all return true
input_a = c(0.01, 2.2, 3.33, 44.4, 560, 700) # less precise values provided
input_b = c(0.011, 2.22, 3.333, 44.4000004, 555, 660) # more precise replication
because when rounded to the lowest pair-wise precision the two vectors are equal:
pair_wise_precision = c(2, 1, 2, 1, -1, -2)
input_a_rounded = rep(NA, 6)
input_b_rounded = rep(NA, 6)
for(ii in 1:6){
input_a_rounded[ii] = round(input_a[ii], pair_wise_precision[ii])
input_b_rounded[ii] = round(input_b[ii], pair_wise_precision[ii])
}
all(input_a_rounded == input_b_rounded)
# TRUE
# ignoring machine precision
However, I need to do this without knowing the pair-wise precision.
Two approaches I have identified:
However, both of these approaches feel cumbersome. I have seen in another language the option to round one number of match the precision of another number (sorry, can't recall which). But I can not find this functionality in R.
(This is not a problem about floating point numbers or inaccuracy due to machine precision. I am comfortable handling these separately.)
Edit in response to comments:
round(12300, -2)
and round(12340, -2)
. If comparing 530 and 570, then the least precise value is rounded to the nearest 10, hence we compare round(530, -1)
and round(570, -1)
.My initial thinking followed @jay.sf's approach to analyse values as numeric. However, considering the values as character provides another way to determine rounding:
was_rounded_to = function(x){
x = as.character(x)
location_of_dot = as.numeric(regexpr("\\.", x))
ref_point = ifelse(location_of_dot < 0, nchar(x), location_of_dot)
last_non_zero = sapply(gregexpr("[1-9]", x), max)
return(last_non_zero - ref_point)
}
# slight expansion in test cases
a <- c(0.01, 2.2, 3.33, 44.4, 555, 700, 530, 1110, 440, 3330)
b <- c(0.011, 2.22, 3.333, 44.4000004, 560, 660, 570, 1120, 4400, 3300)
rounding = pmin(was_rounded_to(a), was_rounded_to(b))
mapply(round, a, digits = rounding) == mapply(round, b, digits = rounding)
Special case: If the numbers only differ by rounding, then it is easier to determine the magnitude by examining the difference:
a <- c(0.01, 2.2, 3.33, 44.4, 555, 700)
b <- c(0.011, 2.22, 3.333, 44.4000004, 560, 660)
abs_diff = abs(a-b)
mag = -floor(log10(abs_diff ) + 1e-15)
mapply(round, a, digits = mag - 1) == mapply(round, b, digits = mag - 1)
However, this fails when the numbers differ by more than rounding. For example: a = 530
and b = 540
will incorrectly round both 530 and 540 to 500.