[DEV] add v1.66.0

This commit is contained in:
2018-01-12 21:47:58 +01:00
parent 87059bb1af
commit a97e9ae7d4
49032 changed files with 7668950 additions and 0 deletions

View File

@@ -0,0 +1,391 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
<title>Implementation</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.6.0">
<link rel="up" href="../special_tut.html" title="Tutorial: How to Write a New Special Function">
<link rel="prev" href="../special_tut.html" title="Tutorial: How to Write a New Special Function">
<link rel="next" href="special_tut_test.html" title="Testing">
</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.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="special_tut_test.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_impl"></a><a class="link" href="special_tut_impl.html" title="Implementation">Implementation</a>
</h3></div></div></div>
<p>
In this section, we'll provide a "recipe" for adding a new special
function to this library to make life easier for future authors wishing to
contribute. We'll assume the function returns a single floating-point result,
and takes two floating-point arguments. For the sake of exposition we'll
give the function the name <em class="replaceable"><code>my_special</code></em>.
</p>
<p>
Normally, the implementation of such a function is split into two layers
- a public user layer, and an internal implementation layer that does the
actual work. The implementation layer is declared inside a <code class="computeroutput"><span class="identifier">detail</span></code> namespace and has a simple signature:
</p>
<pre class="programlisting"><span class="keyword">namespace</span> <span class="identifier">boost</span> <span class="special">{</span> <span class="keyword">namespace</span> <span class="identifier">math</span> <span class="special">{</span> <span class="keyword">namespace</span> <span class="identifier">detail</span> <span class="special">{</span>
<span class="keyword">template</span> <span class="special">&lt;</span><span class="keyword">class</span> <span class="identifier">T</span><span class="special">,</span> <span class="keyword">class</span> <span class="identifier">Policy</span><span class="special">&gt;</span>
<span class="identifier">T</span> <span class="identifier">my_special_imp</span><span class="special">(</span><span class="keyword">const</span> <span class="identifier">T</span><span class="special">&amp;</span> <span class="identifier">a</span><span class="special">,</span> <span class="keyword">const</span> <span class="identifier">T</span><span class="special">&amp;</span><span class="identifier">b</span><span class="special">,</span> <span class="keyword">const</span> <span class="identifier">Policy</span><span class="special">&amp;</span> <span class="identifier">pol</span><span class="special">)</span>
<span class="special">{</span>
<span class="comment">/* Implementation goes here */</span>
<span class="special">}</span>
<span class="special">}}}</span> <span class="comment">// namespaces</span>
</pre>
<p>
We'll come back to what can go inside the implementation later, but first
lets look at the user layer. This consists of two overloads of the function,
with and without a <a class="link" href="../../policy.html" title="Chapter&#160;18.&#160;Policies: Controlling Precision, Error Handling etc">Policy</a> argument:
</p>
<pre class="programlisting"><span class="keyword">namespace</span> <span class="identifier">boost</span><span class="special">{</span> <span class="keyword">namespace</span> <span class="identifier">math</span><span class="special">{</span>
<span class="keyword">template</span> <span class="special">&lt;</span><span class="keyword">class</span> <span class="identifier">T</span><span class="special">,</span> <span class="keyword">class</span> <span class="identifier">U</span><span class="special">&gt;</span>
<span class="keyword">typename</span> <span class="identifier">tools</span><span class="special">::</span><span class="identifier">promote_args</span><span class="special">&lt;</span><span class="identifier">T</span><span class="special">,</span> <span class="identifier">U</span><span class="special">&gt;::</span><span class="identifier">type</span> <span class="identifier">my_special</span><span class="special">(</span><span class="keyword">const</span> <span class="identifier">T</span><span class="special">&amp;</span> <span class="identifier">a</span><span class="special">,</span> <span class="keyword">const</span> <span class="identifier">U</span><span class="special">&amp;</span> <span class="identifier">b</span><span class="special">);</span>
<span class="keyword">template</span> <span class="special">&lt;</span><span class="keyword">class</span> <span class="identifier">T</span><span class="special">,</span> <span class="keyword">class</span> <span class="identifier">U</span><span class="special">,</span> <span class="keyword">class</span> <span class="identifier">Policy</span><span class="special">&gt;</span>
<span class="keyword">typename</span> <span class="identifier">tools</span><span class="special">::</span><span class="identifier">promote_args</span><span class="special">&lt;</span><span class="identifier">T</span><span class="special">,</span> <span class="identifier">U</span><span class="special">&gt;::</span><span class="identifier">type</span> <span class="identifier">my_special</span><span class="special">(</span><span class="keyword">const</span> <span class="identifier">T</span><span class="special">&amp;</span> <span class="identifier">a</span><span class="special">,</span> <span class="keyword">const</span> <span class="identifier">U</span><span class="special">&amp;</span> <span class="identifier">b</span><span class="special">,</span> <span class="keyword">const</span> <span class="identifier">Policy</span><span class="special">&amp;</span> <span class="identifier">pol</span><span class="special">);</span>
<span class="special">}}</span> <span class="comment">// namespaces</span>
</pre>
<p>
Note how each argument has a different template type - this allows for mixed
type arguments - the return type is computed from a traits class and is the
"common type" of all the arguments after any integer arguments
have been promoted to type <code class="computeroutput"><span class="keyword">double</span></code>.
</p>
<p>
The implementation of the non-policy overload is trivial:
</p>
<pre class="programlisting"><span class="keyword">namespace</span> <span class="identifier">boost</span><span class="special">{</span> <span class="keyword">namespace</span> <span class="identifier">math</span><span class="special">{</span>
<span class="keyword">template</span> <span class="special">&lt;</span><span class="keyword">class</span> <span class="identifier">T</span><span class="special">,</span> <span class="keyword">class</span> <span class="identifier">U</span><span class="special">&gt;</span>
<span class="keyword">inline</span> <span class="keyword">typename</span> <span class="identifier">tools</span><span class="special">::</span><span class="identifier">promote_args</span><span class="special">&lt;</span><span class="identifier">T</span><span class="special">,</span> <span class="identifier">U</span><span class="special">&gt;::</span><span class="identifier">type</span> <span class="identifier">my_special</span><span class="special">(</span><span class="keyword">const</span> <span class="identifier">T</span><span class="special">&amp;</span> <span class="identifier">a</span><span class="special">,</span> <span class="keyword">const</span> <span class="identifier">U</span><span class="special">&amp;</span> <span class="identifier">b</span><span class="special">)</span>
<span class="special">{</span>
<span class="comment">// Simply forward with a default policy:</span>
<span class="keyword">return</span> <span class="identifier">my_special</span><span class="special">(</span><span class="identifier">a</span><span class="special">,</span> <span class="identifier">b</span><span class="special">,</span> <span class="identifier">policies</span><span class="special">::</span><span class="identifier">policy</span><span class="special">&lt;&gt;();</span>
<span class="special">}</span>
<span class="special">}}</span> <span class="comment">// namespaces</span>
</pre>
<p>
The implementation of the other overload is somewhat more complex, as there's
some meta-programming to do, but from a runtime perspective is still a one-line
forwarding function. Here it is with comments explaining what each line does:
</p>
<pre class="programlisting"><span class="keyword">namespace</span> <span class="identifier">boost</span><span class="special">{</span> <span class="keyword">namespace</span> <span class="identifier">math</span><span class="special">{</span>
<span class="keyword">template</span> <span class="special">&lt;</span><span class="keyword">class</span> <span class="identifier">T</span><span class="special">,</span> <span class="keyword">class</span> <span class="identifier">U</span><span class="special">,</span> <span class="keyword">class</span> <span class="identifier">Policy</span><span class="special">&gt;</span>
<span class="keyword">inline</span> <span class="keyword">typename</span> <span class="identifier">tools</span><span class="special">::</span><span class="identifier">promote_args</span><span class="special">&lt;</span><span class="identifier">T</span><span class="special">,</span> <span class="identifier">U</span><span class="special">&gt;::</span><span class="identifier">type</span> <span class="identifier">my_special</span><span class="special">(</span><span class="keyword">const</span> <span class="identifier">T</span><span class="special">&amp;</span> <span class="identifier">a</span><span class="special">,</span> <span class="keyword">const</span> <span class="identifier">U</span><span class="special">&amp;</span> <span class="identifier">b</span><span class="special">,</span> <span class="keyword">const</span> <span class="identifier">Policy</span><span class="special">&amp;</span> <span class="identifier">pol</span><span class="special">)</span>
<span class="special">{</span>
<span class="comment">//</span>
<span class="comment">// We've found some standard library functions to misbehave if any FPU exception flags</span>
<span class="comment">// are set prior to their call, this code will clear those flags, then reset them</span>
<span class="comment">// on exit:</span>
<span class="comment">//</span>
<span class="identifier">BOOST_FPU_EXCEPTION_GUARD</span>
<span class="comment">//</span>
<span class="comment">// The type of the result - the common type of T and U after</span>
<span class="comment">// any integer types have been promoted to double:</span>
<span class="comment">//</span>
<span class="keyword">typedef</span> <span class="keyword">typename</span> <span class="identifier">tools</span><span class="special">::</span><span class="identifier">promote_args</span><span class="special">&lt;</span><span class="identifier">T</span><span class="special">,</span> <span class="identifier">U</span><span class="special">&gt;::</span><span class="identifier">type</span> <span class="identifier">result_type</span><span class="special">;</span>
<span class="comment">//</span>
<span class="comment">// The type used for the calculation. This may be a wider type than</span>
<span class="comment">// the result in order to ensure full precision:</span>
<span class="comment">//</span>
<span class="keyword">typedef</span> <span class="keyword">typename</span> <span class="identifier">policies</span><span class="special">::</span><span class="identifier">evaluation</span><span class="special">&lt;</span><span class="identifier">result_type</span><span class="special">,</span> <span class="identifier">Policy</span><span class="special">&gt;::</span><span class="identifier">type</span> <span class="identifier">value_type</span><span class="special">;</span>
<span class="comment">//</span>
<span class="comment">// The type of the policy to forward to the actual implementation.</span>
<span class="comment">// We disable promotion of float and double as that's [possibly]</span>
<span class="comment">// happened already in the line above. Also reset to the default</span>
<span class="comment">// any policies we don't use (reduces code bloat if we're called</span>
<span class="comment">// multiple times with differing policies we don't actually use).</span>
<span class="comment">// Also normalise the type, again to reduce code bloat in case we're</span>
<span class="comment">// called multiple times with functionally identical policies that happen</span>
<span class="comment">// to be different types.</span>
<span class="comment">//</span>
<span class="keyword">typedef</span> <span class="keyword">typename</span> <span class="identifier">policies</span><span class="special">::</span><span class="identifier">normalise</span><span class="special">&lt;</span>
<span class="identifier">Policy</span><span class="special">,</span>
<span class="identifier">policies</span><span class="special">::</span><span class="identifier">promote_float</span><span class="special">&lt;</span><span class="keyword">false</span><span class="special">&gt;,</span>
<span class="identifier">policies</span><span class="special">::</span><span class="identifier">promote_double</span><span class="special">&lt;</span><span class="keyword">false</span><span class="special">&gt;,</span>
<span class="identifier">policies</span><span class="special">::</span><span class="identifier">discrete_quantile</span><span class="special">&lt;&gt;,</span>
<span class="identifier">policies</span><span class="special">::</span><span class="identifier">assert_undefined</span><span class="special">&lt;&gt;</span> <span class="special">&gt;::</span><span class="identifier">type</span> <span class="identifier">forwarding_policy</span><span class="special">;</span>
<span class="comment">//</span>
<span class="comment">// Whew. Now we can make the actual call to the implementation.</span>
<span class="comment">// Arguments are explicitly cast to the evaluation type, and the result</span>
<span class="comment">// passed through checked_narrowing_cast which handles things like overflow</span>
<span class="comment">// according to the policy passed:</span>
<span class="comment">//</span>
<span class="keyword">return</span> <span class="identifier">policies</span><span class="special">::</span><span class="identifier">checked_narrowing_cast</span><span class="special">&lt;</span><span class="identifier">result_type</span><span class="special">,</span> <span class="identifier">forwarding_policy</span><span class="special">&gt;(</span>
<span class="identifier">detail</span><span class="special">::</span><span class="identifier">my_special_imp</span><span class="special">(</span>
<span class="keyword">static_cast</span><span class="special">&lt;</span><span class="identifier">value_type</span><span class="special">&gt;(</span><span class="identifier">a</span><span class="special">),</span>
<span class="keyword">static_cast</span><span class="special">&lt;</span><span class="identifier">value_type</span><span class="special">&gt;(</span><span class="identifier">x</span><span class="special">),</span>
<span class="identifier">forwarding_policy</span><span class="special">()),</span>
<span class="string">"boost::math::my_special&lt;%1%&gt;(%1%, %1%)"</span><span class="special">);</span>
<span class="special">}</span>
<span class="special">}}</span> <span class="comment">// namespaces</span>
</pre>
<p>
We're now almost there, we just need to flesh out the details of the implementation
layer:
</p>
<pre class="programlisting"><span class="keyword">namespace</span> <span class="identifier">boost</span> <span class="special">{</span> <span class="keyword">namespace</span> <span class="identifier">math</span> <span class="special">{</span> <span class="keyword">namespace</span> <span class="identifier">detail</span> <span class="special">{</span>
<span class="keyword">template</span> <span class="special">&lt;</span><span class="keyword">class</span> <span class="identifier">T</span><span class="special">,</span> <span class="keyword">class</span> <span class="identifier">Policy</span><span class="special">&gt;</span>
<span class="identifier">T</span> <span class="identifier">my_special_imp</span><span class="special">(</span><span class="keyword">const</span> <span class="identifier">T</span><span class="special">&amp;</span> <span class="identifier">a</span><span class="special">,</span> <span class="keyword">const</span> <span class="identifier">T</span><span class="special">&amp;</span><span class="identifier">b</span><span class="special">,</span> <span class="keyword">const</span> <span class="identifier">Policy</span><span class="special">&amp;</span> <span class="identifier">pol</span><span class="special">)</span>
<span class="special">{</span>
<span class="comment">/* Implementation goes here */</span>
<span class="special">}</span>
<span class="special">}}}</span> <span class="comment">// namespaces</span>
</pre>
<p>
The following guidelines indicate what (other than basic arithmetic) can
go in the implementation:
</p>
<div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; ">
<li class="listitem">
Error conditions (for example bad arguments) should be handled by calling
one of the <a class="link" href="../error_handling.html#math_toolkit.error_handling.finding_more_information">policy
based error handlers</a>.
</li>
<li class="listitem">
Calls to standard library functions should be made unqualified (this
allows argument dependent lookup to find standard library functions for
user-defined floating point types such as those from <a href="../../../../../../libs/multiprecision/doc/html/index.html" target="_top">Boost.Multiprecision</a>).
In addition, the macro <code class="computeroutput"><span class="identifier">BOOST_MATH_STD_USING</span></code>
should appear at the start of the function (note no semi-colon afterwards!)
so that all the math functions in <code class="computeroutput"><span class="keyword">namespace</span>
<span class="identifier">std</span></code> are visible in the current
scope.
</li>
<li class="listitem">
Calls to other special functions should be made as fully qualified calls,
and include the policy parameter as the last argument, for example <code class="computeroutput"><span class="identifier">boost</span><span class="special">::</span><span class="identifier">math</span><span class="special">::</span><span class="identifier">tgamma</span><span class="special">(</span><span class="identifier">a</span><span class="special">,</span> <span class="identifier">pol</span><span class="special">)</span></code>.
</li>
<li class="listitem">
Where possible, evaluation of series, continued fractions, polynomials,
or root finding should use one of the <a class="link" href="../internals_overview.html" title="Overview">boiler-plate
functions</a>. In any case, after any iterative method, you should
verify that the number of iterations did not exceed the maximum specified
in the <a class="link" href="../../policy.html" title="Chapter&#160;18.&#160;Policies: Controlling Precision, Error Handling etc">Policy</a> type, and if it did terminate
as a result of exceeding the maximum, then the appropriate error handler
should be called (see existing code for examples).
</li>
<li class="listitem">
Numeric constants such as &#960; etc should be obtained via a call to the <a class="link" href="../constants.html" title="The Mathematical Constants">appropriate function</a>, for example:
<code class="computeroutput"><span class="identifier">constants</span><span class="special">::</span><span class="identifier">pi</span><span class="special">&lt;</span><span class="identifier">T</span><span class="special">&gt;()</span></code>.
</li>
<li class="listitem">
Where tables of coefficients are used (for example for rational approximations),
care should be taken to ensure these are initialized at program startup
to ensure thread safety when using user-defined number types. See for
example the use of <code class="computeroutput"><span class="identifier">erf_initializer</span></code>
in <a href="../../../../include/boost/math/special_functions/erf.hpp" target="_top">erf.hpp</a>.
</li>
</ul></div>
<p>
Here are some other useful internal functions:
</p>
<div class="informaltable"><table class="table">
<colgroup>
<col>
<col>
</colgroup>
<thead><tr>
<th>
<p>
function
</p>
</th>
<th>
<p>
Meaning
</p>
</th>
</tr></thead>
<tbody>
<tr>
<td>
<p>
<code class="computeroutput"><span class="identifier">policies</span><span class="special">::</span><span class="identifier">digits</span><span class="special">&lt;</span><span class="identifier">T</span><span class="special">,</span>
<span class="identifier">Policy</span><span class="special">&gt;()</span></code>
</p>
</td>
<td>
<p>
Returns number of binary digits in T (possible overridden by the
policy).
</p>
</td>
</tr>
<tr>
<td>
<p>
<code class="computeroutput"><span class="identifier">policies</span><span class="special">::</span><span class="identifier">get_max_series_iterations</span><span class="special">&lt;</span><span class="identifier">Policy</span><span class="special">&gt;()</span></code>
</p>
</td>
<td>
<p>
Maximum number of iterations for series evaluation.
</p>
</td>
</tr>
<tr>
<td>
<p>
<code class="computeroutput"><span class="identifier">policies</span><span class="special">::</span><span class="identifier">get_max_root_iterations</span><span class="special">&lt;</span><span class="identifier">Policy</span><span class="special">&gt;()</span></code>
</p>
</td>
<td>
<p>
Maximum number of iterations for root finding.
</p>
</td>
</tr>
<tr>
<td>
<p>
<code class="computeroutput"><span class="identifier">polices</span><span class="special">::</span><span class="identifier">get_epsilon</span><span class="special">&lt;</span><span class="identifier">T</span><span class="special">,</span>
<span class="identifier">Policy</span><span class="special">&gt;()</span></code>
</p>
</td>
<td>
<p>
Epsilon for type T, possibly overridden by the Policy.
</p>
</td>
</tr>
<tr>
<td>
<p>
<code class="computeroutput"><span class="identifier">tools</span><span class="special">::</span><span class="identifier">digits</span><span class="special">&lt;</span><span class="identifier">T</span><span class="special">&gt;()</span></code>
</p>
</td>
<td>
<p>
Returns the number of binary digits in T.
</p>
</td>
</tr>
<tr>
<td>
<p>
<code class="computeroutput"><span class="identifier">tools</span><span class="special">::</span><span class="identifier">max_value</span><span class="special">&lt;</span><span class="identifier">T</span><span class="special">&gt;()</span></code>
</p>
</td>
<td>
<p>
Equivalent to <code class="computeroutput"><span class="identifier">std</span><span class="special">::</span><span class="identifier">numeric_limits</span><span class="special">&lt;</span><span class="identifier">T</span><span class="special">&gt;::</span><span class="identifier">max</span><span class="special">()</span></code>
</p>
</td>
</tr>
<tr>
<td>
<p>
<code class="computeroutput"><span class="identifier">tools</span><span class="special">::</span><span class="identifier">min_value</span><span class="special">&lt;</span><span class="identifier">T</span><span class="special">&gt;()</span></code>
</p>
</td>
<td>
<p>
Equivalent to <code class="computeroutput"><span class="identifier">std</span><span class="special">::</span><span class="identifier">numeric_limits</span><span class="special">&lt;</span><span class="identifier">T</span><span class="special">&gt;::</span><span class="identifier">min</span><span class="special">()</span></code>
</p>
</td>
</tr>
<tr>
<td>
<p>
<code class="computeroutput"><span class="identifier">tools</span><span class="special">::</span><span class="identifier">log_max_value</span><span class="special">&lt;</span><span class="identifier">T</span><span class="special">&gt;()</span></code>
</p>
</td>
<td>
<p>
Equivalent to the natural logarithm of <code class="computeroutput"><span class="identifier">std</span><span class="special">::</span><span class="identifier">numeric_limits</span><span class="special">&lt;</span><span class="identifier">T</span><span class="special">&gt;::</span><span class="identifier">max</span><span class="special">()</span></code>
</p>
</td>
</tr>
<tr>
<td>
<p>
<code class="computeroutput"><span class="identifier">tools</span><span class="special">::</span><span class="identifier">log_min_value</span><span class="special">&lt;</span><span class="identifier">T</span><span class="special">&gt;()</span></code>
</p>
</td>
<td>
<p>
Equivalent to the natural logarithm of <code class="computeroutput"><span class="identifier">std</span><span class="special">::</span><span class="identifier">numeric_limits</span><span class="special">&lt;</span><span class="identifier">T</span><span class="special">&gt;::</span><span class="identifier">min</span><span class="special">()</span></code>
</p>
</td>
</tr>
<tr>
<td>
<p>
<code class="computeroutput"><span class="identifier">tools</span><span class="special">::</span><span class="identifier">epsilon</span><span class="special">&lt;</span><span class="identifier">T</span><span class="special">&gt;()</span></code>
</p>
</td>
<td>
<p>
Equivalent to <code class="computeroutput"><span class="identifier">std</span><span class="special">::</span><span class="identifier">numeric_limits</span><span class="special">&lt;</span><span class="identifier">T</span><span class="special">&gt;::</span><span class="identifier">epsilon</span><span class="special">()</span></code>.
</p>
</td>
</tr>
<tr>
<td>
<p>
<code class="computeroutput"><span class="identifier">tools</span><span class="special">::</span><span class="identifier">root_epsilon</span><span class="special">&lt;</span><span class="identifier">T</span><span class="special">&gt;()</span></code>
</p>
</td>
<td>
<p>
Equivalent to the square root of <code class="computeroutput"><span class="identifier">std</span><span class="special">::</span><span class="identifier">numeric_limits</span><span class="special">&lt;</span><span class="identifier">T</span><span class="special">&gt;::</span><span class="identifier">epsilon</span><span class="special">()</span></code>.
</p>
</td>
</tr>
<tr>
<td>
<p>
<code class="computeroutput"><span class="identifier">tools</span><span class="special">::</span><span class="identifier">forth_root_epsilon</span><span class="special">&lt;</span><span class="identifier">T</span><span class="special">&gt;()</span></code>
</p>
</td>
<td>
<p>
Equivalent to the forth root of <code class="computeroutput"><span class="identifier">std</span><span class="special">::</span><span class="identifier">numeric_limits</span><span class="special">&lt;</span><span class="identifier">T</span><span class="special">&gt;::</span><span class="identifier">epsilon</span><span class="special">()</span></code>.
</p>
</td>
</tr>
</tbody>
</table></div>
</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 &#169; 2006-2010, 2012-2014, 2017 Nikhar
Agrawal, Anton Bikineev, Paul A. Bristow, Marco Guazzone, Christopher Kormanyos,
Hubert Holin, Bruno Lalande, John Maddock, Jeremy Murphy, Johan R&#229;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.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="special_tut_test.html"><img src="../../../../../../doc/src/images/next.png" alt="Next"></a>
</div>
</body>
</html>

View File

@@ -0,0 +1,526 @@
<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.6.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">&lt;</span><span class="keyword">double</span><span class="special">&gt;(),</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">numeric_limits</span><span class="special">&lt;</span><span class="keyword">double</span><span class="special">&gt;::</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">&lt;</span><span class="identifier">boost</span><span class="special">::</span><span class="identifier">array</span><span class="special">&lt;</span><span class="identifier">T</span><span class="special">,</span> <span class="number">3</span><span class="special">&gt;,</span> <span class="number">10</span><span class="special">&gt;</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">&lt;</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">&gt;</span>
<span class="preprocessor">#include</span> <span class="special">&lt;</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">&gt;</span>
<span class="preprocessor">#include</span> <span class="special">&lt;</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">&gt;</span>
<span class="preprocessor">#include</span> <span class="special">&lt;</span><span class="identifier">fstream</span><span class="special">&gt;</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">&lt;</span><span class="keyword">class</span> <span class="identifier">T</span><span class="special">&gt;</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">&lt;</span><span class="identifier">cpp_dec_float</span><span class="special">&lt;</span><span class="number">500</span><span class="special">&gt;</span> <span class="special">&gt;</span> <span class="identifier">bignum</span><span class="special">;</span>
<span class="identifier">parameter_info</span><span class="special">&lt;</span><span class="identifier">bignum</span><span class="special">&gt;</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">&lt;</span><span class="identifier">bignum</span><span class="special">&gt;</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">&lt;</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">&amp;</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">&lt;&lt;</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">&lt;&lt;</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">&lt;&lt;</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">scientific</span> <span class="special">&lt;&lt;</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">&lt;</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">&gt;</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">&amp;</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">&lt;</span><span class="keyword">class</span> <span class="identifier">T</span><span class="special">&gt;</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">&lt;</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">&gt;</span>
<span class="preprocessor">#include</span> <span class="special">&lt;</span><span class="identifier">boost</span><span class="special">/</span><span class="identifier">test</span><span class="special">/</span><span class="identifier">floating_point_comparison</span><span class="special">.</span><span class="identifier">hpp</span><span class="special">&gt;</span>
<span class="comment">// Our function to test:</span>
<span class="preprocessor">#include</span> <span class="special">&lt;</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">&gt;</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">&lt;</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">&gt;</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">&lt;</span><span class="keyword">typename</span> <span class="identifier">table_type</span><span class="special">&lt;</span><span class="identifier">T</span><span class="special">&gt;::</span><span class="identifier">type</span><span class="special">&gt;(</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">&lt;</span><span class="keyword">class</span> <span class="identifier">T</span><span class="special">&gt;</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">&lt;</span><span class="identifier">T</span><span class="special">&gt;(</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">&lt;</span><span class="identifier">T</span><span class="special">&gt;(</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">&lt;</span><span class="identifier">T</span><span class="special">&gt;(</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">&lt;</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">&gt;</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">&amp;</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">&lt;</span><span class="identifier">value_type</span><span class="special">,</span> <span class="identifier">value_type</span><span class="special">&gt;;</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">&lt;</span><span class="identifier">value_type</span><span class="special">&gt;</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">&lt;&lt;</span> <span class="string">"Testing "</span> <span class="special">&lt;&lt;</span> <span class="identifier">test_name</span> <span class="special">&lt;&lt;</span> <span class="string">" with type "</span> <span class="special">&lt;&lt;</span> <span class="identifier">type_name</span>
<span class="special">&lt;&lt;</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">&lt;</span><span class="identifier">Real</span><span class="special">&gt;(</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">&lt;</span><span class="identifier">Real</span><span class="special">&gt;(</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">&lt;</span><span class="identifier">Real</span><span class="special">&gt;(</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">&lt;</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">&gt;</span>
<span class="preprocessor">#include</span> <span class="special">&lt;</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">&gt;</span>
<span class="preprocessor">#include</span> <span class="special">&lt;</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">&gt;</span>
<span class="preprocessor">#include</span> <span class="special">&lt;</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">&gt;</span>
<span class="preprocessor">#include</span> <span class="special">&lt;</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">&gt;</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">&lt;&lt;</span> <span class="string">"&lt;note&gt;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.&lt;/note&gt;"</span> <span class="special">&lt;&lt;</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">&lt;</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">&lt;&gt;</span> <span class="special">&gt;()</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">&lt;</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">&lt;&gt;</span> <span class="special">&gt;())</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">&lt;</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">&gt;</span>
<span class="preprocessor">#include</span> <span class="special">&lt;</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">&gt;</span>
<span class="preprocessor">#include</span> <span class="special">&lt;</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">&gt;</span>
<span class="preprocessor">#include</span> <span class="special">&lt;</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">&gt;</span>
<span class="preprocessor">#include</span> <span class="special">&lt;</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">&gt;</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">&lt;</span><span class="identifier">pch_light</span><span class="special">.</span><span class="identifier">hpp</span><span class="special">&gt;</span>
</pre>
<p>
And changing
</p>
<pre class="programlisting"><span class="preprocessor">#include</span> <span class="special">&lt;</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">&gt;</span>
</pre>
<p>
To:
</p>
<pre class="programlisting"><span class="preprocessor">#include</span> <span class="special">&lt;</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">&gt;</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 &#169; 2006-2010, 2012-2014, 2017 Nikhar
Agrawal, Anton Bikineev, Paul A. Bristow, Marco Guazzone, Christopher Kormanyos,
Hubert Holin, Bruno Lalande, John Maddock, Jeremy Murphy, Johan R&#229;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>