util: epsilon comparison for doubles

add the usual standard implementation to compare floating point numbers
based on the machine epsilon and the magnitude of the involved numbers
This commit is contained in:
Fischlurch 2015-08-30 04:14:28 +02:00
parent 25f78bfa83
commit efe97b9174
2 changed files with 35 additions and 7 deletions

View file

@ -1,5 +1,5 @@
/*
UTIL-QUANT.hpp - helper functions to deal with division and quantisation
UTIL-QUANT.hpp - helper functions to deal with quantisation and comparison
Copyright (C) Lumiera.org
2011, Hermann Vosseler <Ichthyostega@web.de>
@ -25,6 +25,8 @@
#define LIB_UTIL_QUANT_H
#include <cstdlib>
#include <cfloat>
#include <cmath>
@ -115,5 +117,31 @@ namespace util {
return res;
}
/**
* epsilon comparison of doubles.
* @remarks Floating point calculations are only accurate up to a certain degree,
* and we need to adjust for the magnitude of the involved numbers, since
* floating point numbers are scaled by the exponent. Moreover, we need
* to be careful with very small numbers (close to zero), where calculating
* the difference could yield coarse grained 'subnormal' values.
* @param ulp number of grid steps to allow for difference (default = 2).
* Here, a 'grid step' is the smallest difference to 1.0 which can be
* represented in floating point ('units in the last place')
* @warning don't use this for comparison against zero, rather use an absolute epsilon then.
* @see https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
* @see http://en.cppreference.com/w/cpp/types/numeric_limits/epsilon
* @see https://en.wikipedia.org/wiki/Unit_in_the_last_place
*/
inline bool
almostEqual (double d1, double d2, unsigned int ulp =2)
{
using std::fabs;
return fabs (d1-d2) < DBL_EPSILON * fabs (d1+d2) * ulp
|| fabs (d1-d2) < DBL_MIN; // special treatment for subnormal results
}
} // namespace util
#endif /*UTIL_QUANT_H*/

View file

@ -27,17 +27,18 @@
#include "lib/diff/gen-node.hpp"
#include "lib/diff/record.hpp"
#include "lib/time/timevalue.hpp"
#include "lib/util-quant.hpp"
#include "lib/util.hpp"
//#include <utility>
//#include <string>
//#include <vector>
#include <iostream>
#include <cmath>
using util::isnil;
using util::contains;
using util::isSameObject;
using util::almostEqual;
using lib::hash::LuidH;
using lib::time::FSecs;
using lib::time::Time;
@ -46,7 +47,6 @@ using lib::test::randTime;
//using std::string;
//using std::vector;
//using std::swap;
using std::fabs;
using std::cout;
using std::endl;
@ -61,7 +61,7 @@ namespace test{
namespace {//Test fixture....
const double PI = 3.14159265358979323846264338328;
}//(End)Test fixture
@ -117,8 +117,8 @@ namespace test{
CHECK (contains (name(n1), "_CHILD_"));
// can optionally be named
GenNode n2("π", 3.14159265358979323846264338328);
CHECK (fabs (3.14159265 - n2.data.get<double>()) < 1e-5 );
GenNode n2("π", PI);
CHECK (almostEqual (PI, n2.data.get<double>()));
CHECK (n2.isNamed());
CHECK ("π" == n2.idi.getSym());
CHECK ("π" == name(n2));
@ -178,7 +178,7 @@ namespace test{
CHECK ("NIL" == o3.data.get<Rec>().getType());
CHECK (GenNode("Ψ", int64_t(42)) == o3.data.get<Rec>().get("Ψ"));
CHECK (42L == o3.data.get<Rec>().get("Ψ").data.get<int64_t>());
CHECK (1e-7 > fabs (3.14159265 - o3.data.get<Rec>().get("π").data.get<double>()));
CHECK (almostEqual (PI, o3.data.get<Rec>().get("π").data.get<double>()));
LuidH luid;
//Demonstration: object builder is based on the mutator mechanism for Records...