diff options
author | Baptiste Lepilleur <gaiacrtn@free.fr> | 2007-02-24 20:13:04 +0000 |
---|---|---|
committer | Baptiste Lepilleur <gaiacrtn@free.fr> | 2007-02-24 20:13:04 +0000 |
commit | 0d30a2aec28085cfb9fe359c321c289609b884ca (patch) | |
tree | d5848b1cb981a9fa5ba351b5eb01e4a0e06e1cf8 | |
parent | 3ca9c5d071cb8162c89fd514a6116ee6b450d763 (diff) |
Src/cppunit/TestAssert.
src/cppunit/TestAssert.cpp (assertDoubleEquals): Moved finite & NaN
tests to include/cppunit/portability/FloatingPoint.h. Changed
implementation assertDoubleEquals to explicitly test for NaN
in case of non-finite values to force equality failure in the
presence of NaN. Previous implementation failed on Microsoft
Visual Studio 6 (on this platform: NaN == NaN).
* examples/cppunittest/TestAssertTest.cpp: Add more unit tests to
test the portable floating-point primitive. Added missing
include <limits>.
* include/cppunit/portability/Makefile.am:
* include/cppunit/portability/FloatingPoint.h: Added file. Extracted
isfinite() from TestAssert.cpp.
* include/cppunit/config-evc4:
* include/cppunit/config-msvc6: Added support for _finite().
-rw-r--r-- | ChangeLog | 19 | ||||
-rw-r--r-- | examples/cppunittest/CppUnitTestMain.dsp | 8 | ||||
-rw-r--r-- | examples/cppunittest/TestAssertTest.cpp | 37 | ||||
-rw-r--r-- | include/cppunit/TestAssert.h | 9 | ||||
-rw-r--r-- | include/cppunit/config/config-evc4.h | 5 | ||||
-rw-r--r-- | include/cppunit/config/config-msvc6.h | 5 | ||||
-rw-r--r-- | include/cppunit/portability/FloatingPoint.h | 50 | ||||
-rw-r--r-- | include/cppunit/portability/Makefile.am | 11 | ||||
-rw-r--r-- | src/cppunit/TestAssert.cpp | 33 | ||||
-rw-r--r-- | src/cppunit/cppunit.dsp | 4 |
10 files changed, 156 insertions, 25 deletions
@@ -1,3 +1,22 @@ +2007-02-24 Baptiste Lepilleur <blep@users.sourceforge.net> + + * src/cppunit/TestAssert.cpp (assertDoubleEquals): Moved finite & NaN + tests to include/cppunit/portability/FloatingPoint.h. Changed + implementation assertDoubleEquals to explicitly test for NaN + in case of non-finite values to force equality failure in the + presence of NaN. Previous implementation failed on Microsoft + Visual Studio 6 (on this platform: NaN == NaN). + * examples/cppunittest/TestAssertTest.cpp: Add more unit tests to + test the portable floating-point primitive. Added missing + include <limits>. + + * include/cppunit/portability/Makefile.am: + * include/cppunit/portability/FloatingPoint.h: Added file. Extracted + isfinite() from TestAssert.cpp. + + * include/cppunit/config-evc4: + * include/cppunit/config-msvc6: Added support for _finite(). + 2007-01-30 Steve M. Robbins <smr@sumost.ca> * examples/cppunittest/assertion_traitsTest.h: diff --git a/examples/cppunittest/CppUnitTestMain.dsp b/examples/cppunittest/CppUnitTestMain.dsp index 517fd2e..9b32154 100644 --- a/examples/cppunittest/CppUnitTestMain.dsp +++ b/examples/cppunittest/CppUnitTestMain.dsp @@ -176,6 +176,14 @@ PostBuild_Cmds=$(TargetPath) # PROP Default_Filter "" # Begin Source File +SOURCE=.\assertion_traitsTest.cpp +# End Source File +# Begin Source File + +SOURCE=.\assertion_traitsTest.h +# End Source File +# Begin Source File + SOURCE=.\ExceptionTest.cpp # End Source File # Begin Source File diff --git a/examples/cppunittest/TestAssertTest.cpp b/examples/cppunittest/TestAssertTest.cpp index 3b621ed..e05c54e 100644 --- a/examples/cppunittest/TestAssertTest.cpp +++ b/examples/cppunittest/TestAssertTest.cpp @@ -1,6 +1,8 @@ #include "CoreSuite.h" #include "TestAssertTest.h" +#include <cppunit/portability/FloatingPoint.h> #include <algorithm> +#include <limits> /* Note: @@ -192,16 +194,45 @@ TestAssertTest::testAssertDoubleEqualsPrecision() CPPUNIT_FAIL( "Expected assertion failure" ); } + void TestAssertTest::testAssertDoubleNonFinite() { double inf = std::numeric_limits<double>::infinity(); + double nan = std::numeric_limits<double>::quiet_NaN(); + // test our portable floating-point primitives that detect NaN values + CPPUNIT_ASSERT( CPPUNIT_NS::floatingPointIsUnordered( nan ) ); + CPPUNIT_ASSERT( !CPPUNIT_NS::floatingPointIsUnordered( inf ) ); + CPPUNIT_ASSERT( !CPPUNIT_NS::floatingPointIsUnordered( -inf ) ); + CPPUNIT_ASSERT( !CPPUNIT_NS::floatingPointIsUnordered( 1.0 ) ); + CPPUNIT_ASSERT( !CPPUNIT_NS::floatingPointIsUnordered( 1.5 ) ); + CPPUNIT_ASSERT( !CPPUNIT_NS::floatingPointIsUnordered( 2.0 ) ); + CPPUNIT_ASSERT( !CPPUNIT_NS::floatingPointIsUnordered( 2.5 ) ); + CPPUNIT_ASSERT( !CPPUNIT_NS::floatingPointIsUnordered( 0.0 ) ); + CPPUNIT_ASSERT( !CPPUNIT_NS::floatingPointIsUnordered( -1.0 ) ); + CPPUNIT_ASSERT( !CPPUNIT_NS::floatingPointIsUnordered( -2.0 ) ); + // test our portable floating-point primitives that detect finite values + CPPUNIT_ASSERT( CPPUNIT_NS::floatingPointIsFinite( 0.0 ) ); + CPPUNIT_ASSERT( CPPUNIT_NS::floatingPointIsFinite( 0.5 ) ); + CPPUNIT_ASSERT( CPPUNIT_NS::floatingPointIsFinite( 1.0 ) ); + CPPUNIT_ASSERT( CPPUNIT_NS::floatingPointIsFinite( 1.5 ) ); + CPPUNIT_ASSERT( CPPUNIT_NS::floatingPointIsFinite( 2.0 ) ); + CPPUNIT_ASSERT( CPPUNIT_NS::floatingPointIsFinite( 2.5 ) ); + CPPUNIT_ASSERT( CPPUNIT_NS::floatingPointIsFinite( -1.5 ) ); + CPPUNIT_ASSERT( !CPPUNIT_NS::floatingPointIsFinite( nan ) ); + CPPUNIT_ASSERT( !CPPUNIT_NS::floatingPointIsFinite( inf ) ); + CPPUNIT_ASSERT( !CPPUNIT_NS::floatingPointIsFinite( -inf ) ); + // Infinity tests + CPPUNIT_ASSERT( inf == inf ); + CPPUNIT_ASSERT( -inf == -inf ); + CPPUNIT_ASSERT( -inf != inf ); + CPPUNIT_ASSERT( -inf < inf ); + CPPUNIT_ASSERT( inf > -inf ); CPPUNIT_ASSERT_ASSERTION_FAIL( CPPUNIT_ASSERT_DOUBLES_EQUAL( inf, 0.0, 1.0 ) ); CPPUNIT_ASSERT_ASSERTION_FAIL( CPPUNIT_ASSERT_DOUBLES_EQUAL( 0.0, inf, 1.0 ) ); CPPUNIT_ASSERT_ASSERTION_PASS( CPPUNIT_ASSERT_DOUBLES_EQUAL( inf, inf, 1.0 ) ); - - double nan = std::numeric_limits<double>::quiet_NaN(); - CPPUNIT_ASSERT_ASSERTION_FAIL( CPPUNIT_ASSERT_DOUBLES_EQUAL( nan, 0.0, 1.0 ) ); + // NaN tests + CPPUNIT_ASSERT_ASSERTION_FAIL( CPPUNIT_ASSERT_DOUBLES_EQUAL( nan, 0.0, 1.0 ) ); // this one fails CPPUNIT_ASSERT_ASSERTION_FAIL( CPPUNIT_ASSERT_DOUBLES_EQUAL( nan, nan, 1.0 ) ); CPPUNIT_ASSERT_ASSERTION_FAIL( CPPUNIT_ASSERT_DOUBLES_EQUAL( nan, inf, 1.0 ) ); CPPUNIT_ASSERT_ASSERTION_FAIL( CPPUNIT_ASSERT_DOUBLES_EQUAL( inf, nan, 1.0 ) ); diff --git a/include/cppunit/TestAssert.h b/include/cppunit/TestAssert.h index 07783a2..f74797b 100644 --- a/include/cppunit/TestAssert.h +++ b/include/cppunit/TestAssert.h @@ -109,6 +109,7 @@ void assertEquals( const T& expected, /*! \brief (Implementation) Asserts that two double are equals given a tolerance. * Use CPPUNIT_ASSERT_DOUBLES_EQUAL instead of this function. * \sa Asserter::failNotEqual(). + * \sa CPPUNIT_ASSERT_DOUBLES_EQUAL for detailed semantic of the assertion. */ void CPPUNIT_API assertDoubleEquals( double expected, double actual, @@ -218,6 +219,13 @@ void CPPUNIT_API assertDoubleEquals( double expected, /*! \brief Macro for primitive double value comparisons. * \ingroup Assertions + * + * The assertion pass if both expected and actual are finite and + * \c fabs( \c expected - \c actual ) <= \c delta. + * If either \c expected or actual are infinite (+/- inf), the + * assertion pass if \c expected == \c actual. + * If either \c expected or \c actual is a NaN (not a number), then + * the assertion fails. */ #define CPPUNIT_ASSERT_DOUBLES_EQUAL(expected,actual,delta) \ ( CPPUNIT_NS::assertDoubleEquals( (expected), \ @@ -230,6 +238,7 @@ void CPPUNIT_API assertDoubleEquals( double expected, /*! \brief Macro for primitive double value comparisons, setting a * user-supplied message in case of failure. * \ingroup Assertions + * \sa CPPUNIT_ASSERT_DOUBLES_EQUAL for detailed semantic of the assertion. */ #define CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(message,expected,actual,delta) \ ( CPPUNIT_NS::assertDoubleEquals( (expected), \ diff --git a/include/cppunit/config/config-evc4.h b/include/cppunit/config/config-evc4.h index cb141b0..a791698 100644 --- a/include/cppunit/config/config-evc4.h +++ b/include/cppunit/config/config-evc4.h @@ -60,6 +60,11 @@ # define CPPUNIT_COMPILER_LOCATION_FORMAT "%p(%l):" #endif +/* define to 1 if the compiler has _finite() */ +#ifndef CPPUNIT_HAVE__FINITE +#define CPPUNIT_HAVE__FINITE 1 +#endif + // Uncomment to turn on STL wrapping => use this to test compilation. // This will make CppUnit subclass std::vector & co to provide default // parameter. diff --git a/include/cppunit/config/config-msvc6.h b/include/cppunit/config/config-msvc6.h index a1b0af2..d688171 100644 --- a/include/cppunit/config/config-msvc6.h +++ b/include/cppunit/config/config-msvc6.h @@ -64,6 +64,11 @@ // Define to 1 if the compiler support C++ style cast. #define CPPUNIT_HAVE_CPP_CAST 1 +/* define to 1 if the compiler has _finite() */ +#ifndef CPPUNIT_HAVE__FINITE +#define CPPUNIT_HAVE__FINITE 1 +#endif + // Uncomment to turn on STL wrapping => use this to test compilation. // This will make CppUnit subclass std::vector & co to provide default diff --git a/include/cppunit/portability/FloatingPoint.h b/include/cppunit/portability/FloatingPoint.h new file mode 100644 index 0000000..fa3095e --- /dev/null +++ b/include/cppunit/portability/FloatingPoint.h @@ -0,0 +1,50 @@ +#ifndef CPPUNIT_PORTABILITY_FLOATINGPOINT_H_INCLUDED +#define CPPUNIT_PORTABILITY_FLOATINGPOINT_H_INCLUDED + +#include <math.h> + +CPPUNIT_NS_BEGIN + +/// \brief Tests if a floating-point is a NaN. +// According to IEEE-754 floating point standard, NaN comparison should always +// be 'false'. +// At least Microsoft Visual Studio 6 is known not to implement this test correctly. +// It emits the following code to test equality: +// fcomp qword ptr [nan] +// fnstsw ax // copie fp (floating-point) status register to ax +// test ah,40h // test bit 14 of ax (0x4000) => C3 of fp status register +// According to the following documentation on the x86 floating point status register, +// the C2 bit should be tested to test for NaN value. +// http://webster.cs.ucr.edu/AoA/Windows/HTML/RealArithmetic.html#1000117 +// In Microsoft Visual Studio 2003 & 2005, the test is implemented with: +// test ah,44h // Visual Studio 2005 test both C2 & C3... +// +// To work around this, a NaN is assumed to be detected if no strict ordering is found. +inline bool floatingPointIsUnordered( double x ) +{ + // x != x will detect a NaN on conformant platform + // (2.0 < x && x < 1.0) will detect a NaN on non conformant platform: + // => no ordering can be found for x. + return (x != x) || (2.0 < x && x < 1.0); +} + + +/// \brief Tests if a floating-point is finite. +/// @return \c true if x is neither a NaN, nor +inf, nor -inf, \c false otherwise. +inline bool floatingPointIsFinite( double x ) +{ +#if defined(CPPUNIT_HAVE_ISFINITE) + return (bool)isfinite( x ); +#elif defined(CPPUNIT_HAVE_FINITE) + return (bool)finite( x ); +#elif defined(CPPUNIT_HAVE__FINITE) + return _finite(x); +#else + double testInf = x * 0.0; // Produce 0.0 if x is finite, a NaN otherwise. + return testInf == 0.0 && !floatingPointIsUnordered(testInf); +#endif +} + +CPPUNIT_NS_END + +#endif // CPPUNIT_PORTABILITY_FLOATINGPOINT_H_INCLUDED diff --git a/include/cppunit/portability/Makefile.am b/include/cppunit/portability/Makefile.am index 3c578e3..2caeb32 100644 --- a/include/cppunit/portability/Makefile.am +++ b/include/cppunit/portability/Makefile.am @@ -1,9 +1,10 @@ libcppunitincludedir = $(includedir)/cppunit/portability libcppunitinclude_HEADERS = \ - CppUnitDeque.h \ - CppUnitMap.h \ - CppUnitSet.h \ - CppUnitStack.h \ + CppUnitDeque.h \ + CppUnitMap.h \ + CppUnitSet.h \ + CppUnitStack.h \ CppUnitVector.h \ - Stream.h + FloatingPoint.h \ + Stream.h diff --git a/src/cppunit/TestAssert.cpp b/src/cppunit/TestAssert.cpp index 44be55f..6e4e794 100644 --- a/src/cppunit/TestAssert.cpp +++ b/src/cppunit/TestAssert.cpp @@ -1,19 +1,5 @@ #include <cppunit/TestAssert.h> - -#include <math.h> - -#if !defined(CPPUNIT_HAVE_ISFINITE) - - static inline bool isfinite( double x ) - { -#if defined(CPPUNIT_HAVE_FINITE) - return finite( x ); -#else - return ( x * 0.0 ) == 0.0; -#endif - } - -#endif +#include <cppunit/portability/FloatingPoint.h> CPPUNIT_NS_BEGIN @@ -30,10 +16,23 @@ assertDoubleEquals( double expected, msg.addDetail( AdditionalMessage(message) ); bool equal; - if ( isfinite(expected) && isfinite(actual) ) + if ( floatingPointIsFinite(expected) && floatingPointIsFinite(actual) ) equal = fabs( expected - actual ) <= delta; else - equal = expected == actual; + { + // If expected or actual is not finite, it may be +inf, -inf or NaN (Not a Number). + // Value of +inf or -inf leads to a true equality regardless of delta if both + // expected and actual have the same value (infinity sign). + // NaN Value should always lead to a failed equality. + if ( floatingPointIsUnordered(expected) || floatingPointIsUnordered(actual) ) + { + equal = false; // expected or actual is a NaN + } + else // ordered values, +inf or -inf comparison + { + equal = expected == actual; + } + } Asserter::failNotEqualIf( !equal, assertion_traits<double>::toString(expected), diff --git a/src/cppunit/cppunit.dsp b/src/cppunit/cppunit.dsp index 7dcffe4..55f4769 100644 --- a/src/cppunit/cppunit.dsp +++ b/src/cppunit/cppunit.dsp @@ -247,6 +247,10 @@ SOURCE=..\..\include\cppunit\portability\CppUnitVector.h # End Source File # Begin Source File +SOURCE=..\..\include\cppunit\portability\FloatingPoint.h +# End Source File +# Begin Source File + SOURCE=..\..\include\cppunit\Portability.h # End Source File # Begin Source File |