summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBaptiste Lepilleur <gaiacrtn@free.fr>2007-02-24 20:13:04 +0000
committerBaptiste Lepilleur <gaiacrtn@free.fr>2007-02-24 20:13:04 +0000
commit0d30a2aec28085cfb9fe359c321c289609b884ca (patch)
treed5848b1cb981a9fa5ba351b5eb01e4a0e06e1cf8
parent3ca9c5d071cb8162c89fd514a6116ee6b450d763 (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--ChangeLog19
-rw-r--r--examples/cppunittest/CppUnitTestMain.dsp8
-rw-r--r--examples/cppunittest/TestAssertTest.cpp37
-rw-r--r--include/cppunit/TestAssert.h9
-rw-r--r--include/cppunit/config/config-evc4.h5
-rw-r--r--include/cppunit/config/config-msvc6.h5
-rw-r--r--include/cppunit/portability/FloatingPoint.h50
-rw-r--r--include/cppunit/portability/Makefile.am11
-rw-r--r--src/cppunit/TestAssert.cpp33
-rw-r--r--src/cppunit/cppunit.dsp4
10 files changed, 156 insertions, 25 deletions
diff --git a/ChangeLog b/ChangeLog
index a9102dc..ad13c46 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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