2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2014,2015,2016, by the GROMACS development team, led by
5 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6 * and including many others, as listed in the AUTHORS file in the
7 * top-level source directory and at http://www.gromacs.org.
9 * GROMACS is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 2.1
12 * of the License, or (at your option) any later version.
14 * GROMACS is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with GROMACS; if not, see
21 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * If you want to redistribute modifications to GROMACS, please
25 * consider that scientific software is very special. Version
26 * control is crucial - bugs must be traceable. We will be happy to
27 * consider code for inclusion in the official distribution, but
28 * derived work must not be called official GROMACS. Details are found
29 * in the README & COPYING files - if they are missing, get the
30 * official version at http://www.gromacs.org.
32 * To help us fund GROMACS development, we humbly ask that you cite
33 * the research papers on the package. Check out http://www.gromacs.org.
37 * Implements floating-point comparison routines from testasserts.h.
39 * \author Teemu Murtola <teemu.murtola@gmail.com>
40 * \ingroup module_testutils
44 #include "testasserts.h"
51 #include <gtest/gtest.h>
53 #include "gromacs/options/basicoptions.h"
54 #include "gromacs/options/ioptionscontainer.h"
55 #include "gromacs/utility/basedefinitions.h"
56 #include "gromacs/utility/exceptions.h"
57 #include "gromacs/utility/stringutil.h"
59 #include "testutils/testoptions.h"
70 //! Whether to print the message from expected exceptions.
71 bool g_showExpectedExceptions
= false;
74 GMX_TEST_OPTIONS(ExceptionOptions
, options
)
76 options
->addOption(BooleanOption("show-error-messages")
77 .store(&g_showExpectedExceptions
)
78 .description("Show error messages from expected "
88 void processExpectedException(const std::exception
&ex
)
90 if (g_showExpectedExceptions
)
92 std::printf("Exception message (from expected exception):\n");
93 formatExceptionMessageToFile(stdout
, ex
);
98 } // namespace internal
103 using ::testing::internal::FloatingPoint
;
105 //! \internal \addtogroup module_testutils
108 /*! \name Helper functions for computing floating-point differences
110 * These routines are used to initialize FloatingPointDifference.
111 * They peek into some internal types from Google Test (gtest-internal.h),
112 * and duplicate some other functionality from there, but that is likely
113 * a better alternative than just copying all that code here.
118 * Computes biased integer representation for a floating-point value.
120 * This moves the integer representation from a sign-and-magnitude
121 * representation to a biased representation where the 0x8000... represents
122 * zero, and the order of the integer values matches the order of the
123 * floating-point values.
125 template <typename FloatType
>
126 typename FloatingPoint
<FloatType
>::Bits
127 floatingPointToBiasedInteger(const FloatingPoint
<FloatType
> &value
)
129 if (value
.sign_bit())
131 return ~value
.bits() + 1;
135 return value
.bits() | FloatingPoint
<FloatType
>::kSignBitMask
;
140 * Computes the magnitude of the difference in ULPs between two numbers,
141 * treating also values of different sign.
143 template <typename FloatType
>
144 gmx_uint64_t
calculateUlpDifference(const FloatingPoint
<FloatType
> &value1
,
145 const FloatingPoint
<FloatType
> &value2
)
147 typename FloatingPoint
<FloatType
>::Bits biased1
148 = floatingPointToBiasedInteger(value1
);
149 typename FloatingPoint
<FloatType
>::Bits biased2
150 = floatingPointToBiasedInteger(value2
);
151 return biased1
> biased2
? biased1
- biased2
: biased2
- biased1
;
155 * Helper to implement the constructors for FloatingPointDifference.
157 template <typename FloatType
>
158 void initDifference(FloatType raw1
, FloatType raw2
, double *absoluteDifference
,
159 gmx_uint64_t
*ulpDifference
, bool *bSignDifference
)
161 FloatingPoint
<FloatType
> value1(raw1
);
162 FloatingPoint
<FloatType
> value2(raw2
);
164 if (value1
.is_nan() || value2
.is_nan())
166 *absoluteDifference
= std::numeric_limits
<double>::quiet_NaN();
167 *bSignDifference
= false;
171 *absoluteDifference
= std::fabs(raw1
- raw2
);
172 *bSignDifference
= (value1
.sign_bit() != value2
.sign_bit());
173 *ulpDifference
= calculateUlpDifference(value1
, value2
);
177 * Converts a relative tolerance into an ULP difference.
179 template <typename FloatType
>
180 gmx_uint64_t
relativeToleranceToUlp(FloatType tolerance
)
182 FloatingPoint
<FloatType
> m(1.0);
183 FloatingPoint
<FloatType
> t(1.0 + tolerance
);
184 return calculateUlpDifference
<FloatType
>(m
, t
);
192 /********************************************************************
193 * FloatingPointDifference
196 FloatingPointDifference::FloatingPointDifference(float ref
, float value
)
197 : termMagnitude_(std::abs(ref
))
199 initDifference(ref
, value
,
200 &absoluteDifference_
, &ulpDifference_
, &bSignDifference_
);
204 FloatingPointDifference::FloatingPointDifference(double ref
, double value
)
205 : termMagnitude_(std::abs(ref
))
207 initDifference(ref
, value
,
208 &absoluteDifference_
, &ulpDifference_
, &bSignDifference_
);
212 bool FloatingPointDifference::isNaN() const
214 return FloatingPoint
<double>(absoluteDifference_
).is_nan();
217 std::string
FloatingPointDifference::toString() const
219 std::string relDiffStr
;
221 if (termMagnitude_
> 0)
223 // If the reference value is finite we calculate the proper quotient
224 relDiffStr
= formatString("%.3g", std::abs(absoluteDifference_
/termMagnitude_
));
226 else if (absoluteDifference_
== 0.0)
228 // If the numbers are identical the quotient is strictly NaN here, but
229 // there no reason to worry when we have a perfect match.
230 relDiffStr
= formatString("%.3g", 0.0);
234 // If the reference value is zero and numbers are non-identical, relative difference is infinite.
235 relDiffStr
= formatString("Inf");
238 return formatString("%g (%" GMX_PRIu64
" %s-prec. ULPs, rel. %s)%s",
239 absoluteDifference_
, ulpDifference_
,
240 isDouble() ? "double" : "single",
242 bSignDifference_
? ", signs differ" : "");
245 /********************************************************************
246 * FloatingPointTolerance
249 bool FloatingPointTolerance::isWithin(
250 const FloatingPointDifference
&difference
) const
252 if (difference
.isNaN())
257 if (bSignMustMatch_
&& difference
.signsDiffer())
262 const double absoluteTolerance
263 = difference
.isDouble() ? doubleAbsoluteTolerance_
: singleAbsoluteTolerance_
;
264 if (difference
.asAbsolute() < absoluteTolerance
)
269 // By using smaller-than-or-equal below, we allow the test to pass if
270 // the numbers are identical, even if the term magnitude is 0, which seems
271 // a reasonable thing to do...
272 const double relativeTolerance
273 = difference
.isDouble() ? doubleRelativeTolerance_
: singleRelativeTolerance_
;
275 if (difference
.asAbsolute() <= relativeTolerance
* difference
.termMagnitude())
280 const gmx_uint64_t ulpTolerance
281 = difference
.isDouble() ? doubleUlpTolerance_
: singleUlpTolerance_
;
282 if (ulpTolerance
< GMX_UINT64_MAX
&& difference
.asUlps() <= ulpTolerance
)
290 std::string
FloatingPointTolerance::toString(const FloatingPointDifference
&difference
) const
293 const double absoluteTolerance
294 = difference
.isDouble() ? doubleAbsoluteTolerance_
: singleAbsoluteTolerance_
;
295 const double relativeTolerance
296 = difference
.isDouble() ? doubleRelativeTolerance_
: singleRelativeTolerance_
;
297 const gmx_uint64_t ulpTolerance
298 = difference
.isDouble() ? doubleUlpTolerance_
: singleUlpTolerance_
;
300 if (absoluteTolerance
> 0.0)
302 result
.append(formatString("abs. %g", absoluteTolerance
));
304 if (relativeTolerance
> 0.0)
310 result
.append(formatString("rel. %.3g", relativeTolerance
));
312 if (ulpTolerance
< GMX_UINT64_MAX
)
318 result
.append(formatString("%" GMX_PRIu64
" ULPs", ulpTolerance
));
326 result
.append("sign must match");
331 // Doxygen does not recognize this as the same function as in the header...
333 FloatingPointTolerance
334 relativeToleranceAsFloatingPoint(double magnitude
, double tolerance
)
336 const double absoluteTolerance
= std::abs(magnitude
) * tolerance
;
337 return FloatingPointTolerance(absoluteTolerance
, absoluteTolerance
,
338 tolerance
, tolerance
,
339 GMX_UINT64_MAX
, GMX_UINT64_MAX
,