the Compartmented Robust Posix C++ Unit Test system

crpcut::ulps_diff

Test that two floating point numbers do not differ by more than a fixed number of possibly representable values for the type.

Used in: Template parameter to crpcut::match<matcher>(...) in an ASSERT_PRED(pred, ...) check.

Verify that a and b do not differ by more than a fixed number of Units of Least Precision. See Cygnus information on comparing floating point numbers for an introduction to ULPS. The two parameters a and b must have the same floating point type.

It is, optionally, possible to decide whether infinity is a valid number (one ULPS higher than std::numeric_limits<T>::max()) by by setting the second parameter to the constructor to one of crpcut::include_inf or crpcut::exclude_inf. By default crpcut::exclude_inf is assumed.

[Caution]Caution
ULPS diff only works for float and double, and is only tested on x86 and compatible architectures and is almost certain to fail horrendously on others.

Example: the test program

     
     #include <crpcut.hpp>
     #include <numeric>
     #include <limits>
     
     template <typename T>
     class moving_avg
     {
     public:
       moving_avg() : avg(T()), n(T()) {}
       moving_avg& operator+=(T t) { ++n; avg-= (avg - t)/n; return *this; }
       operator T() const { return avg; }
     private:
       T avg;
       T n;
     };
     
     static const unsigned count = 300;
     
     TEST(too_narrow)
     {
       moving_avg<float> mavg;
       float sum = 0.0;
       for (unsigned n = 0; n < count; ++n)
         {
           mavg+= 1.0f/3 + float(n);
           sum+= 1.0f/3 + float(n);
        }
       float avg = sum/count;
       ASSERT_PRED(crpcut::match<crpcut::ulps_diff>(2U), float(mavg), avg);
     }
     
     TEST(close_enough)
     {
       moving_avg<float> mavg;
       float sum = 0.0;
       for (unsigned n = 0; n < count; ++n)
         {
           mavg+= 1.0f/3 + float(n);
           sum+= 1.0f/3 + float(n);
         }
       float avg = sum/count;
       ASSERT_PRED(crpcut::match<crpcut::ulps_diff>(10U), float(mavg), avg);
     }
     
     int main(int argc, char *argv[])
     {
       return crpcut::run(argc, argv);
     }

      

fails one test:


     FAILED!: too_narrow
     phase="running"  --------------------------------------------------------------
     samples/ulps_diff.cpp:56
     ASSERT_PRED(crpcut::match<crpcut::ulps_diff>(2U), float(mavg), avg)
       param1 = 149.833
       param2 = 149.833
     for crpcut::match<crpcut::ulps_diff>(2U):     Max allowed diff = 2 ULPS
         Actual diff = 5 ULPS
     
     -------------------------------------------------------------------------------
     ===============================================================================
     2 test cases selected
     
                    Sum   Critical   Non-critical
     PASSED   :       1          1              0
     FAILED   :       1          1              0

      

See also crpcut::abs_diff and crpcut::relative_diff for alternative floating point number matching methods.