Louis Dionne 585da50d7d
[third-party] Add a snapshot of Boost.Math 1.89 standalone (#141508)
This PR adds the code of Boost.Math as of version 1.89 into the
third-party directory, as discussed in a recent RFC [1].

The goal is for this code to be used as a back-end for the C++17
Math Special Functions.

As explained in third-paty/README.md, this code is cleared for
usage inside libc++ for the Math Special functions, however
the LLVM Foundation should be consulted before using this
code anywhere else in the LLVM project, due to the fact
that it is under the Boost Software License (as opposed
to the usual LLVM license). See the RFC [1] for more details.

[1]: https://discourse.llvm.org/t/rfc-libc-taking-a-dependency-on-boost-math-for-the-c-17-math-special-functions
2025-10-27 14:43:57 -07:00

135 lines
5.3 KiB
C++

// (C) Copyright John Maddock 2006, 2015
// Use, modification and distribution are subject to the
// Boost Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_MATH_RELATIVE_ERROR
#define BOOST_MATH_RELATIVE_ERROR
#include <boost/math/special_functions/fpclassify.hpp>
#include <boost/math/tools/promotion.hpp>
#include <boost/math/tools/precision.hpp>
namespace boost{
namespace math{
template <class T, class U>
typename boost::math::tools::promote_args<T,U>::type relative_difference(const T& arg_a, const U& arg_b)
{
typedef typename boost::math::tools::promote_args<T, U>::type result_type;
result_type a = arg_a;
result_type b = arg_b;
BOOST_MATH_STD_USING
#ifdef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
//
// If math.h has no long double support we can't rely
// on the math functions generating exponents outside
// the range of a double:
//
result_type min_val = (std::max)(
tools::min_value<result_type>(),
static_cast<result_type>((std::numeric_limits<double>::min)()));
result_type max_val = (std::min)(
tools::max_value<result_type>(),
static_cast<result_type>((std::numeric_limits<double>::max)()));
#else
result_type min_val = tools::min_value<result_type>();
result_type max_val = tools::max_value<result_type>();
#endif
// Screen out NaN's first, if either value is a NaN then the distance is "infinite":
if((boost::math::isnan)(a) || (boost::math::isnan)(b))
return max_val;
// Screen out infinities:
if(fabs(b) > max_val)
{
if(fabs(a) > max_val)
return (a < 0) == (b < 0) ? 0 : max_val; // one infinity is as good as another!
else
return max_val; // one infinity and one finite value implies infinite difference
}
else if(fabs(a) > max_val)
return max_val; // one infinity and one finite value implies infinite difference
//
// If the values have different signs, treat as infinite difference:
//
if(((a < 0) != (b < 0)) && (a != 0) && (b != 0))
return max_val;
a = fabs(a);
b = fabs(b);
//
// Now deal with zero's, if one value is zero (or denorm) then treat it the same as
// min_val for the purposes of the calculation that follows:
//
if(a < min_val)
a = min_val;
if(b < min_val)
b = min_val;
return (std::max)(fabs((a - b) / a), fabs((a - b) / b));
}
#if (defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__)) && (LDBL_MAX_EXP <= DBL_MAX_EXP)
template <>
inline boost::math::tools::promote_args<double, double>::type relative_difference(const double& arg_a, const double& arg_b)
{
BOOST_MATH_STD_USING
double a = arg_a;
double b = arg_b;
//
// On Mac OS X we evaluate "double" functions at "long double" precision,
// but "long double" actually has a very slightly narrower range than "double"!
// Therefore use the range of "long double" as our limits since results outside
// that range may have been truncated to 0 or INF:
//
double min_val = (std::max)((double)tools::min_value<long double>(), tools::min_value<double>());
double max_val = (std::min)((double)tools::max_value<long double>(), tools::max_value<double>());
// Screen out NaN's first, if either value is a NaN then the distance is "infinite":
if((boost::math::isnan)(a) || (boost::math::isnan)(b))
return max_val;
// Screen out infinities:
if(fabs(b) > max_val)
{
if(fabs(a) > max_val)
return 0; // one infinity is as good as another!
else
return max_val; // one infinity and one finite value implies infinite difference
}
else if(fabs(a) > max_val)
return max_val; // one infinity and one finite value implies infinite difference
//
// If the values have different signs, treat as infinite difference:
//
if(((a < 0) != (b < 0)) && (a != 0) && (b != 0))
return max_val;
a = fabs(a);
b = fabs(b);
//
// Now deal with zero's, if one value is zero (or denorm) then treat it the same as
// min_val for the purposes of the calculation that follows:
//
if(a < min_val)
a = min_val;
if(b < min_val)
b = min_val;
return (std::max)(fabs((a - b) / a), fabs((a - b) / b));
}
#endif
template <class T, class U>
inline typename boost::math::tools::promote_args<T, U>::type epsilon_difference(const T& arg_a, const U& arg_b)
{
typedef typename boost::math::tools::promote_args<T, U>::type result_type;
result_type r = relative_difference(arg_a, arg_b);
if(tools::max_value<result_type>() * boost::math::tools::epsilon<result_type>() < r)
return tools::max_value<result_type>();
return r / boost::math::tools::epsilon<result_type>();
}
} // namespace math
} // namespace boost
#endif