diff --git a/src/lib/util-quant.hpp b/src/lib/util-quant.hpp index 361edace7..5648135b9 100644 --- a/src/lib/util-quant.hpp +++ b/src/lib/util-quant.hpp @@ -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 @@ -25,6 +25,8 @@ #define LIB_UTIL_QUANT_H #include +#include +#include @@ -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*/ diff --git a/tests/library/diff/gen-node-basic-test.cpp b/tests/library/diff/gen-node-basic-test.cpp index 0f8fd4769..d7933cf3f 100644 --- a/tests/library/diff/gen-node-basic-test.cpp +++ b/tests/library/diff/gen-node-basic-test.cpp @@ -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 //#include //#include #include -#include 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()) < 1e-5 ); + GenNode n2("π", PI); + CHECK (almostEqual (PI, n2.data.get())); CHECK (n2.isNamed()); CHECK ("π" == n2.idi.getSym()); CHECK ("π" == name(n2)); @@ -178,7 +178,7 @@ namespace test{ CHECK ("NIL" == o3.data.get().getType()); CHECK (GenNode("Ψ", int64_t(42)) == o3.data.get().get("Ψ")); CHECK (42L == o3.data.get().get("Ψ").data.get()); - CHECK (1e-7 > fabs (3.14159265 - o3.data.get().get("π").data.get())); + CHECK (almostEqual (PI, o3.data.get().get("π").data.get())); LuidH luid; //Demonstration: object builder is based on the mutator mechanism for Records...