diff --git a/docs/source/api/basic_functions.rst b/docs/source/api/basic_functions.rst index 63d0e20..58cd2bb 100644 --- a/docs/source/api/basic_functions.rst +++ b/docs/source/api/basic_functions.rst @@ -39,6 +39,10 @@ Basic functions .. doxygenfunction:: fmod(const T1, const T2) :project: gcem +.. _hypot-func-ref: +.. doxygenfunction:: hypot(const T1, const T2) + :project: gcem + .. _log-function-reference: .. doxygenfunction:: log(const T) :project: gcem diff --git a/docs/source/api/math_index.rst b/docs/source/api/math_index.rst index 51a1844..5d06871 100644 --- a/docs/source/api/math_index.rst +++ b/docs/source/api/math_index.rst @@ -61,6 +61,8 @@ Mathematical functions +---------------------------------------+----------------------------------------------------+ | :ref:`fmod ` | remainder of division function | +---------------------------------------+----------------------------------------------------+ +| :ref:`hypot ` | Pythagorean addition function | ++---------------------------------------+----------------------------------------------------+ | :ref:`log ` | natural logarithm function | +---------------------------------------+----------------------------------------------------+ | :ref:`log1p ` | natural logarithm 1 plus argument function | diff --git a/include/gcem.hpp b/include/gcem.hpp index 6aa367d..cb28ff0 100644 --- a/include/gcem.hpp +++ b/include/gcem.hpp @@ -46,6 +46,7 @@ namespace gcem #include "gcem_incl/min.hpp" #include "gcem_incl/sqrt.hpp" #include "gcem_incl/inv_sqrt.hpp" + #include "gcem_incl/hypot.hpp" #include "gcem_incl/find_exponent.hpp" #include "gcem_incl/find_fraction.hpp" diff --git a/include/gcem_incl/gcem_options.hpp b/include/gcem_incl/gcem_options.hpp index f26eaf8..cd2747c 100644 --- a/include/gcem_incl/gcem_options.hpp +++ b/include/gcem_incl/gcem_options.hpp @@ -53,7 +53,7 @@ #endif #ifndef GCEM_VERSION_MINOR - #define GCEM_VERSION_MINOR 15 + #define GCEM_VERSION_MINOR 16 #endif #ifndef GCEM_VERSION_PATCH diff --git a/include/gcem_incl/hypot.hpp b/include/gcem_incl/hypot.hpp new file mode 100644 index 0000000..5a805ed --- /dev/null +++ b/include/gcem_incl/hypot.hpp @@ -0,0 +1,90 @@ +/*################################################################################ + ## + ## Copyright (C) 2016-2022 Keith O'Hara + ## + ## This file is part of the GCE-Math C++ library. + ## + ## Licensed under the Apache License, Version 2.0 (the "License"); + ## you may not use this file except in compliance with the License. + ## You may obtain a copy of the License at + ## + ## http://www.apache.org/licenses/LICENSE-2.0 + ## + ## Unless required by applicable law or agreed to in writing, software + ## distributed under the License is distributed on an "AS IS" BASIS, + ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ## See the License for the specific language governing permissions and + ## limitations under the License. + ## + ################################################################################*/ + +/* + * compile-time Pythagorean addition function + */ + +// see: https://en.wikipedia.org/wiki/Pythagorean_addition + +#ifndef _gcem_hypot_HPP +#define _gcem_hypot_HPP + +namespace internal +{ + +template +constexpr +T +hypot_compute(const T x, const T ydx) +noexcept +{ + return abs(x) * sqrt( T(1) + (ydx * ydx) ); +} + +template +constexpr +T +hypot_vals_check(const T x, const T y) +noexcept +{ + return( any_nan(x, y) ? \ + GCLIM::quiet_NaN() : + // + any_inf(x,y) ? \ + GCLIM::infinity() : + // indistinguishable from zero or one + GCLIM::min() > abs(x) ? \ + abs(y) : + GCLIM::min() > abs(y) ? \ + abs(x) : + // else + hypot_compute(x, y/x) ); +} + +template> +constexpr +TC +hypot_type_check(const T1 x, const T2 y) +noexcept +{ + return hypot_vals_check(static_cast(x),static_cast(y)); +} + +} + +/** + * Compile-time Pythagorean addition function + * + * @param x a real-valued input. + * @param y a real-valued input. + * @return Computes \f$ x \oplus y = \sqrt{x^2 + y^2} \f$. + */ + +template +constexpr +common_return_t +hypot(const T1 x, const T2 y) +noexcept +{ + return internal::hypot_type_check(x,y); +} + +#endif diff --git a/tests/Makefile b/tests/Makefile index 664806d..f67cf5d 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -98,6 +98,9 @@ fmod: gcd: $(GCEM_MAKE_CALL) +hypot: + $(GCEM_MAKE_CALL) + incomplete_beta: $(GCEM_MAKE_CALL) diff --git a/tests/hypot.cpp b/tests/hypot.cpp new file mode 100644 index 0000000..1bfc9f1 --- /dev/null +++ b/tests/hypot.cpp @@ -0,0 +1,75 @@ +/*################################################################################ + ## + ## Copyright (C) 2016-2022 Keith O'Hara + ## + ## This file is part of the GCE-Math C++ library. + ## + ## Licensed under the Apache License, Version 2.0 (the "License"); + ## you may not use this file except in compliance with the License. + ## You may obtain a copy of the License at + ## + ## http://www.apache.org/licenses/LICENSE-2.0 + ## + ## Unless required by applicable law or agreed to in writing, software + ## distributed under the License is distributed on an "AS IS" BASIS, + ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ## See the License for the specific language governing permissions and + ## limitations under the License. + ## + ################################################################################*/ + +#define TEST_PRINT_PRECISION_1 6 +#define TEST_PRINT_PRECISION_2 18 + +#include "gcem_tests.hpp" + +int main() +{ + print_begin("hypot"); + + // + + GCEM_TEST_COMPARE_VALS(gcem::hypot,std::hypot, 0.0L, 0.0L); + GCEM_TEST_COMPARE_VALS(gcem::hypot,std::hypot, -0.0L, 0.0L); + GCEM_TEST_COMPARE_VALS(gcem::hypot,std::hypot, 0.0L, -0.0L); + GCEM_TEST_COMPARE_VALS(gcem::hypot,std::hypot, -0.0L, -0.0L); + + GCEM_TEST_COMPARE_VALS(gcem::hypot,std::hypot, 0.2L, 0.0L); + GCEM_TEST_COMPARE_VALS(gcem::hypot,std::hypot, -0.2L, 0.0L); + GCEM_TEST_COMPARE_VALS(gcem::hypot,std::hypot, 0.001L, 0.001L); + GCEM_TEST_COMPARE_VALS(gcem::hypot,std::hypot, 0.49L, 0.49L); + + GCEM_TEST_COMPARE_VALS(gcem::hypot,std::hypot, -0.5L, -0.5L); + GCEM_TEST_COMPARE_VALS(gcem::hypot,std::hypot, 0.5L, -0.5L); + GCEM_TEST_COMPARE_VALS(gcem::hypot,std::hypot, -0.5L, 0.5L); + + GCEM_TEST_COMPARE_VALS(gcem::hypot,std::hypot, 9.6L, 8.4L); + GCEM_TEST_COMPARE_VALS(gcem::hypot,std::hypot, 1.0L, 0.0L); + GCEM_TEST_COMPARE_VALS(gcem::hypot,std::hypot, 0.0L, 1.0L); + GCEM_TEST_COMPARE_VALS(gcem::hypot,std::hypot, -1.0L, 0.0L); + GCEM_TEST_COMPARE_VALS(gcem::hypot,std::hypot, 0.0L, -1.0L); + GCEM_TEST_COMPARE_VALS(gcem::hypot,std::hypot, 1.0L, 3.0L); + GCEM_TEST_COMPARE_VALS(gcem::hypot,std::hypot, -5.0L, 2.5L); + GCEM_TEST_COMPARE_VALS(gcem::hypot,std::hypot, -1000.0L, -0.001L); + GCEM_TEST_COMPARE_VALS(gcem::hypot,std::hypot, 0.1337L, -123456.0L); + + GCEM_TEST_COMPARE_VALS(gcem::hypot,std::hypot, TEST_POSINF, 2.0L); + GCEM_TEST_COMPARE_VALS(gcem::hypot,std::hypot, 2.0L, TEST_POSINF); + GCEM_TEST_COMPARE_VALS(gcem::hypot,std::hypot, TEST_POSINF, TEST_POSINF); + + GCEM_TEST_COMPARE_VALS(gcem::hypot,std::hypot, TEST_NEGINF, 2.0L); + GCEM_TEST_COMPARE_VALS(gcem::hypot,std::hypot, 2.0L, TEST_NEGINF); + GCEM_TEST_COMPARE_VALS(gcem::hypot,std::hypot, TEST_NEGINF, TEST_NEGINF); + + // + + GCEM_TEST_COMPARE_VALS(gcem::hypot,std::hypot, TEST_NAN, 1.0L); + GCEM_TEST_COMPARE_VALS(gcem::hypot,std::hypot, 1.0L, TEST_NAN); + GCEM_TEST_COMPARE_VALS(gcem::hypot,std::hypot, TEST_NAN, TEST_NAN); + + // + + print_final("hypot"); + + return 0; +}