[Boost-bugs] [Boost C++ Libraries] #9104: boost::math::ellint_2 bug in x86_64 double precision

Subject: [Boost-bugs] [Boost C++ Libraries] #9104: boost::math::ellint_2 bug in x86_64 double precision
From: Boost C++ Libraries (noreply_at_[hidden])
Date: 2013-09-12 03:51:51


#9104: boost::math::ellint_2 bug in x86_64 double precision
------------------------------+-------------------------
 Reporter: julian.panetta@… | Owner: johnmaddock
     Type: Bugs | Status: new
Milestone: To Be Determined | Component: math
  Version: Boost 1.54.0 | Severity: Problem
 Keywords: |
------------------------------+-------------------------
 The way ellint_e_imp reduces its input angle into the range |phi| <= pi/2
 has a bug when executing in double precision mode on intel chips (as
 opposed to the default double extended-precision mode) that results in
 errors as great as 100%.

 The relevant lines are at boost/math/special_functions/ellint_2.hpp:

 {{{
 T rphi = boost::math::tools::fmod_workaround(phi, T(constants::pi<T>() /
 2));
 T m = floor((2 * phi) / constants::pi<T>());
 }}}

 Compiled with optimizations on both g++ and clang++, fmod_workaround()
 essentially becomes a "fprem" instruction, and (2 * phi) / pi() becomes an
 "fdivr" instruction. "fprem" ignores the precision mode and always
 produces exact results. "fdivr," however, gives less accurate results in
 plain double precision mode. This leads to the following error when, for
 example, phi = M_PI (slightly less than boost's more accurate pi()):

 In plain double precision, rphi is computed as slightly less than pi()/2
 (correct since M_PI < pi()). However, m = 2 (incorrect, rounded result).
 The end result is m * pi()/2 + rphi is close to 3pi/2 instead of M_PI,
 causing the computed integral to be 50% too large. In extended precision,
 the "fdivr" is accurate enough to yield m = 1 correctly.

 There are two ways I can see of avoiding this:

 {{{
 T rphi = boost::math::tools::fmod_workaround(phi, T(constants::pi<T>() /
 2));
 T m = (phi - rphi) / T(constants::pi<T>() / 2);
 }}}

 and the less verbose:

 {{{
 T m = floor(phi / T(constants::pi<T>() / 2));
 T rphi = phi - m * T(constants::pi<T>() / 2);
 }}}

 Both would ensure rphi and m are consistent with the original phi. Note:
 in the second formula for m, T(constants::pi<T>() / 2) is folded into a
 constant that is exactly pi()/2, whereas the floor() argument in the
 original version generated extra code to double phi.

-- 
Ticket URL: <https://svn.boost.org/trac/boost/ticket/9104>
Boost C++ Libraries <http://www.boost.org/>
Boost provides free peer-reviewed portable C++ source libraries.

This archive was generated by hypermail 2.1.7 : 2017-02-16 18:50:14 UTC