Float comparison in C++98 - c++

I need to write a float comparison function (equal/not equal) but I have to use C++98 and boost libraries at most. I know that float comparison should include epsilon but I don't know how to write such code without using C++11.

One C++98 example:
#include <cmath>
#include <limits>
#include <iostream>
inline bool equal_with_tolerance(float a, float b, float tolerance = std::numeric_limits<float>::epsilon()) {
return std::abs(a - b) < tolerance;
}
int main() {
float a = 0.1f;
float b = 0.1000001f;
std::cout << (a == b) << '\n'; // Outputs 0.
std::cout << equal_with_tolerance(a, b) << '\n'; // Outputs 1.
}
tolerance depends on your problem domain, using std::numeric_limits<float>::epsilon is rarely adequate, see this for more details.

I know that float comparison should include epsilon but I don't know how
You could use std::numeric_limits<float>::epsilon() to get the "machine" epsilon.
However, floating point equality comparison with tolerance is not quite as simple as directly comparing absolute difference to machine epsilon. Any small epsilon is going to devolve the comparison into an equality comparison for large values, which leaves you with zero error tolerance.
Meaningful tolerant comparison, requires that you know what sort of values you're expecting, their magnitude, their sign, expected error that you wish to tolerate.
This blog explains the problem in intricate detail. It suggests following, which may be reasonable for "generic" comparison
bool AlmostEqualRelativeAndAbs(float A, float B,
float maxDiff, float maxRelDiff = FLT_EPSILON)
{
// Check if the numbers are really close -- needed
// when comparing numbers near zero.
float diff = fabs(A - B);
if (diff <= maxDiff)
return true;
A = fabs(A);
B = fabs(B);
float largest = (B > A) ? B : A;
if (diff <= largest * maxRelDiff)
return true;
return false;
}
The example is in C, but trivial to translate to C++ idioms. There is also an ULP based function in the article, but its implementation relies on union type punning that is not allowed in C++.

Related

When to use std::numeric_limits<double>::espsilon() instead of DBL_EPSILON

I understood that std::numeric_limits::espsilon() and DBL_EPSILON should deliver the same value but are defined in different headers, limits, and cfloat. Which makes std::numeric_limits::espsilon() a c++ style way of writing and DBL_EPSILON the c style.
My question is if there is any benefit in using std::numeric_limits::espsilon() over DBL_EPSILON in c++ project? Aside from a clean c++ coding style. Or did I understand this completely wrong?
Here on this page https://en.cppreference.com/w/cpp/types/numeric_limits you can find tables of what are the C marco equivalents of the std::numeric_limits.
They are equivalents, so for any pair of std::limits function/constant and C macro you find in the table, they can be interchanged.
The big difference is in generic code:
template <typename T>
void foo() {
std::cout << std::numeric_limits<T>::epsilon();
}
Doing the same with C macros would require to write much more code. Also any opportunity to not use a macro is a good one.
First of all DBL_EPSILON is a C API so it is a good practice to use C++ specific API in C++ code. I know it is more typing, still it is a good practice. In my code when I need epsilon multiple times for single type, I just bind it to some constexpr.
More important thing this is a great tool when you write a template. For example:
template<std::floating_point T>
bool fuzzyCompare(T a, T b)
{
return std::fabs(a - b) <= 4 * std::max(std::fabs(a), std::fabs(b)) * std::numeric_limits<T>::espsilon();
}
One obvious advantage of using std::numeric_limits<T>::epsilon is generic function. Imagine if you write some function like an almost-equal. This function should accept floating numbers of different precisions, including double, float, long double. Or maybe even integer types as well. To write this with the macro solution, you would have to write an overload for each of the floating point types:
bool almost_equal(float a, float b, int k)
{
return std::abs(a - b) <= FLT_EPSILON * std::abs(a + b) * k;
}
bool almost_equal(double a, double b, int k)
{
return std::abs(a - b) <= DBL_EPSILON * std::abs(a + b) * k;
}
...
But with the numeric_limits template, you can simply write a single function for all of them:
template<typename T>
bool almost_equal(T a, T b, int k)
{
return std::abs(a - b) <= std::numeric_limits<T>::epsilon() * std::abs(a + b) * k;
}

c++ std set insert not "working"

I'm having some problems with std set. I know that it does not allows you to insert repeated elements and (I think that) my code is not trying to insert repeated elements. But it seems like the set is not inserting both elements. What is the problem? Is the collection considering both elements equal? Why?
#include <bits/stdc++.h>
using namespace std;
struct sam{
double a,b, tam;
sam(){
}
sam(double a1, double b1){
a = a1;
b = b1;
tam = b - a;
}
bool operator<(const sam &p) const{
return tam > p.tam;
}
};
set<sam> ssw;
int main(void){
ssw.insert(sam(0,2));
ssw.insert(sam(4,6));
cout<<ssw.size()<<"\n"; // prints "1"
return 0;
}
For both objects, the value of tam is 2.0. Since the operator< function works with that value, the two objects are considered to be equal.
BTW, using a floating point number to compare two objects is not a good idea. You can get unexpected results due to the imprecise nature of how floating points are represented.
In std::set
In imprecise terms, two objects a and b are considered equivalent (not
unique) if neither compares less than the other: !comp(a, b) &&
!comp(b, a)
In your case bool operator< not satisfy the above condition hence set treats them not unique.
Currently your comparator returns same values for both the inserts. Hence, only one item is successfully inserted. The other is just a duplicate, and is hence, ignored.
Maybe you meant this:
bool operator<(const sam &p) const{
return ( (a > p.a) || (b > p.b) || (tam > p.tam) );
}

Compute numerical error

As seen in this question, there is a difference between the results MKL gives, between serial and distributed execution. For that reason, I would like to study that error. From my book I have:
|ε_(x_c)| = |x - x_c| <= 1/2 * 10^(-d), where d specifies the decimal digits that are accurate, between the actual number, x and the number the computer has, x_c.
|ρ_(x_c)| = |x - x_c|/|x| <= 5 * 10^(-s) is the absolute relative error, where s specifies the number of significant digits.
So, we can write code like this:
double calc_error(double a,double x)
{
return std::abs(x-a)/std::abs(a);
}
in order to compute the absolute error for example, as seen here.
Are there more types of errors to study, except from the absolute error and the absolute relative error?
Here are some of my data to play with:
serial gives:
-250207683.634793 -1353198687.861288 2816966067.598196 -144344843844.616425 323890119928.788757
distributed gives:
-250207683.634692 -1353198687.861386 2816966067.598891 -144344843844.617096 323890119928.788757
and then I can expand the idea(s) to the actual data and results.
It doesn't get much more complicated than absolute and absolute relative errors. There is another method that compares integer-representations of floating-point formats, the idea being that you want your "tolerance" to adapt with the magnitude of the numbers you are comparing (specifically because there aren't "as many" numbers representable depending on the magnitude).
All in all, I think your question is very similar to floating-point comparison, for which there is this excellent guide, and this more exhaustive but much longer paper.
It might also be worth throwing in these for comparing floating point values:
#include <limits>
#include <cmath>
template <class T>
struct fp_equal_strict
{
inline bool operator() ( const T& a, const T& b )
{
return std::abs(a - b)
<= std::max(
std::numeric_limits<T>::min() * std::min( std::abs(a), std::abs(b) ),
std::numeric_limits<T>::epsilon()
);
}
};
template <class T>
struct fp_equal_loose
{
inline bool operator() ( const T& a, const T& b )
{
return std::abs(a - b)
<= std::max(
std::numeric_limits<T>::min() * std::max( std::abs(a), std::abs(b) ),
std::numeric_limits<T>::epsilon()
);
}
};
template <class T>
struct fp_greater
{
inline bool operator() ( const T& a, const T& b )
{
return (a - b) >= std::numeric_limits<T>::min() * std::max( std::abs(a), std::abs(b) );
}
};
template <class T>
struct fp_lesser
{
inline bool operator() ( const T& a, const T& b )
{
return (b - a) >= std::numeric_limits<T>::min() * std::max( std::abs(a), std::abs(b) );
}
};
I would mention that it is also possible to perform an ULPs (Units in the Last Place) comparison, which shows how far away two floating point numbers are in the binary representation. This is a nice indication of "closeness" since if two numbers are for example one ULP apart it means that there is no floating point number between them, so they are as close as possible in the binary representation without acutally being equal.
This method is descrbied here which is a more recent version than the article linked from the accepted answer by the same author. Sample code is also provided.
As an aside, but related to the context of your work (comparing sequential vs parallel floating point computations) it is important to note that floating point operations are not associative which means parallel implementations may not in general give the same result as sequential implementations. Even changing the compiler and optimisation options can actually lead to different results (e.g. GCC vs ICC, -O0 vs -O3).
An example algorithm on how to reduce the error computation for performing summation of floating point numbers can be found here and a comprehensive document by the author of that algorithm can be found here.

Will std::sort work correctly if I defined a custom compare function for floating number?

For the issue of the floating precision, I defined my custom compare function for floating numbers:
bool cmp(double a, double b)
{
if(abs(a - b) <= eps) return false;
return a < b;
}
Then I call sort on some array of floating numbers. I've heard that some bad compare function will cause the sort to segment fault. I just wondering will cmp work correctly for sort? On one hand, cmp satisfied the associating rule. But on the other hand, cmp(x - eps, x) == false && cmp(x, x + eps) == false doesn't mean cmp(x - eps, x + eps) == false.
I didn't use sort directly on floating numbers because what I want to sort is pair<double, double>.
For example:
(1,2), (2,1), (2.000000001, 0)
I'd like to consider 2 and 2.000000001 as the same and expect the result to be:
(1,2), (2.000000001, 0), (2,1)
std::sort requires a comparer that defines a strict weak ordering. This means, among other things, that the following condition must be met:
We define two items, a and b, to be equivalent (a === b) if !cmp(a, b) && !cmp(b, a)
Equivalence is transitive: a === b && b === c => a === c
As you already say in your question, your function cmp() does not meet these conditions, so you cannot use your function in std::sort(). Not only the result of the algorithm will be unpredictable, which is bad unless you are actually looking for this unpredictability (cf. randomize): if you have a few values that are very close to each other, such that any of them compare true with some, but false with some others, the algorithm might enter an infinite loop.
So the answer is no, you cannot use your function cmp() in std::sort() unless you want to risk your program freezing.
Why would you bother to make an approximate less-than comparison? That makes no sense.
Just sort your array strictly by actual values.
Then use your approximate comparison function to determine which of the elements you wish to consider to be equal.
(The equivalent in English would be the infamous "almost better". Think about it.)
It's possible to define a comparison function for floating point that groups similar values. You do so by rounding:
bool cmp(double a, double b)
{
const double eps = 0.0001;
int a_exp;
double a_mant = frexp(a, &a_exp); // Between 0.5 and 1.0
a_mant -= modf(a_mant, eps); // Round a_mant to 0.00001
a = ldexp(a_mant, a_exp); // Round a to 0.00001 * 10^a_exp
// and the same for b
int b_exp;
double b_mant = frexp(b, &b_exp);
b_mant -= modf(b_mant, eps);
b = ldexp(b_mant, b_exp);
// Compare rounded results.
return a < b;
}
Now cmp(a,b)==true implies that a<b, and a==b and a>b both imply cmp(a,b)==false.

Custom EXPECT_NEAR macro in Google Test

Scope: Using Google Test and OpenCV.
I'd like to test that my Vec3f equals another Vec3f. Vec3f is a vector in OpenCV of dimension 3 and type float. The ==-operator is defined, so EXPECT_EQ(Vec3f(), Vec3f()) works.
But as they are floats, I'd like to use the EXPECT_NEAR(float a, float b, float delta) macro. What can I do so that I can use it like EXPECT_NEAR(vec_a, vec_b, float delta)?
At the moment I am looping through each element of the vector and doing an EXPECT_NEAR there.
This might be related: Convenient method in GoogleTest for a double comparison of not equal?
You can use the Pointwise() matcher from Google Mock. Combine it with a custom matcher that checks that the two arguments are near:
#include <tr1/tuple>
#include <gmock/gmock.h>
using std::tr1::get;
using testing::Pointwise;
MATCHER_P(NearWithPrecision, precision, "") {
return abs(get<0>(arg) - get<1>(arg)) < precision;
}
TEST(FooTest, ArraysNear) {
EXPECT_THAT(result_array, Pointwise(NearWithPrecision(0.1), expected_array));
}
You are doing basically the correct thing. However, I would use a custom assertion function like:
::testing::AssertionResult AreAllElementsInVectorNear(const Vec3f& a, const Vect3f& b, float delta) {
if ([MAGIC])
return ::testing::AssertionSuccess();
else
return ::testing::AssertionFailure() << "Vectors differ by more than " << delta;
}
MAGIC would then include your code to e.g. compare if both vectors have the same size, followed by iterating over all elements and mutually check if the elements at the same index differ by no more than the delta. Note that the code assumes that the << operator is provided for Vec3f.
The function then is used:
EXPECT_TRUE(AreAllElementsInVectorNear(a, b, 0.1))
If the expect fails the output might be:
Value of: AreAllElementsInVectorNear(a, b, 0.1)
Actual: false (Vectors differ by more then 0.1)
Expected: true