Skip to content

Commit ab265d4

Browse files
authored
Merge pull request #1315 from boostorg/edges_c99_funcs
Repair neg-zero and tiny-arg erf and erfc
2 parents 0992ba6 + c1d2bec commit ab265d4

File tree

2 files changed

+36
-10
lines changed

2 files changed

+36
-10
lines changed

include/boost/math/special_functions/erf.hpp

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,19 @@ T erf_imp(T z, bool invert, const Policy& pol, const Tag& t)
153153
if ((boost::math::isnan)(z))
154154
return policies::raise_domain_error("boost::math::erf<%1%>(%1%)", "Expected a finite argument but got %1%", z, pol);
155155

156-
if(z < 0)
156+
if (fabs(z) < tools::root_epsilon<T>())
157+
{
158+
// Series[Erf[x], {x, 0, 4}]
159+
// Series[Erfc[x], {x, 0, 4}]
160+
161+
const T term2 { z * 2 / constants::root_pi<T>() };
162+
163+
return invert ? 1 - term2 : term2;
164+
}
165+
166+
const bool signbit_result = ((boost::math::signbit)(z) != 0);
167+
168+
if (signbit_result)
157169
{
158170
if(!invert)
159171
return -erf_imp(T(-z), invert, pol, t);
@@ -172,32 +184,32 @@ T erf_imp(T z, bool invert, const Policy& pol, const Tag& t)
172184
}
173185
else
174186
{
175-
T x = z * z;
187+
const T z_sq { z * z };
188+
176189
if(z < 1.3f)
177190
{
178191
// Compute P:
179192
// This is actually good for z p to 2 or so, but the cutoff given seems
180-
// to be the best compromise. Performance wise, this is way quicker than anything else...
193+
// to be the best compromise. Regarding performance, this is way quicker than anything else...
181194
result = erf_series_near_zero_sum(z, pol);
182195
}
183-
else if(x > 1 / tools::epsilon<T>())
196+
else if(z_sq > 1 / tools::epsilon<T>())
184197
{
185198
// http://functions.wolfram.com/06.27.06.0006.02
186199
invert = !invert;
187-
result = exp(-x) / (constants::root_pi<T>() * z);
200+
result = exp(-z_sq) / (constants::root_pi<T>() * z);
188201
}
189202
else
190203
{
191204
// Compute Q:
192205
invert = !invert;
193-
result = z * exp(-x);
206+
result = z * exp(-z_sq);
194207
result /= boost::math::constants::root_pi<T>();
195-
result *= upper_gamma_fraction(T(0.5f), x, policies::get_epsilon<T, Policy>());
208+
result *= upper_gamma_fraction(T(0.5f), z_sq, policies::get_epsilon<T, Policy>());
196209
}
197210
}
198-
if(invert)
199-
result = 1 - result;
200-
return result;
211+
212+
return ((!invert) ? result : 1 - result);
201213
}
202214
// LCOV_EXCL_STOP
203215

test/test_erf.hpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,20 @@ void test_erf(T, const char* name)
174174
do_test_erfc_inv<T>(erfc_inv_big_data, name, "Inverse Erfc Function: extreme values");
175175
}
176176

177+
BOOST_CHECK_EQUAL(boost::math::erf(T(0)), T(0));
178+
BOOST_CHECK_EQUAL(boost::math::erfc(T(0)), T(1));
179+
180+
BOOST_CHECK(boost::math::erf(boost::math::tools::root_epsilon<T>() / 8) > T(0));
181+
BOOST_CHECK(boost::math::erf(boost::math::tools::epsilon<T>() / 32) >= T(0));
182+
BOOST_CHECK(boost::math::erfc(boost::math::tools::epsilon<T>() / 32) <= T(1));
183+
184+
const bool has_negative_zero { ((boost::math::signbit)(-T(0)) != 0) };
185+
186+
if(has_negative_zero)
187+
{
188+
BOOST_CHECK_EQUAL(boost::math::erf(-T(0)), -T(0));
189+
}
190+
177191
BOOST_IF_CONSTEXPR(std::numeric_limits<T>::has_quiet_NaN)
178192
{
179193
BOOST_CHECK_THROW(boost::math::erf(std::numeric_limits<T>::quiet_NaN()), std::domain_error);

0 commit comments

Comments
 (0)