From c307f865208b6a3f45d8cfcd7e7af18345576ab2 Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Thu, 24 Dec 2020 00:19:20 +0200 Subject: [PATCH] Add core/cmath.hpp --- include/boost/core/cmath.hpp | 126 +++++++++++++++++ test/Jamfile.v2 | 2 + test/cmath_test.cpp | 266 +++++++++++++++++++++++++++++++++++ 3 files changed, 394 insertions(+) create mode 100644 include/boost/core/cmath.hpp create mode 100644 test/cmath_test.cpp diff --git a/include/boost/core/cmath.hpp b/include/boost/core/cmath.hpp new file mode 100644 index 0000000..842766f --- /dev/null +++ b/include/boost/core/cmath.hpp @@ -0,0 +1,126 @@ +#ifndef BOOST_CORE_CMATH_HPP_INCLUDED +#define BOOST_CORE_CMATH_HPP_INCLUDED + +// MS compatible compilers support #pragma once + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +// boost/core/cmath.hpp +// +// Floating point classification and sign manipulation functions +// Extracted from https://github.com/boostorg/lexical_cast/pull/37 +// +// Copyright 2020 Peter Dimov +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#if defined(_MSC_VER) && _MSC_VER < 1800 +# include +#endif + +namespace boost +{ +namespace core +{ +#if defined(_MSC_VER) && _MSC_VER < 1800 + +template T copysign( T x, T y ) +{ + return static_cast( _copysign( static_cast( x ), static_cast( y ) ) ); +} + +template bool isnan( T x ) +{ + return _isnan( static_cast( x ) ) != 0; +} + +template bool isfinite( T x ) +{ + return _finite( static_cast( x ) ) != 0; +} + +template bool isinf( T x ) +{ + return ( _fpclass( static_cast( x ) ) & ( _FPCLASS_PINF | _FPCLASS_NINF ) ) != 0; +} + +inline bool isnormal( float x ) +{ + // no _fpclassf in 32 bit mode + unsigned y = reinterpret_cast< unsigned const& >( x ); + unsigned exp = ( y >> 23 ) & 0xFF; + return exp != 0 && exp != 0xFF; +} + +inline bool isnormal( double x ) +{ + return ( _fpclass( x ) & ( _FPCLASS_PN | _FPCLASS_NN ) ) != 0; +} + +inline bool isnormal( long double x ) +{ + return boost::core::isnormal( static_cast( x ) ); +} + +template bool signbit( T x ) +{ + return _copysign( 1.0, static_cast( x ) ) < 0.0; +} + +#else + +using std::isfinite; +using std::isnan; +using std::isinf; +using std::isnormal; + +using std::signbit; + +// std::copysign doesn't exist in libstdc++ under -std=c++03 + +#if !defined(__GNUC__) + +template T copysign( T x, T y ) +{ + return std::copysign( x, y ); +} + +#else + +namespace detail +{ + +// ::copysignl is unreliable, use the built-ins + +inline float copysign_impl( float x, float y ) +{ + return __builtin_copysignf( x, y ); +} + +inline double copysign_impl( double x, double y ) +{ + return __builtin_copysign( x, y ); +} + +inline long double copysign_impl( long double x, long double y ) +{ + return __builtin_copysignl( x, y ); +} + +} // namespace detail + +template T copysign( T x, T y ) +{ + return boost::core::detail::copysign_impl( x, y ); +} + +#endif // !defined(__GNUC__) +#endif // #if defined(_MSC_VER) && _MSC_VER < 1800 + +} // namespace core +} // namespace boost + +#endif // #ifndef BOOST_CORE_CMATH_HPP_INCLUDED diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 0616ecc..6aa1bf2 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -220,5 +220,7 @@ run uncaught_exceptions_np.cpp run no_exceptions_support_test.cpp ; run no_exceptions_support_test.cpp : : : off : no_exceptions_support_test_nx ; +run cmath_test.cpp ; + use-project /boost/core/swap : ./swap ; build-project ./swap ; diff --git a/test/cmath_test.cpp b/test/cmath_test.cpp new file mode 100644 index 0000000..8e2a903 --- /dev/null +++ b/test/cmath_test.cpp @@ -0,0 +1,266 @@ +// Test for boost/core/cmath.hpp +// +// Copyright 2020 Peter Dimov +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include + +template void test_positive_normal( T x ) +{ + BOOST_TEST( boost::core::isfinite( x ) ); + BOOST_TEST( !boost::core::isinf( x ) ); + BOOST_TEST( !boost::core::isnan( x ) ); + BOOST_TEST( boost::core::isnormal( x ) ); + + BOOST_TEST( !boost::core::signbit( x ) ); + + BOOST_TEST_EQ( boost::core::copysign( T(+2), x ), T(+2) ); + BOOST_TEST_EQ( boost::core::copysign( T(-2), x ), T(+2) ); +} + +template void test_negative_normal( T x ) +{ + BOOST_TEST( boost::core::isfinite( x ) ); + BOOST_TEST( !boost::core::isinf( x ) ); + BOOST_TEST( !boost::core::isnan( x ) ); + BOOST_TEST( boost::core::isnormal( x ) ); + + BOOST_TEST( boost::core::signbit( x ) ); + + BOOST_TEST_EQ( boost::core::copysign( T(+2), x ), T(-2) ); + BOOST_TEST_EQ( boost::core::copysign( T(-2), x ), T(-2) ); +} + +template void test_positive_zero( T x ) +{ + BOOST_TEST( boost::core::isfinite( x ) ); + BOOST_TEST( !boost::core::isinf( x ) ); + BOOST_TEST( !boost::core::isnan( x ) ); + BOOST_TEST( !boost::core::isnormal( x ) ); + + BOOST_TEST( !boost::core::signbit( x ) ); + + BOOST_TEST_EQ( boost::core::copysign( T(+2), x ), T(+2) ); + BOOST_TEST_EQ( boost::core::copysign( T(-2), x ), T(+2) ); +} + +template void test_negative_zero( T x ) +{ + BOOST_TEST( boost::core::isfinite( x ) ); + BOOST_TEST( !boost::core::isinf( x ) ); + BOOST_TEST( !boost::core::isnan( x ) ); + BOOST_TEST( !boost::core::isnormal( x ) ); + + BOOST_TEST( boost::core::signbit( x ) ); + + BOOST_TEST_EQ( boost::core::copysign( T(+2), x ), T(-2) ); + BOOST_TEST_EQ( boost::core::copysign( T(-2), x ), T(-2) ); +} + +template void test_positive_infinity( T x ) +{ + BOOST_TEST( !boost::core::isfinite( x ) ); + BOOST_TEST( boost::core::isinf( x ) ); + BOOST_TEST( !boost::core::isnan( x ) ); + BOOST_TEST( !boost::core::isnormal( x ) ); + + BOOST_TEST( !boost::core::signbit( x ) ); + + BOOST_TEST_EQ( boost::core::copysign( T(+2), x ), T(+2) ); + BOOST_TEST_EQ( boost::core::copysign( T(-2), x ), T(+2) ); +} + +template void test_negative_infinity( T x ) +{ + BOOST_TEST( !boost::core::isfinite( x ) ); + BOOST_TEST( boost::core::isinf( x ) ); + BOOST_TEST( !boost::core::isnan( x ) ); + BOOST_TEST( !boost::core::isnormal( x ) ); + + BOOST_TEST( boost::core::signbit( x ) ); + + BOOST_TEST_EQ( boost::core::copysign( T(+2), x ), T(-2) ); + BOOST_TEST_EQ( boost::core::copysign( T(-2), x ), T(-2) ); +} + +template void test_positive_nan( T x ) +{ + BOOST_TEST( !boost::core::isfinite( x ) ); + BOOST_TEST( !boost::core::isinf( x ) ); + BOOST_TEST( boost::core::isnan( x ) ); + BOOST_TEST( !boost::core::isnormal( x ) ); + + BOOST_TEST( !boost::core::signbit( x ) ); + + BOOST_TEST_EQ( boost::core::copysign( T(+2), x ), T(+2) ); + BOOST_TEST_EQ( boost::core::copysign( T(-2), x ), T(+2) ); +} + +template void test_negative_nan( T x ) +{ + BOOST_TEST( !boost::core::isfinite( x ) ); + BOOST_TEST( !boost::core::isinf( x ) ); + BOOST_TEST( boost::core::isnan( x ) ); + BOOST_TEST( !boost::core::isnormal( x ) ); + + BOOST_TEST( boost::core::signbit( x ) ); + + BOOST_TEST_EQ( boost::core::copysign( T(+2), x ), T(-2) ); + BOOST_TEST_EQ( boost::core::copysign( T(-2), x ), T(-2) ); +} + +template void test_positive_subnormal( T x ) +{ + BOOST_TEST( boost::core::isfinite( x ) ); + BOOST_TEST( !boost::core::isinf( x ) ); + BOOST_TEST( !boost::core::isnan( x ) ); + BOOST_TEST( !boost::core::isnormal( x ) ); + + BOOST_TEST( !boost::core::signbit( x ) ); + + BOOST_TEST_EQ( boost::core::copysign( T(+2), x ), T(+2) ); + BOOST_TEST_EQ( boost::core::copysign( T(-2), x ), T(+2) ); +} + +template void test_negative_subnormal( T x ) +{ + BOOST_TEST( boost::core::isfinite( x ) ); + BOOST_TEST( !boost::core::isinf( x ) ); + BOOST_TEST( !boost::core::isnan( x ) ); + BOOST_TEST( !boost::core::isnormal( x ) ); + + BOOST_TEST( boost::core::signbit( x ) ); + + BOOST_TEST_EQ( boost::core::copysign( T(+2), x ), T(-2) ); + BOOST_TEST_EQ( boost::core::copysign( T(-2), x ), T(-2) ); +} + +template void test_positive_normal_( T x ) +{ + test_positive_normal( x ); + test_positive_normal( boost::core::copysign( x, T(+1) ) ); + test_negative_normal( boost::core::copysign( x, T(-1) ) ); +} + +template void test_negative_normal_( T x ) +{ + test_negative_normal( x ); + test_positive_normal( boost::core::copysign( x, T(+1) ) ); + test_negative_normal( boost::core::copysign( x, T(-1) ) ); +} + +template void test_positive_zero_( T x ) +{ + test_positive_zero( x ); + test_positive_zero( boost::core::copysign( x, T(+1) ) ); + test_negative_zero( boost::core::copysign( x, T(-1) ) ); +} + +template void test_negative_zero_( T x ) +{ + test_negative_zero( x ); + test_positive_zero( boost::core::copysign( x, T(+1) ) ); + test_negative_zero( boost::core::copysign( x, T(-1) ) ); +} + +template void test_positive_infinity_( T x ) +{ + test_positive_infinity( x ); + test_positive_infinity( boost::core::copysign( x, T(+1) ) ); + test_negative_infinity( boost::core::copysign( x, T(-1) ) ); +} + +template void test_negative_infinity_( T x ) +{ + test_negative_infinity( x ); + test_positive_infinity( boost::core::copysign( x, T(+1) ) ); + test_negative_infinity( boost::core::copysign( x, T(-1) ) ); +} + +template void test_positive_nan_( T x ) +{ + test_positive_nan( x ); + test_positive_nan( boost::core::copysign( x, T(+1) ) ); + test_negative_nan( boost::core::copysign( x, T(-1) ) ); +} + +template void test_negative_nan_( T x ) +{ + test_negative_nan( x ); + test_positive_nan( boost::core::copysign( x, T(+1) ) ); + test_negative_nan( boost::core::copysign( x, T(-1) ) ); +} + +template void test_positive_subnormal_( T x ) +{ + test_positive_subnormal( x ); + test_positive_subnormal( boost::core::copysign( x, T(+1) ) ); + test_negative_subnormal( boost::core::copysign( x, T(-1) ) ); +} + +template void test_negative_subnormal_( T x ) +{ + test_negative_subnormal( x ); + test_positive_subnormal( boost::core::copysign( x, T(+1) ) ); + test_negative_subnormal( boost::core::copysign( x, T(-1) ) ); +} + +int main() +{ + // float + + test_positive_normal_( +1.0f ); + test_negative_normal_( -1.0f ); + test_positive_normal_( FLT_MIN ); + test_negative_normal_( -FLT_MIN ); + test_positive_normal_( FLT_MAX ); + test_negative_normal_( -FLT_MAX ); + test_positive_zero_( +0.0f ); + test_negative_zero_( -0.0f ); + test_positive_infinity_( std::numeric_limits::infinity() ); + test_negative_infinity_( -std::numeric_limits::infinity() ); + test_positive_nan_( std::numeric_limits::quiet_NaN() ); + test_negative_nan_( boost::core::copysign( std::numeric_limits::quiet_NaN(), -1.0f ) ); + test_positive_subnormal_( FLT_MIN / 2 ); + test_negative_subnormal_( -FLT_MIN / 2 ); + + // double + + test_positive_normal_( +1.0 ); + test_negative_normal_( -1.0 ); + test_positive_normal_( DBL_MIN ); + test_negative_normal_( -DBL_MIN ); + test_positive_normal_( DBL_MAX ); + test_negative_normal_( -DBL_MAX ); + test_positive_zero_( +0.0 ); + test_negative_zero_( -0.0 ); + test_positive_infinity_( std::numeric_limits::infinity() ); + test_negative_infinity_( -std::numeric_limits::infinity() ); + test_positive_nan_( std::numeric_limits::quiet_NaN() ); + test_negative_nan_( boost::core::copysign( std::numeric_limits::quiet_NaN(), -1.0 ) ); + test_positive_subnormal_( DBL_MIN / 2 ); + test_negative_subnormal_( -DBL_MIN / 2 ); + + // long double + + test_positive_normal_( +1.0l ); + test_negative_normal_( -1.0l ); + test_positive_normal_( LDBL_MIN ); + test_negative_normal_( -LDBL_MIN ); + test_positive_normal_( LDBL_MAX ); + test_negative_normal_( -LDBL_MAX ); + test_positive_zero_( +0.0l ); + test_negative_zero_( -0.0l ); + test_positive_infinity_( std::numeric_limits::infinity() ); + test_negative_infinity_( -std::numeric_limits::infinity() ); + test_positive_nan_( std::numeric_limits::quiet_NaN() ); + test_negative_nan_( boost::core::copysign( std::numeric_limits::quiet_NaN(), -1.0l ) ); + test_positive_subnormal_( LDBL_MIN / 2 ); + test_negative_subnormal_( -LDBL_MIN / 2 ); + + return boost::report_errors(); +}