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

220 lines
6.1 KiB
C++

// (C) Copyright Matt Borland 2022.
// 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_CCMATH_SIGNBIT_HPP
#define BOOST_MATH_CCMATH_SIGNBIT_HPP
#include <boost/math/ccmath/detail/config.hpp>
#ifdef BOOST_MATH_NO_CCMATH
#error "The header <boost/math/signbit.hpp> can only be used in C++17 and later."
#endif
#include <cstdint>
#include <boost/math/tools/assert.hpp>
#include <boost/math/special_functions/detail/fp_traits.hpp>
#include <boost/math/ccmath/isnan.hpp>
#include <boost/math/ccmath/abs.hpp>
#ifdef __has_include
# if __has_include(<bit>)
# include <bit>
# if __cpp_lib_bit_cast >= 201806L
# define BOOST_MATH_BIT_CAST(T, x) std::bit_cast<T>(x)
# endif
# elif defined(__has_builtin)
# if __has_builtin(__builtin_bit_cast)
# define BOOST_MATH_BIT_CAST(T, x) __builtin_bit_cast(T, x)
# endif
# endif
#endif
/*
The following error is given using Apple Clang version 13.1.6, and Clang 13, and 14 on Ubuntu 22.04.01
TODO: Remove the following undef when Apple Clang supports
ccmath_signbit_test.cpp:32:19: error: static_assert expression is not an integral constant expression
static_assert(boost::math::ccmath::signbit(T(-1)) == true);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../../../boost/math/ccmath/signbit.hpp:62:24: note: constexpr bit_cast involving bit-field is not yet supported
const auto u = BOOST_MATH_BIT_CAST(float_bits, arg);
^
../../../boost/math/ccmath/signbit.hpp:20:37: note: expanded from macro 'BOOST_MATH_BIT_CAST'
# define BOOST_MATH_BIT_CAST(T, x) __builtin_bit_cast(T, x)
^
*/
#if defined(__clang__) && defined(BOOST_MATH_BIT_CAST)
# undef BOOST_MATH_BIT_CAST
#endif
namespace boost::math::ccmath {
namespace detail {
#ifdef BOOST_MATH_BIT_CAST
struct IEEEf2bits
{
#if BOOST_MATH_ENDIAN_LITTLE_BYTE
std::uint32_t mantissa : 23;
std::uint32_t exponent : 8;
std::uint32_t sign : 1;
#else // Big endian
std::uint32_t sign : 1;
std::uint32_t exponent : 8;
std::uint32_t mantissa : 23;
#endif
};
struct IEEEd2bits
{
#if BOOST_MATH_ENDIAN_LITTLE_BYTE
std::uint32_t mantissa_l : 32;
std::uint32_t mantissa_h : 20;
std::uint32_t exponent : 11;
std::uint32_t sign : 1;
#else // Big endian
std::uint32_t sign : 1;
std::uint32_t exponent : 11;
std::uint32_t mantissa_h : 20;
std::uint32_t mantissa_l : 32;
#endif
};
// 80 bit long double
#if LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384
struct IEEEl2bits
{
#if BOOST_MATH_ENDIAN_LITTLE_BYTE
std::uint32_t mantissa_l : 32;
std::uint32_t mantissa_h : 32;
std::uint32_t exponent : 15;
std::uint32_t sign : 1;
std::uint32_t pad : 32;
#else // Big endian
std::uint32_t pad : 32;
std::uint32_t sign : 1;
std::uint32_t exponent : 15;
std::uint32_t mantissa_h : 32;
std::uint32_t mantissa_l : 32;
#endif
};
// 128 bit long double
#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
struct IEEEl2bits
{
#if BOOST_MATH_ENDIAN_LITTLE_BYTE
std::uint64_t mantissa_l : 64;
std::uint64_t mantissa_h : 48;
std::uint32_t exponent : 15;
std::uint32_t sign : 1;
#else // Big endian
std::uint32_t sign : 1;
std::uint32_t exponent : 15;
std::uint64_t mantissa_h : 48;
std::uint64_t mantissa_l : 64;
#endif
};
// 64 bit long double (double == long double on ARM)
#elif LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
struct IEEEl2bits
{
#if BOOST_MATH_ENDIAN_LITTLE_BYTE
std::uint32_t mantissa_l : 32;
std::uint32_t mantissa_h : 20;
std::uint32_t exponent : 11;
std::uint32_t sign : 1;
#else // Big endian
std::uint32_t sign : 1;
std::uint32_t exponent : 11;
std::uint32_t mantissa_h : 20;
std::uint32_t mantissa_l : 32;
#endif
};
#else // Unsupported long double representation
# define BOOST_MATH_UNSUPPORTED_LONG_DOUBLE
#endif
template <typename T>
constexpr bool signbit_impl(T arg)
{
if constexpr (std::is_same_v<T, float>)
{
const auto u = BOOST_MATH_BIT_CAST(IEEEf2bits, arg);
return u.sign;
}
else if constexpr (std::is_same_v<T, double>)
{
const auto u = BOOST_MATH_BIT_CAST(IEEEd2bits, arg);
return u.sign;
}
#ifndef BOOST_MATH_UNSUPPORTED_LONG_DOUBLE
else if constexpr (std::is_same_v<T, long double>)
{
const auto u = BOOST_MATH_BIT_CAST(IEEEl2bits, arg);
return u.sign;
}
#endif
else
{
BOOST_MATH_ASSERT_MSG(!boost::math::ccmath::isnan(arg), "NAN is not supported with this type or platform");
BOOST_MATH_ASSERT_MSG(boost::math::ccmath::abs(arg) != 0, "Signed 0 is not support with this type or platform");
return arg < static_cast<T>(0);
}
}
#else
// Typical implementations of signbit involve type punning via union and manipulating
// overflow (see libc++ or musl). Neither of these are allowed in constexpr contexts
// (technically type punning via union in general is UB in c++ but well defined in C)
// therefore we static assert these cases.
template <typename T>
constexpr bool signbit_impl(T arg)
{
BOOST_MATH_ASSERT_MSG(!boost::math::ccmath::isnan(arg), "NAN is not supported without __builtin_bit_cast or std::bit_cast");
BOOST_MATH_ASSERT_MSG(boost::math::ccmath::abs(arg) != 0, "Signed 0 is not support without __builtin_bit_cast or std::bit_cast");
return arg < static_cast<T>(0);
}
#endif
}
// Return value: true if arg is negative, false if arg is 0, NAN, or positive
template <typename Real, std::enable_if_t<!std::is_integral_v<Real>, bool> = true>
constexpr bool signbit(Real arg)
{
if (BOOST_MATH_IS_CONSTANT_EVALUATED(arg))
{
return boost::math::ccmath::detail::signbit_impl(arg);
}
else
{
using std::signbit;
return signbit(arg);
}
}
template <typename Z, std::enable_if_t<std::is_integral_v<Z>, bool> = true>
constexpr bool signbit(Z arg)
{
return boost::math::ccmath::signbit(static_cast<double>(arg));
}
} // Namespaces
#endif // BOOST_MATH_CCMATH_SIGNBIT_HPP