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
Related
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++.
My medianfilter.cpp class invokes qsort as seen below.
vector<float> medianfilter::computeMedian(vector<float> v) {
float arr[100];
std::copy(v.begin(), v.end(), arr);
unsigned int i;
qsort(arr, v.size(), sizeof(float), compare);
for (i = 0; i < v.size(); i++) {
printf("%f ", arr[i]);
}
printf("median=%d ", arr[v.size() / 2]);
return v;
}
The implementaiton of my comparison is:
int medianfilter::compare(const void * a, const void * b) {
float fa = *(const float*) a;
float fb = *(const float*) b;
return (fa > fb) - (fa < fb);
}
while the declaration in mediafilter.hpp is set private and looks like that:
int compare (const void*, const void*);
A compilation error occurs: cannot convert ‘mediafilter::compare’ from type ‘int (mediafilter::)(const void*, const void*)’ to type ‘__compar_fn_t {aka int (*)(const void*, const void*)}’
I don't understand this error completly. How do I correctly declare and implement this comparison method?
Thanks!
Compare is a non-static member function whereas qsort expects a non-member function (or a static member function). As your compare function doesn't seem to use any non-static members of the class, you could just declare it static. In fact I'm not sure what your median filter class does at all. Perhaps you just need a namespace.
Why not sort the vector directly instead of copying it into a second array? Furthermore your code will break if the vector has more than 100 elements.
The default behavior of sort does just want you need, but for completeness I show how to use a compare function.
I also changed the return type of your function because I don't understand why a function called computeMedian wouldn't return the median..
namespace medianfilter
{
bool compare(float fa, float fb)
{
return fa < fb;
}
float computeMedian(vector<float> v)
{
std::sort(v.begin(), v.end(), compare);
// or simply: std::sort(v.begin(), v.end());
for (size_t i = 0; i < v.size(); i++) {
printf("%f ", v[i]);
}
if (v.empty())
{
// what do you want to happen here?
}
else
{
float median = v[v.size() / 2]; // what should happen if size is odd?
printf("median=%f ", median); // it was %d before
return median;
}
}
}
You can't call compare as it is because it is a member function and requires a this pointer (i.e. it needs to be called on an object). However, as your compare function doesn't need a this pointer, simply make it a static function and your code will compile.
Declare it like this in your class:
static int compare(const void * a, const void * b);
Not directly related to your question (for which you already have the answer) but some observations:
Your calculation of median is wrong. If the number of elements is even you should return the average of the two center values not the value of lower one.
The copy to the array with a set size screams buffer overflow. Copy to another vector and std:sort it or (as suggested by #NeilKirk) just sort the original one unless you have cause not to modify it.
There is no guard against empty input. Median is undefined in this case but your implementation would just return whatever happens to be on arr[0]
Ok, this is more of an appendix to Eli Algranti (excellent) answer than an answer to the original question.
Here is a generic code to compute the quantile quant of a vector of double called x (which the code below preserves).
First things first: there are many definitions of quantiles (R alone lists 9). The code below corresponds to definition #5 (which is also the default quantile function in matlab and generally the ones statisticians think of when they think quantile).
The key idea here is that when the quantile do not fall on a precise observation (e.g. when you want the 15% quantile of an array of length 10) the implementation below realizes the (correct) interpolation (in this case between the 10% and 20%) between adjacent quantile. This is important so that when you increase the number of observations (i m hinting at the name medianfilter here) the value of the quantile do not jump about abruptly but converges smoothly instead (which is one reason why this is the statistician's preferred definition).
The code assumes that x has at least one element (the code below is part of a longer one and I feel this point has been made already).
Unfortunately it s written using many function from the (excellent!) c++ eigen library and it is too late for me at this advanced time in the night to translate the eigen functions --or sanitize the variable names--, but the key ideas should be readable.
#include <Eigen/Dense>
#include <Eigen/QR>
using namespace std;
using namespace Eigen;
using Eigen::MatrixXd;
using Eigen::VectorXd;
using Eigen::VectorXi;
double quantiles(const Ref<const VectorXd>& x,const double quant){
//computes the quantile 'quant' of x.
const int n=x.size();
double lq,uq,fq;
const double q1=n*(double)quant+0.5;
const int index1=floor(q1);
const int index2=ceil(q1);
const double index3=(double)index2-q1;
VectorXd x1=x;
std::nth_element(x1.data(),x1.data()+index1-1,x1.data()+x1.size());
lq=x1(index1-1);
if(index1==index2){
fq=lq;
} else {
uq=x1.segment(index1,x1.size()-index1-1).minCoeff();
fq=lq*index3+uq*(1.0-index3);
}
return(fq);
}
So the code uses one call to nth_element, which has average complexity O(n) [sorry for sloppely using big O for average] and (when n is even) one extra call to min() [which in eigen dialect is noted .minCoeff()] on at most n/2 elements of the vector, which is O(n/2).
This is much better than using partial sort (which would cost O(nlog(n/2)), worst case) or sort (which would cost
O(nlogn))
I have an std::map<mpz_class,int> (for those unfamiliar, mpz_class is a class container for a very large integer number, defined by GMP, Gnu Multiprecision Library). I use a custom Comparator which uses GMP's cmp() function. In the map, I have inserted several std::pair<mpz_class,int> with correct values (they are reasonable when I print them).
However, I noticed map::find was not working correctly, so I printed what the Comparator is comparing. It turns out the second element (key) is always a very wild value integer value, like 128957236027369832796823768439267, way out of scale of the integers I'm working with.
Is there some sort of memory corruption going on that I'm unawares of? Perhaps mpz_class cannot be used in this fashion? How would I work around this problem? I haven't had this problem with other containers so far.
#include <map>
#include <gmpxx.h>
#include <iostream>
struct Equaler {
inline bool operator()(const mpz_class a, const mpz_class b) const {
std::cout << " about to return " << a << "," << b << "," << cmp(a,b) << "\n";
return cmp(a, b);
}
};
int main() {
mpz_class x("38268");
std::map<mpz_class,int,Equaler> map;
map.insert(std::pair<mpz_class,int>(x,42));
map.find(x);
return 0;
}
Output:
about to return 38268,812462232382732367817613904064203084469901797507,-2
The problem is your comparator. std::map expects a comparator which returns true if the first operand should be considered less than the second, and false otherwise. But cmp works differently. It doesn't return a boolean, it returns an integer, in one of three possible states:
negative : lhs < rhs
0 : lhs == rhs
positive : lhs > rhs
However, a negative and a positive integer, in a boolean context, both evaluate to true, so the results of cmp do not convert correctly to what std::map expects. Change this:
return cmp(a, b);
to this:
return cmp(a, b) < 0;
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.
I want to know why std::accumulate (aka reduce) 3rd parameter is needed. For those who do not know what accumulate is, it's used like so:
vector<int> V{1,2,3};
int sum = accumulate(V.begin(), V.end(), 0);
// sum == 6
Call to accumulate is equivalent to:
sum = 0; // 0 - value of 3rd param
for (auto x : V) sum += x;
There is also optional 4th parameter, which allow to replace addition with any other operation.
Rationale that I've heard is that if you need let say not to add up, but multiply elements of a vector, we need other (non-zero) initial value:
vector<int> V{1,2,3};
int product = accumulate(V.begin(), V.end(), 1, multiplies<int>());
But why not do like Python - set initial value for V.begin(), and use range starting from V.begin()+1. Something like this:
int sum = accumulate(V.begin()+1, V.end(), V.begin());
This will work for any op. Why is 3rd parameter needed at all?
You're making a mistaken assumption: that type T is of the same type as the InputIterator.
But std::accumulate is generic, and allows all different kinds of creative accumulations and reductions.
Example #1: Accumulate salary across Employees
Here's a simple example: an Employee class, with many data fields.
class Employee {
/** All kinds of data: name, ID number, phone, email address... */
public:
int monthlyPay() const;
};
You can't meaningfully "accumulate" a set of employees. That makes no sense; it's undefined. But, you can define an accumulation regarding the employees. Let's say we want to sum up all the monthly pay of all employees. std::accumulate can do that:
/** Simple class defining how to add a single Employee's
* monthly pay to our existing tally */
auto accumulate_func = [](int accumulator, const Employee& emp) {
return accumulator + emp.monthlyPay();
};
// And here's how you call the actual calculation:
int TotalMonthlyPayrollCost(const vector<Employee>& V)
{
return std::accumulate(V.begin(), V.end(), 0, accumulate_func);
}
So in this example, we're accumulating an int value over a collection of Employee objects. Here, the accumulation sum isn't the same type of variable that we're actually summing over.
Example #2: Accumulating an average
You can use accumulate for more complex types of accumulations as well - maybe want to append values to a vector; maybe you have some arcane statistic you're tracking across the input; etc. What you accumulate doesn't have to be just a number; it can be something more complex.
For example, here's a simple example of using accumulate to calculate the average of a vector of ints:
// This time our accumulator isn't an int -- it's a structure that lets us
// accumulate an average.
struct average_accumulate_t
{
int sum;
size_t n;
double GetAverage() const { return ((double)sum)/n; }
};
// Here's HOW we add a value to the average:
auto func_accumulate_average =
[](average_accumulate_t accAverage, int value) {
return average_accumulate_t(
{accAverage.sum+value, // value is added to the total sum
accAverage.n+1}); // increment number of values seen
};
double CalculateAverage(const vector<int>& V)
{
average_accumulate_t res =
std::accumulate(V.begin(), V.end(), average_accumulate_t({0,0}), func_accumulate_average)
return res.GetAverage();
}
Example #3: Accumulate a running average
Another reason you need the initial value is because that value isn't always the default/neutral value for the calculation you're making.
Let's build on the average example we've already seen. But now, we want a class that can hold a running average -- that is, we can keep feeding in new values, and check the average so far, across multiple calls.
class RunningAverage
{
average_accumulate_t _avg;
public:
RunningAverage():_avg({0,0}){} // initialize to empty average
double AverageSoFar() const { return _avg.GetAverage(); }
void AddValues(const vector<int>& v)
{
_avg = std::accumulate(v.begin(), v.end(),
_avg, // NOT the default initial {0,0}!
func_accumulate_average);
}
};
int main()
{
RunningAverage r;
r.AddValues(vector<int>({1,1,1}));
std::cout << "Running Average: " << r.AverageSoFar() << std::endl; // 1.0
r.AddValues(vector<int>({-1,-1,-1}));
std::cout << "Running Average: " << r.AverageSoFar() << std::endl; // 0.0
}
This is a case where we absolutely rely on being able to set that initial value for std::accumulate - we need to be able to initialize the accumulation from different starting points.
In summary, std::accumulate is good for any time you're iterating over an input range, and building up one single result across that range. But the result doesn't need to be the same type as the range, and you can't make any assumptions about what initial value to use -- which is why you must have an initial instance to use as the accumulating result.
The way things are, it is annoying for code that knows for sure a range isn't empty and that wants to start accumulating from the first element of the range on. Depending on the operation that is used to accumulate with, it's not always obvious what the 'zero' value to use is.
If on the other hand you only provide a version that requires non-empty ranges, it's annoying for callers that don't know for sure that their ranges aren't empty. An additional burden is put on them.
One perspective is that the best of both worlds is of course to provide both functionality. As an example, Haskell provides both foldl1 and foldr1 (which require non-empty lists) alongside foldl and foldr (which mirror std::transform).
Another perspective is that since the one can be implemented in terms of the other with a trivial transformation (as you've demonstrated: std::transform(std::next(b), e, *b, f) -- std::next is C++11 but the point still stands), it is preferable to make the interface as minimal as it can be with no real loss of expressive power.
Because standard library algorithms are supposed to work for arbitrary ranges of (compatible) iterators. So the first argument to accumulate doesn't have to be begin(), it could be any iterator between begin() and one before end(). It could also be using reverse iterators.
The whole idea is to decouple algorithms from data. Your suggestion, if I understand it correctly, requires a certain structure in the data.
If you wanted accumulate(V.begin()+1, V.end(), V.begin()) you could just write that. But what if you thought v.begin() might be v.end() (i.e. v is empty)? What if v.begin() + 1 is not implemented (because v only implements ++, not generized addition)? What if the type of the accumulator is not the type of the elements? Eg.
std::accumulate(v.begin(), v.end(), 0, [](long count, char c){
return isalpha(c) ? count + 1 : count
});
It's indeed not needed. Our codebase has 2 and 3-argument overloads which use a T{} value.
However, std::accumulate is pretty old; it comes from the original STL. Our codebase has fancy std::enable_if logic to distinguish between "2 iterators and initial value" and "2 iterators and reduction operator". That requires C++11. Our code also uses a trailing return type (auto accumulate(...) -> ...) to calculate the return type, another C++11 feature.