Skip to content

Commit d721493

Browse files
committed
Merge branch 'develop' of https://github.com/boostorg/math into develop
2 parents d7ab3a9 + 09c0d37 commit d721493

13 files changed

+227
-17
lines changed

doc/sf/erf.qbk

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,29 @@ than the one shown will have __zero_error.
6666

6767
[table_erfc]
6868

69-
The following error plot are based on an exhaustive search of the functions domain, MSVC-15.5 at `double` precision,
70-
and GCC-7.1/Ubuntu for `long double` and `__float128`.
69+
The following error plots are based on an exhaustive search of the functions domain, MSVC-16.7.1 at `double` precision,
70+
and GCC-10/Mingw64 for `long double` and `__float128`.
7171

72-
[graph erf__double]
72+
[$../../reporting/accuracy/erf_errors_double.png]
7373

74-
[graph erf__80_bit_long_double]
74+
[$../../reporting/accuracy/erf_errors_long_double.png]
75+
76+
[$../../reporting/accuracy/erf_errors_float128.png]
77+
78+
In the erfc case, error rates are almost entirely the error in calculating `exp(-x*x)`:
79+
80+
[$../../reporting/accuracy/erfc_errors_double.png]
81+
82+
[$../../reporting/accuracy/erfc_errors_long_double.png]
83+
84+
[$../../reporting/accuracy/erfc_errors_float128.png]
85+
86+
Multiprecision error rates are similar, albeit with a much larger error in calculating the exponent term for erfc:
87+
88+
[$../../reporting/accuracy/erf_errors_cpp_bin_float_50.png]
89+
90+
[$../../reporting/accuracy/erfc_errors_cpp_bin_float_50.png]
7591

76-
[graph erf____float128]
7792

7893
[h4 Testing]
7994

include/boost/math/special_functions/erf.hpp

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,37 @@ inline T erf_asymptotic_limit()
105105
return erf_asymptotic_limit_N(tag_type());
106106
}
107107

108+
template <class T>
109+
struct erf_series_near_zero
110+
{
111+
typedef T result_type;
112+
T term;
113+
T zz;
114+
int k;
115+
erf_series_near_zero(const T& z) : term(z), zz(-z * z), k(0) {}
116+
117+
T operator()()
118+
{
119+
T result = term / (2 * k + 1);
120+
term *= zz / ++k;
121+
return result;
122+
}
123+
};
124+
125+
template <class T, class Policy>
126+
T erf_series_near_zero_sum(const T& x, const Policy& pol)
127+
{
128+
//
129+
// We need Kahan summation here, otherwise the errors grow fairly quickly.
130+
// This method is *much* faster than the alternatives even so.
131+
//
132+
erf_series_near_zero<T> sum(x);
133+
boost::uintmax_t max_iter = policies::get_max_series_iterations<Policy>();
134+
T result = constants::two_div_root_pi<T>() * tools::kahan_sum_series(sum, tools::digits<T>(), max_iter);
135+
policies::check_series_iterations<T>("boost::math::erf<%1%>(%1%, %1%)", max_iter, pol);
136+
return result;
137+
}
138+
108139
template <class T, class Policy, class Tag>
109140
T erf_imp(T z, bool invert, const Policy& pol, const Tag& t)
110141
{
@@ -132,20 +163,12 @@ T erf_imp(T z, bool invert, const Policy& pol, const Tag& t)
132163
else
133164
{
134165
T x = z * z;
135-
if(x < 0.6)
166+
if(z < 1.3f)
136167
{
137168
// Compute P:
138-
result = z * exp(-x);
139-
result /= sqrt(boost::math::constants::pi<T>());
140-
if(result != 0)
141-
result *= 2 * detail::lower_gamma_series(T(0.5f), x, pol);
142-
}
143-
else if(x < 1.1f)
144-
{
145-
// Compute Q:
146-
invert = !invert;
147-
result = tgamma_small_upper_part(T(0.5f), x, pol);
148-
result /= sqrt(boost::math::constants::pi<T>());
169+
// This is actually good for z p to 2 or so, but the cutoff given seems
170+
// to be the best compromise. Performance wise, this is way quicker than anything else...
171+
result = erf_series_near_zero_sum(z, pol);
149172
}
150173
else if(x > 1 / tools::epsilon<T>())
151174
{

reporting/accuracy/Jamfile.v2

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,3 +159,25 @@ boostbook standalone
159159
<xsl:param>generate.section.toc.level=10
160160
;
161161

162+
lib gmp ;
163+
lib mpfr ;
164+
lib quadmath ;
165+
#
166+
# Some manual tests that are expensive to run:
167+
#
168+
run erf_error_plot.cpp mpfr gmp : : : release <cxxstd>17 [ check-target-builds ../../config//has_mpfr : : <build>no ] : erf_error_plot_double ;
169+
explicit erf_error_plot_double ;
170+
run erf_error_plot.cpp mpfr gmp : : : release <cxxstd>17 <define>TEST_TYPE="\"long double\"" [ check-target-builds ../../config//has_mpfr : : <build>no ] : erf_error_plot_long_double ;
171+
explicit erf_error_plot_long_double ;
172+
run erf_error_plot.cpp mpfr gmp : : : release <cxxstd>17 <define>TEST_TYPE=cpp_bin_float_50 [ check-target-builds ../../config//has_mpfr : : <build>no ] : erf_error_plot_cpp_bin_float_50 ;
173+
explicit erf_error_plot_cpp_bin_float_50 ;
174+
run erf_error_plot.cpp mpfr gmp quadmath : : : release <cxxstd>17 <cxxstd-dialect>gnu <define>TEST_TYPE=float128 [ check-target-builds ../../config//has_mpfr : : <build>no ] : erf_error_plot_float128 ;
175+
explicit erf_error_plot_cpp_bin_float_50 ;
176+
run erfc_error_plot.cpp mpfr gmp : : : release <cxxstd>17 [ check-target-builds ../../config//has_mpfr : : <build>no ] : erfc_error_plot_double ;
177+
explicit erfc_error_plot_double ;
178+
run erfc_error_plot.cpp mpfr gmp : : : release <cxxstd>17 <define>TEST_TYPE="\"long double\"" [ check-target-builds ../../config//has_mpfr : : <build>no ] : erfc_error_plot_long_double ;
179+
explicit erfc_error_plot_long_double ;
180+
run erfc_error_plot.cpp mpfr gmp : : : release <cxxstd>17 <define>TEST_TYPE=cpp_bin_float_50 [ check-target-builds ../../config//has_mpfr : : <build>no ] : erfc_error_plot_cpp_bin_float_50 ;
181+
explicit erfc_error_plot_cpp_bin_float_50 ;
182+
run erfc_error_plot.cpp mpfr gmp quadmath : : : release <cxxstd>17 <cxxstd-dialect>gnu <define>TEST_TYPE=float128 [ check-target-builds ../../config//has_mpfr : : <build>no ] : erfc_error_plot_float128 ;
183+
explicit erfc_error_plot_cpp_bin_float_50 ;

reporting/accuracy/erf_error_plot.cpp

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
2+
// (C) Copyright Nick Thompson 2020.
3+
// (C) Copyright John Maddock 2020.
4+
// Use, modification and distribution are subject to the
5+
// Boost Software License, Version 1.0. (See accompanying file
6+
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7+
#include <iostream>
8+
#include <boost/math/tools/ulps_plot.hpp>
9+
#include <boost/core/demangle.hpp>
10+
#include <boost/multiprecision/mpfr.hpp>
11+
#include <boost/multiprecision/cpp_bin_float.hpp>
12+
#ifdef BOOST_HAS_FLOAT128
13+
#include <boost/multiprecision/float128.hpp>
14+
#endif
15+
16+
using namespace boost::multiprecision;
17+
18+
#ifndef TEST_TYPE
19+
#define TEST_TYPE double
20+
#endif
21+
22+
std::string test_type_name(BOOST_STRINGIZE(TEST_TYPE));
23+
std::string test_type_filename(BOOST_STRINGIZE(TEST_TYPE));
24+
25+
using boost::math::tools::ulps_plot;
26+
27+
int main()
28+
{
29+
std::string::size_type n;
30+
while ((n = test_type_filename.find_first_not_of("_qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890")) != std::string::npos)
31+
{
32+
test_type_filename[n] = '_';
33+
}
34+
35+
using PreciseReal = boost::multiprecision::mpfr_float_100;
36+
using CoarseReal = TEST_TYPE;
37+
38+
typedef boost::math::policies::policy<
39+
boost::math::policies::promote_float<false>,
40+
boost::math::policies::promote_double<false> >
41+
no_promote_policy;
42+
43+
auto ai_coarse = [](CoarseReal const& x)->CoarseReal {
44+
return erf(x);
45+
};
46+
auto ai_precise = [](PreciseReal const& x)->PreciseReal {
47+
return erf(x);
48+
};
49+
50+
std::string filename = "erf_errors_";
51+
filename += test_type_filename;
52+
filename += ".svg";
53+
int samples = 100000;
54+
// How many pixels wide do you want your .svg?
55+
int width = 700;
56+
// Near a root, we have unbounded relative error. So for functions with roots, we define an ULP clip:
57+
PreciseReal clip = 40;
58+
// Should we perturb the abscissas? i.e., should we compute the high precision function f at x,
59+
// and the low precision function at the nearest representable x̂ to x?
60+
// Or should we compute both the high precision and low precision function at a low precision representable x̂?
61+
bool perturb_abscissas = false;
62+
auto plot = ulps_plot<decltype(ai_precise), PreciseReal, CoarseReal>(ai_precise, CoarseReal(-10), CoarseReal(10), samples, perturb_abscissas);
63+
// Note the argument chaining:
64+
plot.clip(clip).width(width);
65+
plot.background_color("white").font_color("black");
66+
// Sometimes it's useful to set a title, but in many cases it's more useful to just use a caption.
67+
std::string title = "Erf ULP plot at " + test_type_name + " precision";
68+
plot.title(title);
69+
plot.vertical_lines(6);
70+
plot.add_fn(ai_coarse);
71+
// You can write the plot to a stream:
72+
//std::cout << plot;
73+
// Or to a file:
74+
plot.write(filename);
75+
}
115 KB
Loading
80.4 KB
Loading
319 KB
Loading
96.7 KB
Loading
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
2+
// (C) Copyright Nick Thompson 2020.
3+
// (C) Copyright John Maddock 2020.
4+
// Use, modification and distribution are subject to the
5+
// Boost Software License, Version 1.0. (See accompanying file
6+
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7+
#include <iostream>
8+
#include <boost/math/tools/ulps_plot.hpp>
9+
#include <boost/core/demangle.hpp>
10+
#include <boost/multiprecision/mpfr.hpp>
11+
#include <boost/multiprecision/cpp_bin_float.hpp>
12+
#ifdef BOOST_HAS_FLOAT128
13+
#include <boost/multiprecision/float128.hpp>
14+
#endif
15+
16+
using namespace boost::multiprecision;
17+
18+
#ifndef TEST_TYPE
19+
#define TEST_TYPE cpp_bin_float_50
20+
#endif
21+
22+
std::string test_type_name(BOOST_STRINGIZE(TEST_TYPE));
23+
std::string test_type_filename(BOOST_STRINGIZE(TEST_TYPE));
24+
25+
using boost::math::tools::ulps_plot;
26+
27+
int main()
28+
{
29+
std::string::size_type n;
30+
while ((n = test_type_filename.find_first_not_of("_qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890")) != std::string::npos)
31+
{
32+
test_type_filename[n] = '_';
33+
}
34+
35+
using PreciseReal = boost::multiprecision::mpfr_float_100;
36+
using CoarseReal = TEST_TYPE;
37+
38+
typedef boost::math::policies::policy<
39+
boost::math::policies::promote_float<false>,
40+
boost::math::policies::promote_double<false> >
41+
no_promote_policy;
42+
43+
auto ai_coarse = [](CoarseReal const& x)->CoarseReal {
44+
return erfc(x);
45+
};
46+
auto ai_precise = [](PreciseReal const& x)->PreciseReal {
47+
return erfc(x);
48+
};
49+
50+
std::string filename = "erfc_errors_";
51+
filename += test_type_filename;
52+
filename += ".svg";
53+
int samples = 100000;
54+
// How many pixels wide do you want your .svg?
55+
int width = 700;
56+
// Near a root, we have unbounded relative error. So for functions with roots, we define an ULP clip:
57+
PreciseReal clip = 40;
58+
// Should we perturb the abscissas? i.e., should we compute the high precision function f at x,
59+
// and the low precision function at the nearest representable x̂ to x?
60+
// Or should we compute both the high precision and low precision function at a low precision representable x̂?
61+
bool perturb_abscissas = false;
62+
auto plot = ulps_plot<decltype(ai_precise), PreciseReal, CoarseReal>(ai_precise, CoarseReal(-10), CoarseReal(30), samples, perturb_abscissas);
63+
// Note the argument chaining:
64+
plot.clip(clip).width(width);
65+
plot.background_color("white").font_color("black");
66+
// Sometimes it's useful to set a title, but in many cases it's more useful to just use a caption.
67+
std::string title = "Erfc ULP plot at " + test_type_name + " precision";
68+
plot.title(title);
69+
plot.vertical_lines(6);
70+
plot.add_fn(ai_coarse);
71+
// You can write the plot to a stream:
72+
//std::cout << plot;
73+
// Or to a file:
74+
plot.write(filename);
75+
}
171 KB
Loading

0 commit comments

Comments
 (0)