123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526 |
- <html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
- <title>Testing</title>
- <link rel="stylesheet" href="../../math.css" type="text/css">
- <meta name="generator" content="DocBook XSL Stylesheets V1.79.1">
- <link rel="home" href="../../index.html" title="Math Toolkit 2.11.0">
- <link rel="up" href="../special_tut.html" title="Tutorial: How to Write a New Special Function">
- <link rel="prev" href="special_tut_impl.html" title="Implementation">
- <link rel="next" href="../relative_error.html" title="Relative Error">
- </head>
- <body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
- <table cellpadding="2" width="100%"><tr>
- <td valign="top"><img alt="Boost C++ Libraries" width="277" height="86" src="../../../../../../boost.png"></td>
- <td align="center"><a href="../../../../../../index.html">Home</a></td>
- <td align="center"><a href="../../../../../../libs/libraries.htm">Libraries</a></td>
- <td align="center"><a href="http://www.boost.org/users/people.html">People</a></td>
- <td align="center"><a href="http://www.boost.org/users/faq.html">FAQ</a></td>
- <td align="center"><a href="../../../../../../more/index.htm">More</a></td>
- </tr></table>
- <hr>
- <div class="spirit-nav">
- <a accesskey="p" href="special_tut_impl.html"><img src="../../../../../../doc/src/images/prev.png" alt="Prev"></a><a accesskey="u" href="../special_tut.html"><img src="../../../../../../doc/src/images/up.png" alt="Up"></a><a accesskey="h" href="../../index.html"><img src="../../../../../../doc/src/images/home.png" alt="Home"></a><a accesskey="n" href="../relative_error.html"><img src="../../../../../../doc/src/images/next.png" alt="Next"></a>
- </div>
- <div class="section">
- <div class="titlepage"><div><div><h3 class="title">
- <a name="math_toolkit.special_tut.special_tut_test"></a><a class="link" href="special_tut_test.html" title="Testing">Testing</a>
- </h3></div></div></div>
- <p>
- We work under the assumption that untested code doesn't work, so some tests
- for your new special function are in order, we'll divide these up in to 3
- main categories:
- </p>
- <h5>
- <a name="math_toolkit.special_tut.special_tut_test.h0"></a>
- <span class="phrase"><a name="math_toolkit.special_tut.special_tut_test.spot_tests"></a></span><a class="link" href="special_tut_test.html#math_toolkit.special_tut.special_tut_test.spot_tests">Spot Tests</a>
- </h5>
- <p>
- Spot tests consist of checking that the expected exception is generated when
- the inputs are in error (or otherwise generate undefined values), and checking
- any special values. We can check for expected exceptions with <code class="computeroutput"><span class="identifier">BOOST_CHECK_THROW</span></code>, so for example if it's
- a domain error for the last parameter to be outside the range <code class="computeroutput"><span class="special">[</span><span class="number">0</span><span class="special">,</span><span class="number">1</span><span class="special">]</span></code> then we
- might have:
- </p>
- <pre class="programlisting"><span class="identifier">BOOST_CHECK_THROW</span><span class="special">(</span><span class="identifier">my_special</span><span class="special">(</span><span class="number">0</span><span class="special">,</span> <span class="special">-</span><span class="number">0.1</span><span class="special">),</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">domain_error</span><span class="special">);</span>
- <span class="identifier">BOOST_CHECK_THROW</span><span class="special">(</span><span class="identifier">my_special</span><span class="special">(</span><span class="number">0</span><span class="special">,</span> <span class="number">1.1</span><span class="special">),</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">domain_error</span><span class="special">);</span>
- </pre>
- <p>
- When the function has known exact values (typically integer values) we can
- use <code class="computeroutput"><span class="identifier">BOOST_CHECK_EQUAL</span></code>:
- </p>
- <pre class="programlisting"><span class="identifier">BOOST_CHECK_EQUAL</span><span class="special">(</span><span class="identifier">my_special</span><span class="special">(</span><span class="number">1.0</span><span class="special">,</span> <span class="number">0.0</span><span class="special">),</span> <span class="number">0</span><span class="special">);</span>
- <span class="identifier">BOOST_CHECK_EQUAL</span><span class="special">(</span><span class="identifier">my_special</span><span class="special">(</span><span class="number">1.0</span><span class="special">,</span> <span class="number">1.0</span><span class="special">),</span> <span class="number">1</span><span class="special">);</span>
- </pre>
- <p>
- When the function has known values which are not exact (from a floating point
- perspective) then we can use <code class="computeroutput"><span class="identifier">BOOST_CHECK_CLOSE_FRACTION</span></code>:
- </p>
- <pre class="programlisting"><span class="comment">// Assumes 4 epsilon is as close as we can get to a true value of 2Pi:</span>
- <span class="identifier">BOOST_CHECK_CLOSE_FRACTION</span><span class="special">(</span><span class="identifier">my_special</span><span class="special">(</span><span class="number">0.5</span><span class="special">,</span> <span class="number">0.5</span><span class="special">),</span> <span class="number">2</span> <span class="special">*</span> <span class="identifier">constants</span><span class="special">::</span><span class="identifier">pi</span><span class="special"><</span><span class="keyword">double</span><span class="special">>(),</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">numeric_limits</span><span class="special"><</span><span class="keyword">double</span><span class="special">>::</span><span class="identifier">epsilon</span><span class="special">()</span> <span class="special">*</span> <span class="number">4</span><span class="special">);</span>
- </pre>
- <h5>
- <a name="math_toolkit.special_tut.special_tut_test.h1"></a>
- <span class="phrase"><a name="math_toolkit.special_tut.special_tut_test.independent_test_values"></a></span><a class="link" href="special_tut_test.html#math_toolkit.special_tut.special_tut_test.independent_test_values">Independent
- Test Values</a>
- </h5>
- <p>
- If the function is implemented by some other known good source (for example
- Mathematica or it's online versions <a href="http://functions.wolfram.com" target="_top">functions.wolfram.com</a>
- or <a href="http://www.wolframalpha.com" target="_top">www.wolframalpha.com</a>
- then it's a good idea to sanity check our implementation by having at least
- one independendly generated value for each code branch our implementation
- may take. To slot these in nicely with our testing framework it's best to
- tabulate these like this:
- </p>
- <pre class="programlisting"><span class="comment">// function values calculated on http://functions.wolfram.com/</span>
- <span class="keyword">static</span> <span class="keyword">const</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">array</span><span class="special"><</span><span class="identifier">boost</span><span class="special">::</span><span class="identifier">array</span><span class="special"><</span><span class="identifier">T</span><span class="special">,</span> <span class="number">3</span><span class="special">>,</span> <span class="number">10</span><span class="special">></span> <span class="identifier">my_special_data</span> <span class="special">=</span> <span class="special">{{</span>
- <span class="special">{{</span> <span class="identifier">SC_</span><span class="special">(</span><span class="number">0</span><span class="special">),</span> <span class="identifier">SC_</span><span class="special">(</span><span class="number">0</span><span class="special">),</span> <span class="identifier">SC_</span><span class="special">(</span><span class="number">1</span><span class="special">)</span> <span class="special">}},</span>
- <span class="special">{{</span> <span class="identifier">SC_</span><span class="special">(</span><span class="number">0</span><span class="special">),</span> <span class="identifier">SC_</span><span class="special">(</span><span class="number">1</span><span class="special">),</span> <span class="identifier">SC_</span><span class="special">(</span><span class="number">1.26606587775200833559824462521471753760767031135496220680814</span><span class="special">)</span> <span class="special">}},</span>
- <span class="comment">/* More values here... */</span>
- <span class="special">}};</span>
- </pre>
- <p>
- We'll see how to use this table and the meaning of the <code class="computeroutput"><span class="identifier">SC_</span></code>
- macro later. One important point is to make sure that the input values have
- exact binary representations: so choose values such as 1.5, 1.25, 1.125 etc.
- This ensures that if <code class="computeroutput"><span class="identifier">my_special</span></code>
- is unusually sensitive in one area, that we don't get apparently large errors
- just because the inputs are 0.5 ulp in error.
- </p>
- <h5>
- <a name="math_toolkit.special_tut.special_tut_test.h2"></a>
- <span class="phrase"><a name="math_toolkit.special_tut.special_tut_test.random_test_values"></a></span><a class="link" href="special_tut_test.html#math_toolkit.special_tut.special_tut_test.random_test_values">Random
- Test Values</a>
- </h5>
- <p>
- We can generate a large number of test values to check both for future regressions,
- and for accumulated rounding or cancellation error in our implementation.
- Ideally we would use an independent implementation for this (for example
- my_special may be defined in directly terms of other special functions but
- not implemented that way for performance or accuracy reasons). Alternatively
- we may use our own implementation directly, but with any special cases (asymptotic
- expansions etc) disabled. We have a set of <a class="link" href="../internals/test_data.html" title="Graphing, Profiling, and Generating Test Data for Special Functions">tools</a>
- to generate test data directly, here's a typical example:
- </p>
- <pre class="programlisting"><span class="preprocessor">#include</span> <span class="special"><</span><span class="identifier">boost</span><span class="special">/</span><span class="identifier">multiprecision</span><span class="special">/</span><span class="identifier">cpp_dec_float</span><span class="special">.</span><span class="identifier">hpp</span><span class="special">></span>
- <span class="preprocessor">#include</span> <span class="special"><</span><span class="identifier">boost</span><span class="special">/</span><span class="identifier">math</span><span class="special">/</span><span class="identifier">tools</span><span class="special">/</span><span class="identifier">test_data</span><span class="special">.</span><span class="identifier">hpp</span><span class="special">></span>
- <span class="preprocessor">#include</span> <span class="special"><</span><span class="identifier">boost</span><span class="special">/</span><span class="identifier">test</span><span class="special">/</span><span class="identifier">included</span><span class="special">/</span><span class="identifier">prg_exec_monitor</span><span class="special">.</span><span class="identifier">hpp</span><span class="special">></span>
- <span class="preprocessor">#include</span> <span class="special"><</span><span class="identifier">fstream</span><span class="special">></span>
- <span class="keyword">using</span> <span class="keyword">namespace</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">math</span><span class="special">::</span><span class="identifier">tools</span><span class="special">;</span>
- <span class="keyword">using</span> <span class="keyword">namespace</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">math</span><span class="special">;</span>
- <span class="keyword">using</span> <span class="keyword">namespace</span> <span class="identifier">std</span><span class="special">;</span>
- <span class="keyword">using</span> <span class="keyword">namespace</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">multiprecision</span><span class="special">;</span>
- <span class="keyword">template</span> <span class="special"><</span><span class="keyword">class</span> <span class="identifier">T</span><span class="special">></span>
- <span class="identifier">T</span> <span class="identifier">my_special</span><span class="special">(</span><span class="identifier">T</span> <span class="identifier">a</span><span class="special">,</span> <span class="identifier">T</span> <span class="identifier">b</span><span class="special">)</span>
- <span class="special">{</span>
- <span class="comment">// Implementation of my_special here...</span>
- <span class="keyword">return</span> <span class="identifier">a</span> <span class="special">+</span> <span class="identifier">b</span><span class="special">;</span>
- <span class="special">}</span>
- <span class="keyword">int</span> <span class="identifier">cpp_main</span><span class="special">(</span><span class="keyword">int</span> <span class="identifier">argc</span><span class="special">,</span> <span class="keyword">char</span><span class="special">*</span><span class="identifier">argv</span> <span class="special">[])</span>
- <span class="special">{</span>
- <span class="comment">//</span>
- <span class="comment">// We'll use so many digits of precision that any</span>
- <span class="comment">// calculation errors will still leave us with</span>
- <span class="comment">// 40-50 good digits. We'll only run this program</span>
- <span class="comment">// once so it doesn't matter too much how long this takes!</span>
- <span class="comment">//</span>
- <span class="keyword">typedef</span> <span class="identifier">number</span><span class="special"><</span><span class="identifier">cpp_dec_float</span><span class="special"><</span><span class="number">500</span><span class="special">></span> <span class="special">></span> <span class="identifier">bignum</span><span class="special">;</span>
- <span class="identifier">parameter_info</span><span class="special"><</span><span class="identifier">bignum</span><span class="special">></span> <span class="identifier">arg1</span><span class="special">,</span> <span class="identifier">arg2</span><span class="special">;</span>
- <span class="identifier">test_data</span><span class="special"><</span><span class="identifier">bignum</span><span class="special">></span> <span class="identifier">data</span><span class="special">;</span>
- <span class="keyword">bool</span> <span class="identifier">cont</span><span class="special">;</span>
- <span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span> <span class="identifier">line</span><span class="special">;</span>
- <span class="keyword">if</span><span class="special">(</span><span class="identifier">argc</span> <span class="special"><</span> <span class="number">1</span><span class="special">)</span>
- <span class="keyword">return</span> <span class="number">1</span><span class="special">;</span>
- <span class="keyword">do</span><span class="special">{</span>
- <span class="comment">//</span>
- <span class="comment">// User interface which prompts for </span>
- <span class="comment">// range of input parameters:</span>
- <span class="comment">//</span>
- <span class="keyword">if</span><span class="special">(</span><span class="number">0</span> <span class="special">==</span> <span class="identifier">get_user_parameter_info</span><span class="special">(</span><span class="identifier">arg1</span><span class="special">,</span> <span class="string">"a"</span><span class="special">))</span>
- <span class="keyword">return</span> <span class="number">1</span><span class="special">;</span>
- <span class="keyword">if</span><span class="special">(</span><span class="number">0</span> <span class="special">==</span> <span class="identifier">get_user_parameter_info</span><span class="special">(</span><span class="identifier">arg2</span><span class="special">,</span> <span class="string">"b"</span><span class="special">))</span>
- <span class="keyword">return</span> <span class="number">1</span><span class="special">;</span>
- <span class="comment">//</span>
- <span class="comment">// Get a pointer to the function and call</span>
- <span class="comment">// test_data::insert to actually generate</span>
- <span class="comment">// the values.</span>
- <span class="comment">//</span>
- <span class="identifier">bignum</span> <span class="special">(*</span><span class="identifier">fp</span><span class="special">)(</span><span class="identifier">bignum</span><span class="special">,</span> <span class="identifier">bignum</span><span class="special">)</span> <span class="special">=</span> <span class="special">&</span><span class="identifier">my_special</span><span class="special">;</span>
- <span class="identifier">data</span><span class="special">.</span><span class="identifier">insert</span><span class="special">(</span><span class="identifier">fp</span><span class="special">,</span> <span class="identifier">arg2</span><span class="special">,</span> <span class="identifier">arg1</span><span class="special">);</span>
- <span class="identifier">std</span><span class="special">::</span><span class="identifier">cout</span> <span class="special"><<</span> <span class="string">"Any more data [y/n]?"</span><span class="special">;</span>
- <span class="identifier">std</span><span class="special">::</span><span class="identifier">getline</span><span class="special">(</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">cin</span><span class="special">,</span> <span class="identifier">line</span><span class="special">);</span>
- <span class="identifier">boost</span><span class="special">::</span><span class="identifier">algorithm</span><span class="special">::</span><span class="identifier">trim</span><span class="special">(</span><span class="identifier">line</span><span class="special">);</span>
- <span class="identifier">cont</span> <span class="special">=</span> <span class="special">(</span><span class="identifier">line</span> <span class="special">==</span> <span class="string">"y"</span><span class="special">);</span>
- <span class="special">}</span><span class="keyword">while</span><span class="special">(</span><span class="identifier">cont</span><span class="special">);</span>
- <span class="comment">//</span>
- <span class="comment">// Just need to write the results to a file:</span>
- <span class="comment">//</span>
- <span class="identifier">std</span><span class="special">::</span><span class="identifier">cout</span> <span class="special"><<</span> <span class="string">"Enter name of test data file [default=my_special.ipp]"</span><span class="special">;</span>
- <span class="identifier">std</span><span class="special">::</span><span class="identifier">getline</span><span class="special">(</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">cin</span><span class="special">,</span> <span class="identifier">line</span><span class="special">);</span>
- <span class="identifier">boost</span><span class="special">::</span><span class="identifier">algorithm</span><span class="special">::</span><span class="identifier">trim</span><span class="special">(</span><span class="identifier">line</span><span class="special">);</span>
- <span class="keyword">if</span><span class="special">(</span><span class="identifier">line</span> <span class="special">==</span> <span class="string">""</span><span class="special">)</span>
- <span class="identifier">line</span> <span class="special">=</span> <span class="string">"my_special.ipp"</span><span class="special">;</span>
- <span class="identifier">std</span><span class="special">::</span><span class="identifier">ofstream</span> <span class="identifier">ofs</span><span class="special">(</span><span class="identifier">line</span><span class="special">.</span><span class="identifier">c_str</span><span class="special">());</span>
- <span class="identifier">line</span><span class="special">.</span><span class="identifier">erase</span><span class="special">(</span><span class="identifier">line</span><span class="special">.</span><span class="identifier">find</span><span class="special">(</span><span class="char">'.'</span><span class="special">));</span>
- <span class="identifier">ofs</span> <span class="special"><<</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">scientific</span> <span class="special"><<</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">setprecision</span><span class="special">(</span><span class="number">50</span><span class="special">);</span>
- <span class="identifier">write_code</span><span class="special">(</span><span class="identifier">ofs</span><span class="special">,</span> <span class="identifier">data</span><span class="special">,</span> <span class="identifier">line</span><span class="special">.</span><span class="identifier">c_str</span><span class="special">());</span>
- <span class="keyword">return</span> <span class="number">0</span><span class="special">;</span>
- <span class="special">}</span>
- </pre>
- <p>
- Typically several sets of data will be generated this way, including random
- values in some "normal" range, extreme values (very large or very
- small), and values close to any "interesting" behaviour of the
- function (singularities etc).
- </p>
- <h5>
- <a name="math_toolkit.special_tut.special_tut_test.h3"></a>
- <span class="phrase"><a name="math_toolkit.special_tut.special_tut_test.the_test_file_header"></a></span><a class="link" href="special_tut_test.html#math_toolkit.special_tut.special_tut_test.the_test_file_header">The
- Test File Header</a>
- </h5>
- <p>
- We split the actual test file into 2 distinct parts: a header that contains
- the testing code as a series of function templates, and the actual .cpp test
- driver that decides which types are tested, and sets the "expected"
- error rates for those types. It's done this way because:
- </p>
- <div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; ">
- <li class="listitem">
- We want to test with both built in floating point types, and with multiprecision
- types. However, both compile and runtimes with the latter can be too
- long for the folks who run the tests to realistically cope with, so it
- makes sense to split the test into (at least) 2 parts.
- </li>
- <li class="listitem">
- The definition of the SC_ macro used in our tables of data may differ
- depending on what type we're testing (see below). Again this is largely
- a matter of managing compile times as large tables of user-defined-types
- can take a crazy amount of time to compile with some compilers.
- </li>
- </ul></div>
- <p>
- The test header contains 2 functions:
- </p>
- <pre class="programlisting"><span class="keyword">template</span> <span class="special"><</span><span class="keyword">class</span> <span class="identifier">Real</span><span class="special">,</span> <span class="keyword">class</span> <span class="identifier">T</span><span class="special">></span>
- <span class="keyword">void</span> <span class="identifier">do_test</span><span class="special">(</span><span class="keyword">const</span> <span class="identifier">T</span><span class="special">&</span> <span class="identifier">data</span><span class="special">,</span> <span class="keyword">const</span> <span class="keyword">char</span><span class="special">*</span> <span class="identifier">type_name</span><span class="special">,</span> <span class="keyword">const</span> <span class="keyword">char</span><span class="special">*</span> <span class="identifier">test_name</span><span class="special">);</span>
- <span class="keyword">template</span> <span class="special"><</span><span class="keyword">class</span> <span class="identifier">T</span><span class="special">></span>
- <span class="keyword">void</span> <span class="identifier">test</span><span class="special">(</span><span class="identifier">T</span><span class="special">,</span> <span class="keyword">const</span> <span class="keyword">char</span><span class="special">*</span> <span class="identifier">type_name</span><span class="special">);</span>
- </pre>
- <p>
- Before implementing those, we'll include the headers we'll need, and provide
- a default definition for the SC_ macro:
- </p>
- <pre class="programlisting"><span class="comment">// A couple of Boost.Test headers in case we need any BOOST_CHECK_* macros:</span>
- <span class="preprocessor">#include</span> <span class="special"><</span><span class="identifier">boost</span><span class="special">/</span><span class="identifier">test</span><span class="special">/</span><span class="identifier">unit_test</span><span class="special">.</span><span class="identifier">hpp</span><span class="special">></span>
- <span class="preprocessor">#include</span> <span class="special"><</span><span class="identifier">boost</span><span class="special">/</span><span class="identifier">test</span><span class="special">/</span><span class="identifier">tools</span><span class="special">/</span><span class="identifier">floating_point_comparison</span><span class="special">.</span><span class="identifier">hpp</span><span class="special">></span>
- <span class="comment">// Our function to test:</span>
- <span class="preprocessor">#include</span> <span class="special"><</span><span class="identifier">boost</span><span class="special">/</span><span class="identifier">math</span><span class="special">/</span><span class="identifier">special_functions</span><span class="special">/</span><span class="identifier">my_special</span><span class="special">.</span><span class="identifier">hpp</span><span class="special">></span>
- <span class="comment">// We need boost::array for our test data, plus a few headers from</span>
- <span class="comment">// libs/math/test that contain our testing machinary:</span>
- <span class="preprocessor">#include</span> <span class="special"><</span><span class="identifier">boost</span><span class="special">/</span><span class="identifier">array</span><span class="special">.</span><span class="identifier">hpp</span><span class="special">></span>
- <span class="preprocessor">#include</span> <span class="string">"functor.hpp"</span>
- <span class="preprocessor">#include</span> <span class="string">"handle_test_result.hpp"</span>
- <span class="preprocessor">#include</span> <span class="string">"table_type.hpp"</span>
- <span class="preprocessor">#ifndef</span> <span class="identifier">SC_</span>
- <span class="preprocessor">#define</span> <span class="identifier">SC_</span><span class="special">(</span><span class="identifier">x</span><span class="special">)</span> <span class="keyword">static_cast</span><span class="special"><</span><span class="keyword">typename</span> <span class="identifier">table_type</span><span class="special"><</span><span class="identifier">T</span><span class="special">>::</span><span class="identifier">type</span><span class="special">>(</span><span class="identifier">BOOST_JOIN</span><span class="special">(</span><span class="identifier">x</span><span class="special">,</span> <span class="identifier">L</span><span class="special">))</span>
- <span class="preprocessor">#endif</span>
- </pre>
- <p>
- The easiest function to implement is the "test" function which
- is what we'll be calling from the test-driver program. It simply includes
- the files containing the tabular test data and calls <code class="computeroutput"><span class="identifier">do_test</span></code>
- function for each table, along with a description of what's being tested:
- </p>
- <pre class="programlisting"><span class="keyword">template</span> <span class="special"><</span><span class="keyword">class</span> <span class="identifier">T</span><span class="special">></span>
- <span class="keyword">void</span> <span class="identifier">test</span><span class="special">(</span><span class="identifier">T</span><span class="special">,</span> <span class="keyword">const</span> <span class="keyword">char</span><span class="special">*</span> <span class="identifier">type_name</span><span class="special">)</span>
- <span class="special">{</span>
- <span class="comment">//</span>
- <span class="comment">// The actual test data is rather verbose, so it's in a separate file</span>
- <span class="comment">//</span>
- <span class="comment">// The contents are as follows, each row of data contains</span>
- <span class="comment">// three items, input value a, input value b and my_special(a, b):</span>
- <span class="comment">//</span>
- <span class="preprocessor"># include</span> <span class="string">"my_special_1.ipp"</span>
- <span class="identifier">do_test</span><span class="special"><</span><span class="identifier">T</span><span class="special">>(</span><span class="identifier">my_special_1</span><span class="special">,</span> <span class="identifier">name</span><span class="special">,</span> <span class="string">"MySpecial Function: Mathematica Values"</span><span class="special">);</span>
- <span class="preprocessor"># include</span> <span class="string">"my_special_2.ipp"</span>
- <span class="identifier">do_test</span><span class="special"><</span><span class="identifier">T</span><span class="special">>(</span><span class="identifier">my_special_2</span><span class="special">,</span> <span class="identifier">name</span><span class="special">,</span> <span class="string">"MySpecial Function: Random Values"</span><span class="special">);</span>
- <span class="preprocessor"># include</span> <span class="string">"my_special_3.ipp"</span>
- <span class="identifier">do_test</span><span class="special"><</span><span class="identifier">T</span><span class="special">>(</span><span class="identifier">my_special_3</span><span class="special">,</span> <span class="identifier">name</span><span class="special">,</span> <span class="string">"MySpecial Function: Very Small Values"</span><span class="special">);</span>
- <span class="special">}</span>
- </pre>
- <p>
- The function <code class="computeroutput"><span class="identifier">do_test</span></code> takes
- each table of data and calculates values for each row of data, along with
- statistics for max and mean error etc, most of this is handled by some boilerplate
- code:
- </p>
- <pre class="programlisting"><span class="keyword">template</span> <span class="special"><</span><span class="keyword">class</span> <span class="identifier">Real</span><span class="special">,</span> <span class="keyword">class</span> <span class="identifier">T</span><span class="special">></span>
- <span class="keyword">void</span> <span class="identifier">do_test</span><span class="special">(</span><span class="keyword">const</span> <span class="identifier">T</span><span class="special">&</span> <span class="identifier">data</span><span class="special">,</span> <span class="keyword">const</span> <span class="keyword">char</span><span class="special">*</span> <span class="identifier">type_name</span><span class="special">,</span> <span class="keyword">const</span> <span class="keyword">char</span><span class="special">*</span> <span class="identifier">test_name</span><span class="special">)</span>
- <span class="special">{</span>
- <span class="comment">// Get the type of each row and each element in the rows:</span>
- <span class="keyword">typedef</span> <span class="keyword">typename</span> <span class="identifier">T</span><span class="special">::</span><span class="identifier">value_type</span> <span class="identifier">row_type</span><span class="special">;</span>
- <span class="keyword">typedef</span> <span class="identifier">Real</span> <span class="identifier">value_type</span><span class="special">;</span>
- <span class="comment">// Get a pointer to our function, we have to use a workaround here</span>
- <span class="comment">// as some compilers require the template types to be explicitly</span>
- <span class="comment">// specified, while others don't much like it if it is!</span>
- <span class="keyword">typedef</span> <span class="identifier">value_type</span> <span class="special">(*</span><span class="identifier">pg</span><span class="special">)(</span><span class="identifier">value_type</span><span class="special">,</span> <span class="identifier">value_type</span><span class="special">);</span>
- <span class="preprocessor">#if</span> <span class="identifier">defined</span><span class="special">(</span><span class="identifier">BOOST_MATH_NO_DEDUCED_FUNCTION_POINTERS</span><span class="special">)</span>
- <span class="identifier">pg</span> <span class="identifier">funcp</span> <span class="special">=</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">math</span><span class="special">::</span><span class="identifier">my_special</span><span class="special"><</span><span class="identifier">value_type</span><span class="special">,</span> <span class="identifier">value_type</span><span class="special">>;</span>
- <span class="preprocessor">#else</span>
- <span class="identifier">pg</span> <span class="identifier">funcp</span> <span class="special">=</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">math</span><span class="special">::</span><span class="identifier">my_special</span><span class="special">;</span>
- <span class="preprocessor">#endif</span>
- <span class="comment">// Somewhere to hold our results:</span>
- <span class="identifier">boost</span><span class="special">::</span><span class="identifier">math</span><span class="special">::</span><span class="identifier">tools</span><span class="special">::</span><span class="identifier">test_result</span><span class="special"><</span><span class="identifier">value_type</span><span class="special">></span> <span class="identifier">result</span><span class="special">;</span>
- <span class="comment">// And some pretty printing:</span>
- <span class="identifier">std</span><span class="special">::</span><span class="identifier">cout</span> <span class="special"><<</span> <span class="string">"Testing "</span> <span class="special"><<</span> <span class="identifier">test_name</span> <span class="special"><<</span> <span class="string">" with type "</span> <span class="special"><<</span> <span class="identifier">type_name</span>
- <span class="special"><<</span> <span class="string">"\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"</span><span class="special">;</span>
- <span class="comment">//</span>
- <span class="comment">// Test my_special against data:</span>
- <span class="comment">//</span>
- <span class="identifier">result</span> <span class="special">=</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">math</span><span class="special">::</span><span class="identifier">tools</span><span class="special">::</span><span class="identifier">test_hetero</span><span class="special"><</span><span class="identifier">Real</span><span class="special">>(</span>
- <span class="comment">/* First argument is the table */</span>
- <span class="identifier">data</span><span class="special">,</span>
- <span class="comment">/* Next comes our function pointer, plus the indexes of it's arguments in the table */</span>
- <span class="identifier">bind_func</span><span class="special"><</span><span class="identifier">Real</span><span class="special">>(</span><span class="identifier">funcp</span><span class="special">,</span> <span class="number">0</span><span class="special">,</span> <span class="number">1</span><span class="special">),</span>
- <span class="comment">/* Then the index of the result in the table - potentially we can test several
- related functions this way, each having the same input arguments, and different
- output values in different indexes in the table */</span>
- <span class="identifier">extract_result</span><span class="special"><</span><span class="identifier">Real</span><span class="special">>(</span><span class="number">2</span><span class="special">));</span>
- <span class="comment">//</span>
- <span class="comment">// Finish off with some boilerplate to check the results were within the expected errors,</span>
- <span class="comment">// and pretty print the results:</span>
- <span class="comment">//</span>
- <span class="identifier">handle_test_result</span><span class="special">(</span><span class="identifier">result</span><span class="special">,</span> <span class="identifier">data</span><span class="special">[</span><span class="identifier">result</span><span class="special">.</span><span class="identifier">worst</span><span class="special">()],</span> <span class="identifier">result</span><span class="special">.</span><span class="identifier">worst</span><span class="special">(),</span> <span class="identifier">type_name</span><span class="special">,</span> <span class="string">"boost::math::my_special"</span><span class="special">,</span> <span class="identifier">test_name</span><span class="special">);</span>
- <span class="special">}</span>
- </pre>
- <p>
- Now we just need to write the test driver program, at it's most basic it
- looks something like this:
- </p>
- <pre class="programlisting"><span class="preprocessor">#include</span> <span class="special"><</span><span class="identifier">boost</span><span class="special">/</span><span class="identifier">math</span><span class="special">/</span><span class="identifier">special_functions</span><span class="special">/</span><span class="identifier">math_fwd</span><span class="special">.</span><span class="identifier">hpp</span><span class="special">></span>
- <span class="preprocessor">#include</span> <span class="special"><</span><span class="identifier">boost</span><span class="special">/</span><span class="identifier">math</span><span class="special">/</span><span class="identifier">tools</span><span class="special">/</span><span class="identifier">test</span><span class="special">.</span><span class="identifier">hpp</span><span class="special">></span>
- <span class="preprocessor">#include</span> <span class="special"><</span><span class="identifier">boost</span><span class="special">/</span><span class="identifier">math</span><span class="special">/</span><span class="identifier">tools</span><span class="special">/</span><span class="identifier">stats</span><span class="special">.</span><span class="identifier">hpp</span><span class="special">></span>
- <span class="preprocessor">#include</span> <span class="special"><</span><span class="identifier">boost</span><span class="special">/</span><span class="identifier">type_traits</span><span class="special">.</span><span class="identifier">hpp</span><span class="special">></span>
- <span class="preprocessor">#include</span> <span class="special"><</span><span class="identifier">boost</span><span class="special">/</span><span class="identifier">array</span><span class="special">.</span><span class="identifier">hpp</span><span class="special">></span>
- <span class="preprocessor">#include</span> <span class="string">"functor.hpp"</span>
- <span class="preprocessor">#include</span> <span class="string">"handle_test_result.hpp"</span>
- <span class="preprocessor">#include</span> <span class="string">"test_my_special.hpp"</span>
- <span class="identifier">BOOST_AUTO_TEST_CASE</span><span class="special">(</span> <span class="identifier">test_main</span> <span class="special">)</span>
- <span class="special">{</span>
- <span class="comment">//</span>
- <span class="comment">// Test each floating point type, plus real_concept.</span>
- <span class="comment">// We specify the name of each type by hand as typeid(T).name()</span>
- <span class="comment">// often gives an unreadable mangled name.</span>
- <span class="comment">//</span>
- <span class="identifier">test</span><span class="special">(</span><span class="number">0.1F</span><span class="special">,</span> <span class="string">"float"</span><span class="special">);</span>
- <span class="identifier">test</span><span class="special">(</span><span class="number">0.1</span><span class="special">,</span> <span class="string">"double"</span><span class="special">);</span>
- <span class="comment">//</span>
- <span class="comment">// Testing of long double and real_concept is protected</span>
- <span class="comment">// by some logic to disable these for unsupported</span>
- <span class="comment">// or problem compilers.</span>
- <span class="comment">//</span>
- <span class="preprocessor">#ifndef</span> <span class="identifier">BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS</span>
- <span class="identifier">test</span><span class="special">(</span><span class="number">0.1L</span><span class="special">,</span> <span class="string">"long double"</span><span class="special">);</span>
- <span class="preprocessor">#ifndef</span> <span class="identifier">BOOST_MATH_NO_REAL_CONCEPT_TESTS</span>
- <span class="preprocessor">#if</span> <span class="special">!</span><span class="identifier">BOOST_WORKAROUND</span><span class="special">(</span><span class="identifier">__BORLANDC__</span><span class="special">,</span> <span class="identifier">BOOST_TESTED_AT</span><span class="special">(</span><span class="number">0x582</span><span class="special">))</span>
- <span class="identifier">test</span><span class="special">(</span><span class="identifier">boost</span><span class="special">::</span><span class="identifier">math</span><span class="special">::</span><span class="identifier">concepts</span><span class="special">::</span><span class="identifier">real_concept</span><span class="special">(</span><span class="number">0.1</span><span class="special">),</span> <span class="string">"real_concept"</span><span class="special">);</span>
- <span class="preprocessor">#endif</span>
- <span class="preprocessor">#endif</span>
- <span class="preprocessor">#else</span>
- <span class="identifier">std</span><span class="special">::</span><span class="identifier">cout</span> <span class="special"><<</span> <span class="string">"<note>The long double tests have been disabled on this platform "</span>
- <span class="string">"either because the long double overloads of the usual math functions are "</span>
- <span class="string">"not available at all, or because they are too inaccurate for these tests "</span>
- <span class="string">"to pass.</note>"</span> <span class="special"><<</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">cout</span><span class="special">;</span>
- <span class="preprocessor">#endif</span>
- <span class="special">}</span>
- </pre>
- <p>
- That's almost all there is too it - except that if the above program is run
- it's very likely that all the tests will fail as the default maximum allowable
- error is 1 epsilon. So we'll define a function (don't forget to call it from
- the start of the <code class="computeroutput"><span class="identifier">test_main</span></code>
- above) to up the limits to something sensible, based both on the function
- we're calling and on the particular tests plus the platform and compiler:
- </p>
- <pre class="programlisting"><span class="keyword">void</span> <span class="identifier">expected_results</span><span class="special">()</span>
- <span class="special">{</span>
- <span class="comment">//</span>
- <span class="comment">// Define the max and mean errors expected for</span>
- <span class="comment">// various compilers and platforms.</span>
- <span class="comment">//</span>
- <span class="keyword">const</span> <span class="keyword">char</span><span class="special">*</span> <span class="identifier">largest_type</span><span class="special">;</span>
- <span class="preprocessor">#ifndef</span> <span class="identifier">BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS</span>
- <span class="keyword">if</span><span class="special">(</span><span class="identifier">boost</span><span class="special">::</span><span class="identifier">math</span><span class="special">::</span><span class="identifier">policies</span><span class="special">::</span><span class="identifier">digits</span><span class="special"><</span><span class="keyword">double</span><span class="special">,</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">math</span><span class="special">::</span><span class="identifier">policies</span><span class="special">::</span><span class="identifier">policy</span><span class="special"><></span> <span class="special">>()</span> <span class="special">==</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">math</span><span class="special">::</span><span class="identifier">policies</span><span class="special">::</span><span class="identifier">digits</span><span class="special"><</span><span class="keyword">long</span> <span class="keyword">double</span><span class="special">,</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">math</span><span class="special">::</span><span class="identifier">policies</span><span class="special">::</span><span class="identifier">policy</span><span class="special"><></span> <span class="special">>())</span>
- <span class="special">{</span>
- <span class="identifier">largest_type</span> <span class="special">=</span> <span class="string">"(long\\s+)?double|real_concept"</span><span class="special">;</span>
- <span class="special">}</span>
- <span class="keyword">else</span>
- <span class="special">{</span>
- <span class="identifier">largest_type</span> <span class="special">=</span> <span class="string">"long double|real_concept"</span><span class="special">;</span>
- <span class="special">}</span>
- <span class="preprocessor">#else</span>
- <span class="identifier">largest_type</span> <span class="special">=</span> <span class="string">"(long\\s+)?double"</span><span class="special">;</span>
- <span class="preprocessor">#endif</span>
- <span class="comment">//</span>
- <span class="comment">// We call add_expected_result for each error rate we wish to adjust, these tell</span>
- <span class="comment">// handle_test_result what level of error is acceptable. We can have as many calls</span>
- <span class="comment">// to add_expected_result as we need, each one establishes a rule for acceptable error</span>
- <span class="comment">// with rules set first given preference.</span>
- <span class="comment">//</span>
- <span class="identifier">add_expected_result</span><span class="special">(</span>
- <span class="comment">/* First argument is a regular expression to match against the name of the compiler
- set in BOOST_COMPILER */</span>
- <span class="string">".*"</span><span class="special">,</span>
- <span class="comment">/* Second argument is a regular expression to match against the name of the
- C++ standard library as set in BOOST_STDLIB */</span>
- <span class="string">".*"</span><span class="special">,</span>
- <span class="comment">/* Third argument is a regular expression to match against the name of the
- platform as set in BOOST_PLATFORM */</span>
- <span class="string">".*"</span><span class="special">,</span>
- <span class="comment">/* Forth argument is the name of the type being tested, normally we will
- only need to up the acceptable error rate for the widest floating
- point type being tested */</span>
- <span class="identifier">largest_real</span><span class="special">,</span>
- <span class="comment">/* Fifth argument is a regular expression to match against
- the name of the group of data being tested */</span>
- <span class="string">"MySpecial Function:.*Small.*"</span><span class="special">,</span>
- <span class="comment">/* Sixth argument is a regular expression to match against the name
- of the function being tested */</span>
- <span class="string">"boost::math::my_special"</span><span class="special">,</span>
- <span class="comment">/* Seventh argument is the maximum allowable error expressed in units
- of machine epsilon passed as a long integer value */</span>
- <span class="number">50</span><span class="special">,</span>
- <span class="comment">/* Eighth argument is the maximum allowable mean error expressed in units
- of machine epsilon passed as a long integer value */</span>
- <span class="number">20</span><span class="special">);</span>
- <span class="special">}</span>
- </pre>
- <h5>
- <a name="math_toolkit.special_tut.special_tut_test.h4"></a>
- <span class="phrase"><a name="math_toolkit.special_tut.special_tut_test.testing_multiprecision_types"></a></span><a class="link" href="special_tut_test.html#math_toolkit.special_tut.special_tut_test.testing_multiprecision_types">Testing
- Multiprecision Types</a>
- </h5>
- <p>
- Testing of multiprecision types is handled by the test drivers in libs/multiprecision/test/math,
- please refer to these for examples. Note that these tests are run only occationally
- as they take a lot of CPU cycles to build and run.
- </p>
- <h5>
- <a name="math_toolkit.special_tut.special_tut_test.h5"></a>
- <span class="phrase"><a name="math_toolkit.special_tut.special_tut_test.improving_compile_times"></a></span><a class="link" href="special_tut_test.html#math_toolkit.special_tut.special_tut_test.improving_compile_times">Improving
- Compile Times</a>
- </h5>
- <p>
- As noted above, these test programs can take a while to build as we're instantiating
- a lot of templates for several different types, and our test runners are
- already stretched to the limit, and probably using outdated "spare"
- hardware. There are two things we can do to speed things up:
- </p>
- <div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; ">
- <li class="listitem">
- Use a precompiled header.
- </li>
- <li class="listitem">
- Use separate compilation of our special function templates.
- </li>
- </ul></div>
- <p>
- We can make these changes by changing the list of includes from:
- </p>
- <pre class="programlisting"><span class="preprocessor">#include</span> <span class="special"><</span><span class="identifier">boost</span><span class="special">/</span><span class="identifier">math</span><span class="special">/</span><span class="identifier">special_functions</span><span class="special">/</span><span class="identifier">math_fwd</span><span class="special">.</span><span class="identifier">hpp</span><span class="special">></span>
- <span class="preprocessor">#include</span> <span class="special"><</span><span class="identifier">boost</span><span class="special">/</span><span class="identifier">math</span><span class="special">/</span><span class="identifier">tools</span><span class="special">/</span><span class="identifier">test</span><span class="special">.</span><span class="identifier">hpp</span><span class="special">></span>
- <span class="preprocessor">#include</span> <span class="special"><</span><span class="identifier">boost</span><span class="special">/</span><span class="identifier">math</span><span class="special">/</span><span class="identifier">tools</span><span class="special">/</span><span class="identifier">stats</span><span class="special">.</span><span class="identifier">hpp</span><span class="special">></span>
- <span class="preprocessor">#include</span> <span class="special"><</span><span class="identifier">boost</span><span class="special">/</span><span class="identifier">type_traits</span><span class="special">.</span><span class="identifier">hpp</span><span class="special">></span>
- <span class="preprocessor">#include</span> <span class="special"><</span><span class="identifier">boost</span><span class="special">/</span><span class="identifier">array</span><span class="special">.</span><span class="identifier">hpp</span><span class="special">></span>
- <span class="preprocessor">#include</span> <span class="string">"functor.hpp"</span>
- <span class="preprocessor">#include</span> <span class="string">"handle_test_result.hpp"</span>
- </pre>
- <p>
- To just:
- </p>
- <pre class="programlisting"><span class="preprocessor">#include</span> <span class="special"><</span><span class="identifier">pch_light</span><span class="special">.</span><span class="identifier">hpp</span><span class="special">></span>
- </pre>
- <p>
- And changing
- </p>
- <pre class="programlisting"><span class="preprocessor">#include</span> <span class="special"><</span><span class="identifier">boost</span><span class="special">/</span><span class="identifier">math</span><span class="special">/</span><span class="identifier">special_functions</span><span class="special">/</span><span class="identifier">my_special</span><span class="special">.</span><span class="identifier">hpp</span><span class="special">></span>
- </pre>
- <p>
- To:
- </p>
- <pre class="programlisting"><span class="preprocessor">#include</span> <span class="special"><</span><span class="identifier">boost</span><span class="special">/</span><span class="identifier">math</span><span class="special">/</span><span class="identifier">special_functions</span><span class="special">/</span><span class="identifier">math_fwd</span><span class="special">.</span><span class="identifier">hpp</span><span class="special">></span>
- </pre>
- <p>
- The Jamfile target that builds the test program will need the targets
- </p>
- <pre class="programlisting"><span class="identifier">test_instances</span><span class="comment">//test_instances pch_light</span>
- </pre>
- <p>
- adding to it's list of source dependencies (see the Jamfile for examples).
- </p>
- <p>
- Finally the project in libs/math/test/test_instances will need modifying
- to instantiate function <code class="computeroutput"><span class="identifier">my_special</span></code>.
- </p>
- <p>
- These changes should be made last, when <code class="computeroutput"><span class="identifier">my_special</span></code>
- is stable and the code is in Trunk.
- </p>
- <h5>
- <a name="math_toolkit.special_tut.special_tut_test.h6"></a>
- <span class="phrase"><a name="math_toolkit.special_tut.special_tut_test.concept_checks"></a></span><a class="link" href="special_tut_test.html#math_toolkit.special_tut.special_tut_test.concept_checks">Concept
- Checks</a>
- </h5>
- <p>
- Our concept checks verify that your function's implementation makes no assumptions
- that aren't required by our <a class="link" href="../real_concepts.html" title="Conceptual Requirements for Real Number Types">Real
- number conceptual requirements</a>. They also check for various common
- bugs and programming traps that we've fallen into over time. To add your
- function to these tests, edit libs/math/test/compile_test/instantiate.hpp
- to add calls to your function: there are 7 calls to each function, each with
- a different purpose. Search for something like "ibeta" or "gamm_p"
- and follow their examples.
- </p>
- </div>
- <table xmlns:rev="http://www.cs.rpi.edu/~gregod/boost/tools/doc/revision" width="100%"><tr>
- <td align="left"></td>
- <td align="right"><div class="copyright-footer">Copyright © 2006-2019 Nikhar
- Agrawal, Anton Bikineev, Paul A. Bristow, Marco Guazzone, Christopher Kormanyos,
- Hubert Holin, Bruno Lalande, John Maddock, Jeremy Murphy, Matthew Pulver, Johan
- Råde, Gautam Sewani, Benjamin Sobotta, Nicholas Thompson, Thijs van den Berg,
- Daryle Walker and Xiaogang Zhang<p>
- Distributed under the Boost Software License, Version 1.0. (See accompanying
- file LICENSE_1_0.txt or copy at <a href="http://www.boost.org/LICENSE_1_0.txt" target="_top">http://www.boost.org/LICENSE_1_0.txt</a>)
- </p>
- </div></td>
- </tr></table>
- <hr>
- <div class="spirit-nav">
- <a accesskey="p" href="special_tut_impl.html"><img src="../../../../../../doc/src/images/prev.png" alt="Prev"></a><a accesskey="u" href="../special_tut.html"><img src="../../../../../../doc/src/images/up.png" alt="Up"></a><a accesskey="h" href="../../index.html"><img src="../../../../../../doc/src/images/home.png" alt="Home"></a><a accesskey="n" href="../relative_error.html"><img src="../../../../../../doc/src/images/next.png" alt="Next"></a>
- </div>
- </body>
- </html>
|