]> https://gitweb.dealii.org/ - dealii-svn.git/commitdiff
add float reproducibility option
authorkanschat <kanschat@0785d39b-7218-0410-832d-ea1e28bc413d>
Wed, 30 Nov 2011 20:41:10 +0000 (20:41 +0000)
committerkanschat <kanschat@0785d39b-7218-0410-832d-ea1e28bc413d>
Wed, 30 Nov 2011 20:41:10 +0000 (20:41 +0000)
git-svn-id: https://svn.dealii.org/trunk@24782 0785d39b-7218-0410-832d-ea1e28bc413d

deal.II/include/deal.II/base/logstream.h
deal.II/source/base/logstream.cc

index 3f7cff804cc83896f3fe12107d4530c3f90ac299..5023e59d03a82ff2bce0710ed5daa5b339214646 100644 (file)
@@ -54,8 +54,33 @@ DEAL_II_NAMESPACE_OPEN
  * <li> <tt>deallog.pop()</tt> when leaving that stage entered with <tt>push</tt>.
  * </ul>
  *
+ * <h3>LogStream and reproducible regression test output</h3>
+ *
+ * Generating reproducible floating point output for regression tests
+ * is mildly put a nightmare. In order to make life a little easier,
+ * LogStream implements a few features that try to achieve such a
+ * goal. These features are turned on by calling test_mode(), and it
+ * is not recommended to use them in any other environment. Right now,
+ * LogStream implements the following:
+ *
+ * <ol>
+ * <li> A double number very close to zero will end up being output in
+ * exponential format, although it has no significant digits. The
+ * parameter #double_threshold determines which numbers are too close
+ * to zero to be considered nonzero.
+ * <li> For float numbers holds the same, but with a typically larger
+ * #float_threshold.
+ * <li> Rounded numbers become unreliable with inexact
+ * arithmetics. Therefore, adding a small number before rounding makes
+ * results more reproducible, assuming that numbers like 0.5 are more
+ * likely than 0.49997.
+ * </ol>
+ * It should be pointed out that all of these measures distort the
+ * output and make it less accurate. Therefore, they are only
+ * recommended if the output needs to be reproducible.
+ * 
  * @ingroup textoutput
- * @author Guido Kanschat, Wolfgang Bangerth, 1999, 2003
+ * @author Guido Kanschat, Wolfgang Bangerth, 1999, 2003, 2011
  */
 class LogStream
 {
@@ -87,6 +112,26 @@ class LogStream
                                      */
     void detach ();
 
+                                    /**
+                                     * Setup the logstream for
+                                     * regression test mode.
+                                     *
+                                     * This sets the parameters
+                                     * #double_threshold,
+                                     * #float_threshold, and #offset
+                                     * to nonzero values. The exact
+                                     * values being used have been
+                                     * determined experimentally and
+                                     * can be found in the source
+                                     * code.
+                                     *
+                                     * Called with an argument
+                                     * <tt>false</tt>, switches off
+                                     * test mode and sets all
+                                     * involved parameters to zero.
+                                     */
+    void test_mode (bool on=true);
+    
                                     /**
                                      * Gives the default stream (<tt>std_out</tt>).
                                      */
@@ -259,15 +304,29 @@ class LogStream
                                     /**
                                      * Output double precision
                                      * numbers through this
-                                     * stream. This function
-                                     * eliminates floating point
-                                     * numbers smaller than
-                                     * #double_threshold, which can
-                                     * be changed using
-                                     * threshold_double().
+                                     * stream.
+                                     *
+                                     * If they are set, this function
+                                     * applies the methods for making
+                                     * floating point output
+                                     * reproducible as discussed in
+                                     * the introduction.
                                      */
     LogStream & operator << (const double t);
 
+                                    /**
+                                     * Output single precision
+                                     * numbers through this
+                                     * stream.
+                                     *
+                                     * If they are set, this function
+                                     * applies the methods for making
+                                     * floating point output
+                                     * reproducible as discussed in
+                                     * the introduction.
+                                     */
+    LogStream & operator << (const float t);
+
                                     /**
                                      * Treat ostream
                                      * manipulators. This passes on
@@ -383,9 +442,55 @@ class LogStream
 
                                     /**
                                      * Threshold for printing double
-                                     * values.
+                                     * values. Every number with
+                                     * absolute value less than this
+                                     * is printed as zero.
                                      */
     double double_threshold;
+
+                                    /**
+                                     * Threshold for printing float
+                                     * values. Every number with
+                                     * absolute value less than this
+                                     * is printed as zero.
+                                     */
+    float float_threshold;
+
+                                    /**
+                                     * An offset added to every float
+                                     * or double number upon
+                                     * output. This is done after the
+                                     * number is compared to
+                                     * #double_threshold or #float_threshold,
+                                     * but before rounding.
+                                     *
+                                     * This functionality was
+                                     * introduced to produce more
+                                     * reproducable floating point
+                                     * output for regression
+                                     * tests. The rationale is, that
+                                     * an exact output value is much
+                                     * more likely to be 1/8 than
+                                     * 0.124997. If we round to two
+                                     * digits though, 1/8 becomes
+                                     * unreliably either .12 or .13
+                                     * due to machine accuracy. On
+                                     * the other hand, if we add a
+                                     * something above machine
+                                     * accuracy first, we will always
+                                     * get .13.
+                                     *
+                                     * It is safe to leave this
+                                     * value equal to zero. For
+                                     * regression tests, the function
+                                     * test_mode() sets it to a
+                                     * reasonable value.
+                                     *
+                                     * The offset is relative to the
+                                     * magnitude of the number.
+                                     */
+    double offset;
+    
                                     /**
                                      * Flag for printing thread id.
                                      */
@@ -493,14 +598,6 @@ inline
 LogStream &
 LogStream::operator<< (const double t)
 {
-                                  // for doubles, we want to make
-                                  // sure that if a number is smaller
-                                  // than a given threshold, then we
-                                  // simply print zero (the default
-                                  // threshold is zero, but can be
-                                  // changed for automated testsuite
-                                  // runs)
-                                   //
                                    // we have to make sure that we
                                    // don't catch NaN's and +-Inf's
                                    // with the test, because for these
@@ -509,12 +606,38 @@ LogStream::operator<< (const double t)
                                    // both t<=0 and t>=0 are false at
                                    // the same time, which can't be
                                    // said for any other number
-  if ((std::fabs(t) > double_threshold)
-      ||
-      (! (t<=0) && !(t>=0)))
+  if (! (t<=0) && !(t>=0))
+    print (t);
+  else
+    if (std::fabs(t) < double_threshold)
+      print ('0');
+    else
+      print(t*(1.+offset));
+  
+  return *this;
+}
+
+
+
+inline
+LogStream &
+LogStream::operator<< (const float t)
+{
+                                   // we have to make sure that we
+                                   // don't catch NaN's and +-Inf's
+                                   // with the test, because for these
+                                   // denormals all comparisons are
+                                   // always false. thus, for a NaN,
+                                   // both t<=0 and t>=0 are false at
+                                   // the same time, which can't be
+                                   // said for any other number
+  if (! (t<=0) && !(t>=0))
     print (t);
   else
-    print('0');
+    if (std::fabs(t) < float_threshold)
+      print ('0');
+    else
+      print(t*(1.+offset));
   
   return *this;
 }
index 2c8c1839e93071e9cacc4dac111604002cd451aa..e853162167210d4fe7a85ab824d866b7a40a6c17 100644 (file)
@@ -55,7 +55,8 @@ LogStream::LogStream()
                std_out(&std::cerr), file(0),
                std_depth(10000), file_depth(10000),
                print_utime(false), diff_utime(false),
-               last_time (0.), double_threshold(0.), old_cerr(0)
+               last_time (0.), double_threshold(0.), float_threshold(0.),
+               offset(0), old_cerr(0)
 {
   prefixes.push("DEAL:");
   std_out->setf(std::ios::showpoint | std::ios::left);
@@ -107,6 +108,24 @@ LogStream::~LogStream()
 }
 
 
+void
+LogStream::test_mode(bool on)
+{
+  if (on)
+    {
+      double_threshold = 1.e-10;
+      float_threshold = 1.e-7;
+      offset = 1.e-7;
+    }
+  else
+    {
+      double_threshold = 0.;
+      float_threshold = 0.;
+      offset = 0.;
+    }  
+}
+
+
 LogStream &
 LogStream::operator<< (std::ostream& (*p) (std::ostream&))
 {

In the beginning the Universe was created. This has made a lot of people very angry and has been widely regarded as a bad move.

Douglas Adams


Typeset in Trocchi and Trocchi Bold Sans Serif.