[test] comparing floating point values

OK, here is an opportunity to get me clear on comparing floating point values. I've found a very simple example, that I can not get to work -- and understand... Say I have a class that saves and restores a class. In the class, I have a double, a float, and a long double. As a test, I set the "floating" values to std::numeric_limits< T >::min() and std::numeric_limits< T >::max(), and then save the values, restore them, and compare the restored values to the originals. Obviously, I can not use BOOST_CHECK_EQUAL(), but I am clueless as to how to use the macros for comparing floating point values. Is there anything in numeric_limits that I should use to base the percentage value? Here's a chance for someone to finally clear my understanding of how to compare floating point value... please ?!?!? Thanks!

Jody Hagins wrote:
Here's a chance for someone to finally clear my understanding of how to compare floating point value... please ?!?!?
:-) Let's say you want the tolerance to be N*eps where eps is the machine epsilon and N is you level of tolerance, then you can use either: BOOST_CHECK_CLOSE(a, b, N*100*numeric_limits<T>::epsilon()); // tolerance as a persent or: BOOST_CHECK_FRACTION(a, b, N*numeric_limits<T>::epsilon()); // tolerance as a fraction. There are still a couple of things that can get you: 1) This will fail if one of a and b is zero and the other isn't. Generally speeking you may want to treat all values below some threshold as "effectively zero", but what that threshold is depends on your use case. 2) This fails if a and b are both infinity, probably a Boost.Test bug. 3) This fails on MacOS X with T=long double, this is a problem with the excessively screwy long double on that platform: numeric_limits<long double>::epsilon() is defined to be the same as numeric_limits<double>::min(), which is very small indeed and much smaller than the usual "sane" value for epsilon of ldexp(1.0, 1-numeric_limits<long double>::digits). The latter expression is a viable replacement for if you care about MacOS X BTW. HTH, John.

On Tue, 15 May 2007 17:09:14 +0100 "John Maddock" <john@johnmaddock.co.uk> wrote: Thanks, John!!! I'm still not entirely sure I understand it, but it works ;-) I tested it with a std::stringstream... in and out... and after setting the stream precision large enough, the values in/out compared and the test passed. I then used the same comparisons for the values when I save/restore via my own code, and it works there too. Thanks, again!

A word of caution if you are using MSVC - there are some values for which your 'loopback', even with the sufficient number of decimal digits, does not work for a third of values (are 1 least significant bit off) in a narrow range, about 0.0001 - 0.0005. MS have declared this a 'feature': http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?Feedbac... In practice this is unlikely to bite you - but during testing it might, if you demand equality BOOST_CHECK_EQUAL rather than using a BOOST_CHECK_CLOSE_FRACTION() with a single epsilon sized tolerance. HTH too. Paul --- Paul A Bristow Prizet Farmhouse, Kendal, Cumbria UK LA8 8AB +44 1539561830 & SMS, Mobile +44 7714 330204 & SMS pbristow@hetp.u-net.com

Well we certainly don't want any philosophical discussions :-)) In BOOST_CHECK we are really comparing two bit patterns - both of which are std::numeric_limits<T>::infinity() and will presumably pass a BOOST_CHECK_EQUAL test, so I also would expect a BOOST_CHECK_CLOSE pass, whatever the tolerance. But most important is to try to agree and to document our conclusion? Paul --- Paul A Bristow Prizet Farmhouse, Kendal, Cumbria UK LA8 8AB +44 1539561830 & SMS, Mobile +44 7714 330204 & SMS pbristow@hetp.u-net.com

"Paul A Bristow" <pbristow@hetp.u-net.com> wrote in message news:001601c7979f$b5c37bb0$0200a8c0@hetp7...
This is not exactly true in general. BOOST_CHECK uses operator==. BOOST_CHECK_CLOSE doesn't compare bit patterns either. It uses some advanced algorithm. So we may decide what is the desirable outcome.
Getting back to our FP we need to decide Does 0. == 0. NaN == NaN Inf == Inf. My guess is that curent algorithm produces false in all three cases. My vote would be to keep it this way. Gennadiy

Agreed (and this is different from BOOST_CHECK_EQUAL, where being different from the IEEE spec and operator== would be confusing). BUT there IS a need to be able to see if you get NaN or infinity as a result - John and I have found this in checking the math toolkit. To do this we must have a Standard way of doing this - Standards writers please note the inordinate confusion caused by the previous lack of a Standard way of doing something very simple (but platform specific), having created a Standard way of creating infinity and NaNs in numeric_limits. Boost should soon have this when Johan Råde 's TR1 code comes up for review - and I trust acceptance. For BOOST_CHECK_CLOSE (and BOOST_CHECK_CLOSE_FRACTION) we (well Gennadiy at least ;-) can decide what the rules are. At present 1.34, my tests attached show that:
0. == 0. passes, whatever the tolerance (as long as it isn't NaN!).
NaN == NaN - fails
Inf == Inf - fails
My vote would be to keep it this way.
OK - but it needs documenting, perhaps with the attached example code. If you need (and our experience is that you do) to consider NaN and infinity then use the TR1 functions: template<class T> bool isfinite(T x); template<class T> bool isinf(T x); template<class T> bool isnan(T x); for example, if you expect a possible infinity result: perhaps from a dividebyzero caused by an underflow. double d = 1.23; double n = 0.; // force a divide by zero, double r = d / n; // expecting an infinity result. // cout << r << endl; // 1.#INF if (isfinite(r)) { BOOST_CHECK_CLOSE(r, 1., 0.000001); } else {// If not finite then we expect the result to be infinity. BOOST_CHECK(isinf(r)); } or mark a fail as expected. And similarly if you expect a NaN result. This extra code is a possible reason why BOOST_CHECK_CLOSE might do something smarter with non-finites. But this code is clear, if a hassle to write. Paul --- Paul A Bristow Prizet Farmhouse, Kendal, Cumbria UK LA8 8AB +44 1539561830 & SMS, Mobile +44 7714 330204 & SMS pbristow@hetp.u-net.com

Hi Jody, This may be a little off-topic but I believe it may help you better understand what these discrepancies are that you are seeing. Assume you have the following fractions: 1/2, 1/4, 1/8, 1/16, 1/32, 1/64, 1/128, 1/256, 1/512, 1/1024, etc... You are then asked to select a group of these fractions such that their sum is 0.1, but you are told that there is a limit on the size of the group, for instance you can't use more than 6 fractions in the group (you can use any amount of them just no more than 6), Now the questions: 1. How close could you get to 0.1? 2. Could you represent 0.1 exactly? 3. From all the combinations that you could come up with, what would be the closest value to 0.1 you could come up with? Arash Partow __________________________________________________ Be one who knows what they don't know, Instead of being one who knows not what they don't know, Thinking they know everything about all things. http://www.partow.net Jody Hagins wrote:

On Wed, 16 May 2007 19:20:50 +1000 Arash Partow <arash@partow.net> wrote:
Thank, Arash. All input is beneficial. Aside from my recent rant about Boost.Test/1.34, I actually really like the features I get from Boost.Test (which is why I have put up with having to change our tests with every release). I just don't understand some of the documentation, so I've avoided those features... until I absolutely need them.
participants (7)
-
Andrew Koenig
-
Arash Partow
-
Gennadiy Rozental
-
Gottlob Frege
-
Jody Hagins
-
John Maddock
-
Paul A Bristow