diff --git a/doc/sf/ccmath.qbk b/doc/sf/ccmath.qbk index 6cb6d7df67..b71c235217 100644 --- a/doc/sf/ccmath.qbk +++ b/doc/sf/ccmath.qbk @@ -195,6 +195,9 @@ All of the following functions require C++17 or greater. template constexpr Promoted nexttoward(T from, long double to) + template + constexpr bool signbit(T arg) + } // Namespaces [endsect] [/section:ccmath Constexpr CMath] diff --git a/include/boost/math/ccmath/ccmath.hpp b/include/boost/math/ccmath/ccmath.hpp index 2749ec7b28..f075eac968 100644 --- a/include/boost/math/ccmath/ccmath.hpp +++ b/include/boost/math/ccmath/ccmath.hpp @@ -39,5 +39,7 @@ #include #include #include +#include +#include #endif // BOOST_MATH_CCMATH_HPP diff --git a/include/boost/math/ccmath/signbit.hpp b/include/boost/math/ccmath/signbit.hpp new file mode 100644 index 0000000000..1964b68d38 --- /dev/null +++ b/include/boost/math/ccmath/signbit.hpp @@ -0,0 +1,60 @@ +// (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 +#include +#include +#include +#include + +namespace boost::math::ccmath { + +namespace detail { + +// 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 NANs and 0s are treated as positive + +template +constexpr bool signbit_impl(T arg) +{ + if (boost::math::ccmath::isnan(arg)) + { + return false; + } + + return arg < static_cast(0); +} + +} + +// Return value: true if arg is negative, false if arg is 0, NAN, or positive +template , 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 , bool> = true> +constexpr bool signbit(Z arg) +{ + return boost::math::ccmath::signbit(static_cast(arg)); +} + +} // Namespaces + +#endif // BOOST_MATH_CCMATH_SIGNBIT_HPP diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index dba0fcf115..39287ee891 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -161,6 +161,7 @@ test-suite special_fun : [ run ccmath_isunordered_test.cpp ../../test/build//boost_unit_test_framework : : : [ requires cxx17_if_constexpr ] ] [ run ccmath_next_test.cpp ../../test/build//boost_unit_test_framework : : : [ requires cxx17_if_constexpr ] ] [ run ccmath_fma_test.cpp ../../test/build//boost_unit_test_framework : : : [ requires cxx17_if_constexpr ] ] + [ run ccmath_signbit_test.cpp ../../test/build//boost_unit_test_framework : : : [ requires cxx17_if_constexpr ] ] [ run log1p_expm1_test.cpp test_instances//test_instances pch_light ../../test/build//boost_unit_test_framework ] [ run powm1_sqrtp1m1_test.cpp test_instances//test_instances pch_light ../../test/build//boost_unit_test_framework ] [ run git_issue_705.cpp ../../test/build//boost_unit_test_framework ] diff --git a/test/ccmath_signbit_test.cpp b/test/ccmath_signbit_test.cpp new file mode 100644 index 0000000000..8a1c66ff20 --- /dev/null +++ b/test/ccmath_signbit_test.cpp @@ -0,0 +1,43 @@ +// (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) + +#include +#include + +#if !defined(BOOST_MATH_NO_CONSTEXPR_DETECTION) && !defined(BOOST_MATH_USING_BUILTIN_CONSTANT_P) +template +void test() +{ + // Edge cases + static_assert(boost::math::ccmath::signbit(T(0)) == false); + static_assert(boost::math::ccmath::signbit(std::numeric_limits::quiet_NaN()) == false); + static_assert(boost::math::ccmath::signbit(std::numeric_limits::signaling_NaN()) == false); + + // Positive numbers + static_assert(boost::math::ccmath::signbit(std::numeric_limits::infinity()) == false); + static_assert(boost::math::ccmath::signbit(T(1)) == false); + + // Negative numbers + static_assert(boost::math::ccmath::signbit(-std::numeric_limits::infinity()) == true); + static_assert(boost::math::ccmath::signbit(T(-1)) == true); +} + +int main(void) +{ + test(); + test(); + + #ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS + test(); + #endif + + return 0; +} +#else +int main(void) +{ + return 0; +} +#endif diff --git a/test/compile_test/ccmath_signbit_incl_test.cpp b/test/compile_test/ccmath_signbit_incl_test.cpp new file mode 100644 index 0000000000..1f829c8ad6 --- /dev/null +++ b/test/compile_test/ccmath_signbit_incl_test.cpp @@ -0,0 +1,16 @@ +// (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) + +#include +#include "test_compile_result.hpp" + +void compile_and_link_test() +{ + check_result(boost::math::ccmath::signbit(1.0F)); + check_result(boost::math::ccmath::signbit(1.0)); +#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS + check_result(boost::math::ccmath::signbit(1.0L)); +#endif +}