Note: The relative distance/error between a and b is defined as fabs( (a-b) / min(a,b) )
That is not a good idea if one of them is zero and it also appears to be potentially asymmetric in sign:
- Comparing 10,-1 and then -10,1 say: in the first case you would end up with 11 and in the second 1.1;
- Even if a and b have the same sign it isn't sign-symmetric:
a=10, b=1 would give 9.0
a=-10, b=-1 would give 0.9
publicstatic boolean nearlyEqual(float a, float b, float epsilon) {
final float absA = Math.abs(a);
final float absB = Math.abs(b);
final float diff = Math.abs(a - b);
if (a == b) { // shortcut, handles infinities
returntrue;
} elseif (a == 0 || b == 0 || (absA + absB < Float.MIN_NORMAL)) {
// a or b is zero or both are extremely close to it
// relative error is less meaningful here
return diff < (epsilon * Float.MIN_NORMAL);
} else { // use relative error
return diff / Math.min((absA + absB), Float.MAX_VALUE) < epsilon;
}
}
This method passes tests for many important special cases, but as you can see, it uses some quite non-obvious logic. In particular, it has to use a completely different definition of error margin when a or b is zero, because the classical definition of relative error becomes meaningless in those cases.
There are some cases where the method above still produces unexpected results (in particular, it’s much stricter when one value is nearly zero than when it is exactly zero), and some of the tests it was developed to pass probably specify behaviour that is not appropriate for some applications. Before using it, make sure it’s appropriate for your application!
It is asymmetric, but it is only used to check if the two values are 'close enough' to each other.
My (edited) second example is worse:
a=10, b=1 would give 9.0
a=-10, b=-1 would give 0.9
I would regard both as the same relative distance apart.
I don't have the boost libraries to check if their code actually corresponds to their documentation.
Personally, for physical problems I prefer to work either with "the units appropriate to the problem" or non-dimensional units: then absolute difference would be fine.
If I absolutely had to find a definition for relative difference I would probably use
abs(a-b) / (0.5*(abs(a)+abs(b)))
and then the only "edge case" I would have to deal with would be both a and b being zero.
> I don't have the boost libraries to check if their code actually corresponds to their documentation.
This is what their documentation states:
The following special cases are handled as follows:
. If either of a or b is a NaN, then returns the largest representable value for T: for example for type double, this is std::numeric_limits<double>::max() which is the same as DBL_MAX or 1.7976931348623157e+308.
. If a and b differ in sign then returns the largest representable value for T.
. If both a and b are both infinities (of the same sign), then returns zero.
. If just one of a and b is an infinity, then returns the largest representable value for T.
. If both a and b are zero then returns zero.
. If just one of a or b is a zero or a denormalized value, then it is treated as if it were the smallest (non-denormalized) value representable in T for the purposes of the above calculation.
These rules were primarily designed to assist with our own test suite, they are designed to be robust enough that the function can in most cases be used blindly, including in cases where the expected result is actually too small to represent in type T and underflows to zero.
#include <iostream>
#include <boost/math/special_functions/relative_difference.hpp>
using boost::math::relative_difference;
int main()
{
std::cout << "relative_difference for (1,10) = " << relative_difference(1.0f, 10.0f) << '\n';
std::cout << "relative_difference for (-1,-10) = " << relative_difference(-1.0f, -10.0f) << '\n';
}
Both give 9.0
Neither of these cases is a "special case".
In the second case that simply does not correspond to the formula fabs( (a-b) / min(a,b) ) stated in the boost documentation. I suspect that really it is abs(a) and abs(b) that are inside that min() operation.