From fa136d59da5dd326104390983685e56cec097535 Mon Sep 17 00:00:00 2001 From: Ian Pike Date: Sat, 9 Mar 2024 05:36:27 -0500 Subject: [PATCH 1/3] Bring version 0.1.1 into main (#12) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add support for builtin detection * Update comments to be more eye catching * Add clang-cl and apple clang support * Fix bug with MSVC where not checking for nan * Remove msvc specific code in favor of generic approach * Prepare for 0.1.0 release. * Add doxygen docs * Finalize min/max * Add todo for later work on remquo * Cleanup main for 0.1.0 release * Bring standard back to C++17 * Minor cleanup to cmake list * Prep files for v0.1.0 release * Prepare for 0.1.0 release. * Add doxygen docs * Finalize min/max * Add todo for later work on remquo * Cleanup main for 0.1.0 release * Bring standard back to C++17 * Minor cleanup to cmake list * Prep files for v0.1.0 release * Bring everything in * Remove artifacts that mistakenly got pushed to main * Implement isgreater * Implement isgreaterequal * Implement isless * Implement islessequal * Implement islessgreater * Implement isnormal * Implement isunordered * Add test cases for all new compare functions * Add boilerplate code for future unit tests * Update progress * A lot of work done so far on log * log now far more efficient and accurate * Update readme progress * Continue work on log for double * remove unnecessary include * remove msvc section of unlikely * improve variable names * remove old log impl * Cleanup double implementation of log * cleanup of log details and add more comments. * Finalize work on log function * Update current progress * Fix bug with ±0 in log * Update test cases to cover all major edge cases * Move to correct folder * Add headers for log and log2 * Remove dependency on Threads * Initial addition of Contribution Guide * Cleanup README and add extra information * Update project version to v0.1.1 * Add top12 bits function for upcoming work * Finalize work with log function * Initial working revision of log2 * Push current work. Need to revise log2 to resolve bugs later. * Initial implementation of lerp * Fix scope bug * remove the direct use of cmath. * Update include to use cfloat instead of float.h * Cleanup macros and fix minor bugs * Fix bug where we were improperly assigning a correct value to the wrong variable * Update docs to mention not accounting for big-endian * Add comment to mention we are mirroring impl from cmath * Remove MSVC fallback that was inconsistent with every other impl * Add additional helpers for future work * Add test for static_assert support and some additional tests * Finalize implementation of log2 * Remove static_assert for now * Update README.md --- CMakeLists.txt | 2 +- CONTRIBUTING.md | 3 + README.md | 67 +++--- {ext => benchmark}/ccmath_benchmark_main.cpp | 0 ccmath_headers.cmake | 21 ++ ext/CMakeLists.txt | 7 - include/ccmath/detail/compare/isgreater.hpp | 33 +++ .../ccmath/detail/compare/isgreaterequal.hpp | 33 +++ include/ccmath/detail/compare/isless.hpp | 33 +++ include/ccmath/detail/compare/islessequal.hpp | 33 +++ .../ccmath/detail/compare/islessgreater.hpp | 30 +++ include/ccmath/detail/compare/isnormal.hpp | 27 +++ include/ccmath/detail/compare/isunordered.hpp | 44 ++++ include/ccmath/detail/compare/signbit.hpp | 95 +++------ .../detail/exponential/details/log2_data.hpp | 145 +++++++++++++ .../exponential/details/log2_double_impl.hpp | 160 +++++++++++++++ .../exponential/details/log2_float_impl.hpp | 113 +++++++++++ .../detail/exponential/details/log_data.hpp | 192 ++++++++++++++++++ .../exponential/details/log_double_impl.hpp | 155 ++++++++++++++ .../exponential/details/log_float_impl.hpp | 97 +++++++++ include/ccmath/detail/exponential/exp.hpp | 53 ++++- include/ccmath/detail/exponential/log.hpp | 64 ++++++ include/ccmath/detail/exponential/log2.hpp | 54 +++++ include/ccmath/detail/lerp.hpp | 35 ++++ include/ccmath/detail/nearest/floor.hpp | 65 ++++++ include/ccmath/detail/power/pow.hpp | 35 +++- include/ccmath/internal/helpers/bits.hpp | 88 ++++++++ .../helpers/exponentiation_helpers.hpp | 75 +++++++ .../ccmath/internal/helpers/find_number.hpp | 36 ++++ .../internal/helpers/floating_point_type.hpp | 41 ++++ .../internal/helpers/fpclassify_helper.hpp | 1 - include/ccmath/internal/helpers/is_odd.hpp | 19 ++ .../ccmath/internal/helpers/pow_integral.hpp | 88 ++++++++ include/ccmath/internal/predef/unlikely.hpp | 19 ++ include/ccmath/numbers.hpp | 75 +++++++ test/CMakeLists.txt | 44 +++- test/basic/abs_test.cpp | 3 + test/basic/fdim_test.cpp | 2 + test/basic/fma_test.cpp | 12 ++ test/basic/fmod_test.cpp | 8 +- test/basic/max_test.cpp | 3 + test/basic/min_test.cpp | 3 + test/basic/remainder_test.cpp | 2 + test/compare/fpclassify_test.cpp | 4 + test/compare/isfinite_test.cpp | 5 + test/compare/isgreater_test.cpp | 28 +++ test/compare/isgreaterequal_test.cpp | 28 +++ test/compare/isinf_test.cpp | 10 + test/compare/isless_test.cpp | 28 +++ test/compare/islessequal_test.cpp | 28 +++ test/compare/islessgreater_test.cpp | 27 +++ test/compare/isnan_test.cpp | 10 + test/compare/isnormal_test.cpp | 28 +++ test/compare/isunordered_test.cpp | 33 +++ test/compare/signbit_test.cpp | 3 + test/exponential/exp2_test.cpp | 18 ++ test/exponential/exp_test.cpp | 21 ++ test/exponential/expm1_test.cpp | 18 ++ test/exponential/log10_test.cpp | 18 ++ test/exponential/log1p_test.cpp | 18 ++ test/exponential/log2_test.cpp | 70 +++++++ test/exponential/log_test.cpp | 57 ++++++ test/fmanip/copysign_test.cpp | 19 ++ test/fmanip/frexp_test.cpp | 19 ++ test/fmanip/ilogb_test.cpp | 19 ++ test/fmanip/ldexp_test.cpp | 19 ++ test/fmanip/logb_test.cpp | 19 ++ test/fmanip/modf_test.cpp | 19 ++ test/fmanip/nextafter_test.cpp | 19 ++ test/fmanip/scalbn_test.cpp | 19 ++ test/hyperbolic/_test.cpp | 19 ++ 71 files changed, 2617 insertions(+), 121 deletions(-) create mode 100644 CONTRIBUTING.md rename {ext => benchmark}/ccmath_benchmark_main.cpp (100%) create mode 100644 include/ccmath/detail/exponential/details/log2_data.hpp create mode 100644 include/ccmath/detail/exponential/details/log2_double_impl.hpp create mode 100644 include/ccmath/detail/exponential/details/log2_float_impl.hpp create mode 100644 include/ccmath/detail/exponential/details/log_data.hpp create mode 100644 include/ccmath/detail/exponential/details/log_double_impl.hpp create mode 100644 include/ccmath/detail/exponential/details/log_float_impl.hpp create mode 100644 include/ccmath/internal/helpers/bits.hpp create mode 100644 include/ccmath/internal/helpers/exponentiation_helpers.hpp create mode 100644 include/ccmath/internal/helpers/find_number.hpp create mode 100644 include/ccmath/internal/helpers/floating_point_type.hpp create mode 100644 include/ccmath/internal/helpers/is_odd.hpp create mode 100644 include/ccmath/internal/helpers/pow_integral.hpp create mode 100644 include/ccmath/internal/predef/unlikely.hpp create mode 100644 include/ccmath/numbers.hpp create mode 100644 test/compare/isgreater_test.cpp create mode 100644 test/compare/isgreaterequal_test.cpp create mode 100644 test/compare/isless_test.cpp create mode 100644 test/compare/islessequal_test.cpp create mode 100644 test/compare/islessgreater_test.cpp create mode 100644 test/compare/isnormal_test.cpp create mode 100644 test/compare/isunordered_test.cpp create mode 100644 test/exponential/exp2_test.cpp create mode 100644 test/exponential/exp_test.cpp create mode 100644 test/exponential/expm1_test.cpp create mode 100644 test/exponential/log10_test.cpp create mode 100644 test/exponential/log1p_test.cpp create mode 100644 test/exponential/log2_test.cpp create mode 100644 test/exponential/log_test.cpp create mode 100644 test/fmanip/copysign_test.cpp create mode 100644 test/fmanip/frexp_test.cpp create mode 100644 test/fmanip/ilogb_test.cpp create mode 100644 test/fmanip/ldexp_test.cpp create mode 100644 test/fmanip/logb_test.cpp create mode 100644 test/fmanip/modf_test.cpp create mode 100644 test/fmanip/nextafter_test.cpp create mode 100644 test/fmanip/scalbn_test.cpp create mode 100644 test/hyperbolic/_test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b424855..a94d5765 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.18) enable_language(CXX) -set(CCMATH_BUILD_VERSION 0.1.0) +set(CCMATH_BUILD_VERSION 0.1.1) set(INTERNAL_PROJ_DEFAULT_NAME ccmath) project(${INTERNAL_PROJ_DEFAULT_NAME} VERSION ${CCMATH_BUILD_VERSION}) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..0d5b8e32 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,3 @@ +# Contribution Guidelines + +WIP diff --git a/README.md b/README.md index 870f1787..b0ee5938 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,11 @@ ccmath is a C++17 library that provides a re-implementation of the standard `` library, including trigonometric, exponential, logarithmic, and other common mathematical operations. If `` has it then it is likely ccmath has implemented it. -- **Performance Optimization**: By leveraging constexpr, ccmath aims to optimize performance by computing mathematical expressions at compile time where possible, reducing runtime overhead. +- **Performance Optimization**: By leveraging constexpr, ccmath aims to optimize performance by computing mathematical expressions at compile time when possible, reducing runtime overhead. - **No External Dependencies**: ccmath has no external dependencies and only requires a C++17-compliant compiler. @@ -33,40 +33,51 @@ int main() { ccmath has a comprehensive cmake setup and can be easily included in your project using fetchcontent like so: ```cmake +cmake_minimum_required(VERSION 3.11) # FetchContent is new in version 3.11. + include(FetchContent) FetchContent_Declare( ccmath GIT_REPOSITORY https://github.com/Rinzii/ccmath.git - GIT_TAG main + GIT_TAG v0.1.0 # Replace with the version you want to use ) FetchContent_MakeAvailable(ccmath) + +target_link_libraries(main PRIVATE ccmath::ccmath) ``` -## Compatability +## Compiler Support +* GCC 11.1+ +* Clang 9.0.0+ +* AppleClang 14.0.3+ (Lowest tested version) +* MSVC 19.26+ +* Intel DPC++ 2022.0.0+ +* Nvidia HPC SDK 22.7+ (Lowest tested version) -ccmath is designed to be compatible with any C++17-compliant compiler. It should work seamlessly with popular compilers such as GCC, Clang, and MSVC. +> [!NOTE] +> Currently working on finding manners to lower these requirements. ## Contributing -Contributions to ccmath are welcome! If you encounter any bugs, have suggestions for improvements, or would like to contribute new features, feel free to open an issue or submit a pull request! +CCmath is an open-source project, and it needs your help to go on growing and improving. If you want to get involved and suggest some additional features, file a bug report or submit a patch, please have a look at the contribution guidelines. -## Implementation Progress (Sub Sections) -| Section | % done | In Progress? | Notes? | Planned Completion Version | +## Implementation Progress (Modules) +| Module | % done | In Progress? | Notes? | Planned Completion Version | |--------------------|--------|--------------|---------------------------------------------------------------------------|----------------------------| -| Basic | 91 | ✓ | Remquo is being pushed back to a later release due to technical problems. | v0.1.0 | -| Compare | 40 | ✓ | | -| Exponential | 0 | | | +| Basic | 91 | | Remquo is being pushed back to a later release due to technical problems. | v0.1.0 (Released) | +| Compare | 100 | | | v0.2.0 | +| Exponential | 33 | ✓ | | v0.2.0 | | Float Manipulation | 0 | | | | Hyperbolic | 0 | | | | Nearest | 15 | | | | Power | 5 | | | | Special Functions | 0 | | | | Trigonometric | 0 | | | -| Misc Functions | 0 | | | +| Misc Functions | 10 | | | -> Last Updated: Mar 02, 2024 +> Last Updated: Mar 09, 2024 -## Implementation Progress (All Functions) +## Implementation Progress (Functions) | Feature | % done | TODO | |----------------|--------|------------------------------------------------------------------------------------------| @@ -79,22 +90,22 @@ Contributions to ccmath are welcome! If you encounter any bugs, have suggestions | remquo | 40 | Partially implemented, but being pushed back to later release due to technical problems. | | fpclassify | 100 | | | isfinite | 100 | | -| isgreater | 0 | Implement function | -| isgreaterequal | 0 | Implement function | -| isinf | 98 | Improve documentation | -| isless | 0 | Implement function | -| islessequal | 0 | Implement function | -| islessgreater | 0 | Implement function | -| isnan | 95 | Add more support for built-in functions and improve documentation | -| isnormal | 0 | Implement function | -| isunordered | 0 | Implement function | -| signbit | 90 | Add more fallbacks and builtin support if possible and improve reliability with MSVC | +| isgreater | 100 | | +| isgreaterequal | 100 | | +| isinf | 100 | Improve documentation | +| isless | 100 | | +| islessequal | 100 | | +| islessgreater | 100 | | +| isnan | 100 | Functional, need improved documentation and more test cases. | +| isnormal | 100 | | +| isunordered | 100 | | +| signbit | 100 | Need to find manner of implementing signbit on lower versions of MSVC. | | exp | 35 | Continue implementation process and add documentation and tests | | exp2 | 0 | Implement function | | expm1 | 0 | Implement function | -| log | 0 | Implement function | +| log | 100 | Functional, but fallbacks without requiring recent compiler versions is desired. | | log1p | 0 | Implement function | -| log2 | 0 | Implement function | +| log2 | 100 | Functional, but fallbacks without requiring recent compiler versions is desired. | | log10 | 0 | Implement function | | copysign | 0 | Implement function | | frexp | 0 | Implement function | @@ -149,10 +160,10 @@ Contributions to ccmath are welcome! If you encounter any bugs, have suggestions | sin | 0 | Implement function | | tan | 0 | Implement function | | gamma | 0 | Implement function | -| lerp | 0 | Implement function | +| lerp | 30 | Need to finish implementation process along with handling edge cases and promotion. | | lgamma | 0 | Implement function | -> Last Updated: Mar 02, 2024 +> Last Updated: Mar 09, 2024 ## License diff --git a/ext/ccmath_benchmark_main.cpp b/benchmark/ccmath_benchmark_main.cpp similarity index 100% rename from ext/ccmath_benchmark_main.cpp rename to benchmark/ccmath_benchmark_main.cpp diff --git a/ccmath_headers.cmake b/ccmath_headers.cmake index ce2cee5c..fbb8466e 100644 --- a/ccmath_headers.cmake +++ b/ccmath_headers.cmake @@ -5,6 +5,15 @@ set(ccmath_internal_helpers_headers ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/helpers/make_mantisa.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/helpers/not_null.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/helpers/fpclassify_helper.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/helpers/pow_integral.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/helpers/find_number.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/helpers/is_odd.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/helpers/exponentiation_helpers.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/helpers/bits.hpp +) + +set(ccmath_internal_predef_headers + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/predef/unlikely.hpp ) set(ccmath_internal_setup_headers @@ -27,6 +36,7 @@ set(ccmath_internal_utility_headers set(ccmath_internal_headers ${ccmath_internal_helpers_headers} + ${ccmath_internal_predef_headers} ${ccmath_internal_setup_headers} ${ccmath_internal_typetraits_headers} ${ccmath_internal_utility_headers} @@ -61,7 +71,17 @@ set(ccmath_detail_compare_headers ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/compare/signbit.hpp ) +set(ccmath_detail_exponential_details_headers + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/exponential/details/log_float_impl.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/exponential/details/log_double_impl.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/exponential/details/log_data.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/exponential/details/log2_float_impl.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/exponential/details/log2_double_impl.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/exponential/details/log2_data.hpp +) + set(ccmath_detail_exponential_headers + ${ccmath_detail_exponential_details_headers} ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/exponential/exp.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/exponential/exp2.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/exponential/expm1.hpp @@ -172,6 +192,7 @@ set(ccmath_root_headers ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/special.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/trig.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/ccmath.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/numbers.hpp ) set(ccmath_headers diff --git a/ext/CMakeLists.txt b/ext/CMakeLists.txt index b18d54e2..3c63687f 100644 --- a/ext/CMakeLists.txt +++ b/ext/CMakeLists.txt @@ -33,9 +33,6 @@ if (CCMATH_BUILD_TEST) endif() - -#find_package(Threads REQUIRED) - add_library(${PROJECT_NAME} INTERFACE) add_library(${INTERNAL_PROJ_DEFAULT_NAME}::ext ALIAS ${PROJECT_NAME}) @@ -45,7 +42,3 @@ if(CCMATH_BUILD_TEST) ) endif () -# TODO: May not actually require this. Later decide if I want to remove it. -target_link_libraries(${PROJECT_NAME} INTERFACE - Threads::Threads - ) diff --git a/include/ccmath/detail/compare/isgreater.hpp b/include/ccmath/detail/compare/isgreater.hpp index b950a84d..825c7aa3 100644 --- a/include/ccmath/detail/compare/isgreater.hpp +++ b/include/ccmath/detail/compare/isgreater.hpp @@ -8,7 +8,40 @@ #pragma once +#include + namespace ccm { + /** + * @brief Checks if the first argument is greater than the second. + * @tparam T Type of the values to compare. + * @param x A floating-point or integer value. + * @param y A floating-point or integer value. + * @return true if the first argument is greater than the second, false otherwise. + */ + template + inline constexpr bool isgreater(T x, T y) noexcept + { + return x > y; + } + + /** + * @brief Checks if the first argument is greater than the second. + * @tparam T Type of the left-hand side. + * @tparam U Type of the right-hand side. + * @param x Value of the left-hand side of the comparison. + * @param y Value of the right-hand side of the comparison. + * @return true if the first argument is greater than the second, false otherwise. + */ + template + inline constexpr bool isgreater(T x, U y) noexcept + { + // Find the common type of the two arguments + using shared_type = std::common_type_t; + // Then cast the arguments to the common type and call the single argument version + return static_cast(isgreater(static_cast(x), static_cast(y))); + } } // namespace ccm + +/// @ingroup compare diff --git a/include/ccmath/detail/compare/isgreaterequal.hpp b/include/ccmath/detail/compare/isgreaterequal.hpp index b950a84d..d04a0be0 100644 --- a/include/ccmath/detail/compare/isgreaterequal.hpp +++ b/include/ccmath/detail/compare/isgreaterequal.hpp @@ -8,7 +8,40 @@ #pragma once +#include + namespace ccm { + /** + * @brief Checks if the first argument is greater than or equal to the second. + * @tparam T Type of the values to compare. + * @param x A floating-point or integer value. + * @param y A floating-point or integer value. + * @return true if the first argument is greater than or equal to the second, false otherwise. + */ + template + inline constexpr bool isgreaterequal(T x, T y) noexcept + { + return x >= y; + } + + /** + * @brief Checks if the first argument is greater than or equal to the second. + * @tparam T Type of the left-hand side. + * @tparam U Type of the right-hand side. + * @param x Value of the left-hand side of the comparison. + * @param y Value of the right-hand side of the comparison. + * @return true if the first argument is greater than or equal to the second, false otherwise. + */ + template + inline constexpr bool isgreaterequal(T x, U y) noexcept + { + // Find the common type of the two arguments + using shared_type = std::common_type_t; + // Then cast the arguments to the common type and call the single argument version + return static_cast(isgreaterequal(static_cast(x), static_cast(y))); + } } // namespace ccm + +/// @ingroup compare diff --git a/include/ccmath/detail/compare/isless.hpp b/include/ccmath/detail/compare/isless.hpp index b950a84d..efe52915 100644 --- a/include/ccmath/detail/compare/isless.hpp +++ b/include/ccmath/detail/compare/isless.hpp @@ -8,7 +8,40 @@ #pragma once +#include + namespace ccm { + /** + * @brief Checks if the first argument is less than the second. + * @tparam T Type of the values to compare. + * @param x A floating-point or integer value. + * @param y A floating-point or integer value. + * @return true if the first argument is less than the second, false otherwise. + */ + template + inline constexpr bool isless(T x, T y) noexcept + { + return x < y; + } + + /** + * @brief Checks if the first argument is less than the second. + * @tparam T Type of the left-hand side. + * @tparam U Type of the right-hand side. + * @param x Value of the left-hand side of the comparison. + * @param y Value of the right-hand side of the comparison. + * @return true if the first argument is less than the second, false otherwise. + */ + template + inline constexpr bool isless(T x, U y) noexcept + { + // Find the common type of the two arguments + using shared_type = std::common_type_t; + // Then cast the arguments to the common type and call the single argument version + return static_cast(isless(static_cast(x), static_cast(y))); + } } // namespace ccm + +/// @ingroup compare diff --git a/include/ccmath/detail/compare/islessequal.hpp b/include/ccmath/detail/compare/islessequal.hpp index b950a84d..68c07754 100644 --- a/include/ccmath/detail/compare/islessequal.hpp +++ b/include/ccmath/detail/compare/islessequal.hpp @@ -8,7 +8,40 @@ #pragma once +#include + namespace ccm { + /** + * @brief Checks if the first argument is less than or equal to the second. + * @tparam T Type of the values to compare. + * @param x A floating-point or integer value. + * @param y A floating-point or integer value. + * @return true if the first argument is less than or equal to the second, false otherwise. + */ + template + inline constexpr bool islessequal(T x, T y) noexcept + { + return x <= y; + } + + /** + * @brief Checks if the first argument is less than or equal to the second. + * @tparam T Type of the left-hand side. + * @tparam U Type of the right-hand side. + * @param x Value of the left-hand side of the comparison. + * @param y Value of the right-hand side of the comparison. + * @return true if the first argument is less than or equal to the second, false otherwise. + */ + template + inline constexpr bool islessequal(T x, U y) noexcept + { + // Find the common type of the two arguments + using shared_type = std::common_type_t; + // Then cast the arguments to the common type and call the single argument version + return static_cast(islessequal(static_cast(x), static_cast(y))); + } } // namespace ccm + +/// @ingroup compare diff --git a/include/ccmath/detail/compare/islessgreater.hpp b/include/ccmath/detail/compare/islessgreater.hpp index b950a84d..0957b131 100644 --- a/include/ccmath/detail/compare/islessgreater.hpp +++ b/include/ccmath/detail/compare/islessgreater.hpp @@ -8,7 +8,37 @@ #pragma once +#include + namespace ccm { + /** + * @brief Checks if the first argument is less than the second or greater than the second. + * @tparam T Type of the values to compare. + * @param x A floating-point or integer value. + * @param y A floating-point or integer value. + * @return true if the first argument is less than the second or greater than the second, false otherwise. + */ + template + inline constexpr bool islessgreater(T x, T y) noexcept + { + return x < y || x > y; + } + /** + * @brief Checks if the first argument is less than the second or greater than the second. + * @tparam T Type of the left-hand side. + * @tparam U Type of the right-hand side. + * @param x Value of the left-hand side of the comparison. + * @param y Value of the right-hand side of the comparison. + * @return true if the first argument is less than the second or greater than the second, false otherwise. + */ + template + inline constexpr bool islessgreater(T x, U y) noexcept + { + using shared_type = std::common_type_t; + return static_cast(islessgreater(static_cast(x), static_cast(y))); + } } // namespace ccm + +/// @ingroup compare diff --git a/include/ccmath/detail/compare/isnormal.hpp b/include/ccmath/detail/compare/isnormal.hpp index b950a84d..8352f681 100644 --- a/include/ccmath/detail/compare/isnormal.hpp +++ b/include/ccmath/detail/compare/isnormal.hpp @@ -8,7 +8,34 @@ #pragma once +#include "ccmath/detail/compare/isinf.hpp" +#include "ccmath/detail/compare/isnan.hpp" +#include "ccmath/detail/basic/abs.hpp" + namespace ccm { + /** + * @brief Checks if the given number has a normal value. + * @tparam T The type of the number. + * @param x A floating-point or integer value + * @return true if the number has a normal value, false otherwise. + */ + template , bool> = true> + inline constexpr bool isnormal(T num) noexcept + { + return num != static_cast(0) && !ccm::isnan(num) && !ccm::isinf(num) && ccm::abs(num) >= std::numeric_limits::min(); + } + + /** + * @brief Checks if the given number has a normal value. + * @tparam Integer The type of the integer. + * @param x A integer value to check. + * @return true if the number has a normal value, false otherwise. + */ + template , bool> = true> + inline constexpr bool isnormal(Integer num) noexcept + { + return num != static_cast(0); + } } // namespace ccm diff --git a/include/ccmath/detail/compare/isunordered.hpp b/include/ccmath/detail/compare/isunordered.hpp index b950a84d..ddd7e20f 100644 --- a/include/ccmath/detail/compare/isunordered.hpp +++ b/include/ccmath/detail/compare/isunordered.hpp @@ -8,7 +8,51 @@ #pragma once +#include +#include "ccmath/detail/compare/isnan.hpp" + namespace ccm { + /** + * @brief Checks if the given number is unordered, that is, one or both are NaN and thus cannot be meaningfully compared with each other. + * @tparam T The type of the number. + * @param x A floating-point or integer value + * @param y A floating-point or integer value + * @return true if either x or y is NaN, false otherwise. + */ + template , bool> = true> + inline constexpr bool isunordered(T x, T y) noexcept + { + return ccm::isnan(x) || ccm::isnan(y); + } + + /** + * @brief Checks if the given number is unordered, that is, one or both are NaN and thus cannot be meaningfully compared with each other. + * @tparam Integer The type of the integer. + * @param x A integer value to check. + * @param y A integer value to check. + * @return false, as all integers are ordered. + */ + template , bool> = true> + inline constexpr bool isunordered(Integer x, Integer y) noexcept + { + return false; // All integers are ordered + } + /** + * @brief Checks if the given number is unordered, that is, one or both are NaN and thus cannot be meaningfully compared with each other. + * @tparam T Type of the left-hand side. + * @tparam U Type of the right-hand side. + * @param x Value of the left-hand side of the comparison. + * @param y Value of the right-hand side of the comparison. + * @return true if either x or y is NaN, false otherwise. + */ + template + inline constexpr bool isunordered(T x, U y) noexcept + { + using shared_type = std::common_type_t; + return static_cast(isunordered(static_cast(x), static_cast(y))); + } } // namespace ccm + +/// @ingroup compare diff --git a/include/ccmath/detail/compare/signbit.hpp b/include/ccmath/detail/compare/signbit.hpp index 6cc2b9d8..e0cec291 100644 --- a/include/ccmath/detail/compare/signbit.hpp +++ b/include/ccmath/detail/compare/signbit.hpp @@ -13,56 +13,36 @@ #include "ccmath/detail/compare/isnan.hpp" -#include "ccmath/internal/type_traits/floating_point_traits.hpp" +// Some compilers have __builtin_signbit which is constexpr for some compilers +#ifndef CCMATH_HAS_CONSTEXPR_BUILTIN_SIGNBIT + // GCC 6.1+ has constexpr __builtin_signbit that DOES allow static_assert. Clang & MSVC do not. + #if defined(__GNUC__) && (__GNUC__ >= 6 && __GNUC_MINOR__ >= 1) + #define CCMATH_HAS_CONSTEXPR_BUILTIN_SIGNBIT + #endif +#endif + +#if !defined(CCMATH_HAS_CONSTEXPR_BUILTIN_COPYSIGN) && !defined(CCMATH_HAS_CONSTEXPR_BUILTIN_SIGNBIT) + // GCC 6.1 has constexpr __builtin_copysign that DOES allow static_assert + #if defined(__GNUC__) && (__GNUC__ >= 6 && __GNUC_MINOR__ >= 1) + #define CCMATH_HAS_CONSTEXPR_BUILTIN_COPYSIGN + #endif -// If we have C++23, we can use std::signbit as it is constexpr -#if (defined(__cpp_lib_constexpr_cmath) && __cpp_lib_constexpr_cmath >= 202202L) - #include - #ifndef CCMATH_HAS_CONSTEXPR_SIGNBIT - #define CCMATH_HAS_CONSTEXPR_SIGNBIT + // Clang 5.0.0 has constexpr __builtin_copysign that DOES allow static_assert + #if defined(__clang__) && (__clang_major__ >= 5) + #define CCMATH_HAS_CONSTEXPR_BUILTIN_COPYSIGN #endif #endif // We only implement this for MSVC as that is the only manner to get constexpr signbit that is also static_assert-able -#ifndef CCMATH_HAS_BUILTIN_BIT_CAST +#if !defined(CCMATH_HAS_BUILTIN_BIT_CAST) && !defined(CCMATH_HAS_CONSTEXPR_BUILTIN_SIGNBIT) && !defined(CCMATH_HAS_CONSTEXPR_BUILTIN_COPYSIGN) #if (defined(_MSC_VER) && _MSC_VER >= 1927) #define CCMATH_HAS_BUILTIN_BIT_CAST + #include "ccmath/internal/type_traits/floating_point_traits.hpp" #include // for std::numeric_limits #include // for std::uint64_t #endif #endif -// Some compilers have __builtin_signbit which is constexpr for some compilers -#ifndef CCMATH_HAS_CONSTEXPR_BUILTIN_SIGNBIT - #if !defined(CCMATH_HAS_CONSTEXPR_SIGNBIT) - // GCC 6.1 has constexpr __builtin_signbit that DOES allow static_assert. Clang does not. - #if defined(__GNUC__) && (__GNUC__ == 6 && __GNUC_MINOR__ >= 1) - #define CCMATH_HAS_CONSTEXPR_BUILTIN_SIGNBIT - #endif - #endif -#endif - -#ifndef CCMATH_HAS_CONSTEXPR_BUILTIN_COPYSIGN - #if !defined(CCMATH_HAS_CONSTEXPR_SIGNBIT) - // GCC 6.1 has constexpr __builtin_copysign that DOES allow static_assert - #if defined(__GNUC__) && (__GNUC__ >= 6 && __GNUC_MINOR__ >= 1) - #define CCMATH_HAS_CONSTEXPR_BUILTIN_COPYSIGN - #endif - - // Clang 5.0.0 has constexpr __builtin_copysign that DOES allow static_assert - #if defined(__clang__) && (__clang_major__ >= 5) - #define CCMATH_HAS_CONSTEXPR_BUILTIN_COPYSIGN - #endif - #endif -#endif - -// This is are last resort for MSVC. -#if defined(_MSVC_VER) && !defined(CCMATH_HAS_CONSTEXPR_BUILTIN_BIT_CAST) && !defined(CCMATH_HAS_CONSTEXPR_SIGNBIT) && \ - !defined(CCMATH_HAS_CONSTEXPR_BUILTIN_COPYSIGN) - #define CCMATH_MSVC_DOES_NOT_HAVE_ASSERTABLE_CONSTEXPR_SIGNBIT - #include "float.h" // for _fpclass and _FPCLASS_NZ -#endif - namespace ccm { /** @@ -73,13 +53,10 @@ namespace ccm * * @note This function has multiple implementations based on the compiler and the version of * the the compiler used. With nearly all implementations, this function is fully constexpr and will return - * the same values as std::signbit along with being static_assert-able. The only exception is MSVC 19.26 and earlier - * and unknown compilers that support none of possible implementations. - * - * @note ccm::signbit by default will use std::signbit if using C++23 or later. + * the same values as std::signbit along with being static_assert-able. * - * @warning ccm::signbit will not work with static_assert on MSVC 19.26 and earlier. This is due to the fact that - * MSVC does not provide a constexpr signbit until 19.27. This is a limitation of MSVC and not ccmath. + * @warning ccm::signbit currently is only ensured to work on little-endian systems. There is currently no guarantee this it will work on big-endian + * systems. * * @attention Implementing signbit is a non-trivial task and requires a lot of compiler magic to allow for ccm::signbit to be * fully constexpr and static_assert-able while also conforming to the standard. CCMath has done its best to provide @@ -90,19 +67,8 @@ namespace ccm template ::value, int> = 0> [[nodiscard]] inline constexpr bool signbit(T x) noexcept { -#if defined(CCMATH_HAS_CONSTEXPR_SIGNBIT) - return std::signbit(x); -#elif defined(CCMATH_HAS_CONSTEXPR_BUILTIN_SIGNBIT) +#if defined(CCMATH_HAS_CONSTEXPR_BUILTIN_SIGNBIT) return __builtin_signbit(x); -#elif defined(CCMATH_HAS_BUILTIN_BIT_CAST) - // Check for the sign of +0.0 and -0.0 with __builtin_bit_cast - if (x == static_cast(0) || ccm::isnan(x)) - { - const auto bits = __builtin_bit_cast(helpers::float_bits_t, x); - return (bits & helpers::sign_mask_v) != 0; - } - - return x < static_cast(0); #elif defined(CCMATH_HAS_CONSTEXPR_BUILTIN_COPYSIGN) // use __builtin_copysign to check for the sign of zero if (x == static_cast(0) || ccm::isnan(x)) @@ -118,10 +84,15 @@ namespace ccm } return x < static_cast(0); -#elif defined(CCMATH_MSVC_DOES_NOT_HAVE_ASSERTABLE_CONSTEXPR_SIGNBIT) - // If we don't have access to MSBC 19.27 or later, we can use _fpclass and _FPCLASS_NZ to - // check for the sign of zero. This is is constexpr, but it is not static_assert-able. - return ((x == static_cast(0)) ? (_fpclass(x) == _FPCLASS_NZ) : (x < T(0))); // This won't work in static assertions +#elif defined(CCMATH_HAS_BUILTIN_BIT_CAST) + // Check for the sign of +0.0 and -0.0 with __builtin_bit_cast + if (x == static_cast(0) || ccm::isnan(x)) + { + const auto bits = __builtin_bit_cast(helpers::float_bits_t, x); + return (bits & helpers::sign_mask_v) != 0; + } + + return x < static_cast(0); #else static_assert(false, "ccm::signbit is not implemented for this compiler. Please report this issue to the dev!"); return false; @@ -176,7 +147,3 @@ namespace ccm #ifdef CCMATH_HAS_CONSTEXPR_BUILTIN_COPYSIGN #undef CCMATH_HAS_CONSTEXPR_BUILTIN_COPYSIGN #endif - -#ifdef CCMATH_MSVC_DOES_NOT_HAVE_ASSERTABLE_CONSTEXPR_SIGNBIT - #undef CCMATH_MSVC_DOES_NOT_HAVE_ASSERTABLE_CONSTEXPR_SIGNBIT -#endif diff --git a/include/ccmath/detail/exponential/details/log2_data.hpp b/include/ccmath/detail/exponential/details/log2_data.hpp new file mode 100644 index 00000000..12f64ffe --- /dev/null +++ b/include/ccmath/detail/exponential/details/log2_data.hpp @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2024-Present Ian Pike + * Copyright (c) 2024-Present ccmath contributors + * + * This library is provided under the MIT License. + * See LICENSE for more information. + */ + +#pragma once + +#include +#include + +namespace ccm::internal +{ + // Values borrowed from glibc + + // Float constants + constexpr std::size_t k_log2TableBitsFlt = 4; + constexpr std::size_t k_log2PolyOrderFlt = 4; + + // Double constants + constexpr std::size_t k_log2TableBitsDbl = 6; + constexpr std::size_t k_log2PolyOrderDbl = 7; + constexpr std::size_t k_log2Poly1OrderDbl = 11; + + template , int> = 0> + struct log2_data; + + template <> + struct log2_data + { + struct + { + double invc; + double logc; + } tab[1 << k_log2TableBitsFlt] = { + { 0x1.661ec79f8f3bep+0, -0x1.efec65b963019p-2 }, + { 0x1.571ed4aaf883dp+0, -0x1.b0b6832d4fca4p-2 }, + { 0x1.49539f0f010bp+0, -0x1.7418b0a1fb77bp-2 }, + { 0x1.3c995b0b80385p+0, -0x1.39de91a6dcf7bp-2 }, + { 0x1.30d190c8864a5p+0, -0x1.01d9bf3f2b631p-2 }, + { 0x1.25e227b0b8eap+0, -0x1.97c1d1b3b7afp-3 }, + { 0x1.1bb4a4a1a343fp+0, -0x1.2f9e393af3c9fp-3 }, + { 0x1.12358f08ae5bap+0, -0x1.960cbbf788d5cp-4 }, + { 0x1.0953f419900a7p+0, -0x1.a6f9db6475fcep-5 }, + { 0x1p+0, 0x0p+0 }, + { 0x1.e608cfd9a47acp-1, 0x1.338ca9f24f53dp-4 }, + { 0x1.ca4b31f026aap-1, 0x1.476a9543891bap-3 }, + { 0x1.b2036576afce6p-1, 0x1.e840b4ac4e4d2p-3 }, + { 0x1.9c2d163a1aa2dp-1, 0x1.40645f0c6651cp-2 }, + { 0x1.886e6037841edp-1, 0x1.88e9c2c1b9ff8p-2 }, + { 0x1.767dcf5534862p-1, 0x1.ce0a44eb17bccp-2 }, + }; + + double poly[k_log2PolyOrderFlt] = { + -0x1.712b6f70a7e4dp-2, 0x1.ecabf496832ep-2, -0x1.715479ffae3dep-1, + 0x1.715475f35c8b8p0, + }; + }; + + template <> + struct log2_data + { + // First coefficient: 0x1.71547652b82fe1777d0ffda0d24p0 + double invln2hi{0x1.7154765200000p+0}; + double invln2lo{0x1.705fc2eefa200p-33}; + + // relative error: 0x1.a72c2bf8p-58 + // abs error: 0x1.67a552c8p-66 + // in -0x1.f45p-8 0x1.f45p-8 + std::array poly{ + -0x1.71547652b8339p-1, 0x1.ec709dc3a04bep-2, -0x1.7154764702ffbp-2, 0x1.2776c50034c48p-2, -0x1.ec7b328ea92bcp-3, 0x1.a6225e117f92ep-3, + }; + + // relative error: 0x1.2fad8188p-63 + // in -0x1.5b51p-5 0x1.6ab2p-5 + std::array poly1{ + -0x1.71547652b82fep-1, 0x1.ec709dc3a03f7p-2, -0x1.71547652b7c3fp-2, 0x1.2776c50f05be4p-2, -0x1.ec709dd768fe5p-3, + 0x1.a61761ec4e736p-3, -0x1.7153fbc64a79bp-3, 0x1.484d154f01b4ap-3, -0x1.289e4a72c383cp-3, 0x1.0b32f285aee66p-3, + }; + struct + { + double invc; + double logc; + } tab[1 << k_log2TableBitsDbl] = { + {0x1.724286bb1acf8p+0, -0x1.1095feecdb000p-1}, {0x1.6e1f766d2cca1p+0, -0x1.08494bd76d000p-1}, {0x1.6a13d0e30d48ap+0, -0x1.00143aee8f800p-1}, + {0x1.661ec32d06c85p+0, -0x1.efec5360b4000p-2}, {0x1.623fa951198f8p+0, -0x1.dfdd91ab7e000p-2}, {0x1.5e75ba4cf026cp+0, -0x1.cffae0cc79000p-2}, + {0x1.5ac055a214fb8p+0, -0x1.c043811fda000p-2}, {0x1.571ed0f166e1ep+0, -0x1.b0b67323ae000p-2}, {0x1.53909590bf835p+0, -0x1.a152f5a2db000p-2}, + {0x1.5014fed61adddp+0, -0x1.9217f5af86000p-2}, {0x1.4cab88e487bd0p+0, -0x1.8304db0719000p-2}, {0x1.49539b4334feep+0, -0x1.74189f9a9e000p-2}, + {0x1.460cbdfafd569p+0, -0x1.6552bb5199000p-2}, {0x1.42d664ee4b953p+0, -0x1.56b23a29b1000p-2}, {0x1.3fb01111dd8a6p+0, -0x1.483650f5fa000p-2}, + {0x1.3c995b70c5836p+0, -0x1.39de937f6a000p-2}, {0x1.3991c4ab6fd4ap+0, -0x1.2baa1538d6000p-2}, {0x1.3698e0ce099b5p+0, -0x1.1d98340ca4000p-2}, + {0x1.33ae48213e7b2p+0, -0x1.0fa853a40e000p-2}, {0x1.30d191985bdb1p+0, -0x1.01d9c32e73000p-2}, {0x1.2e025cab271d7p+0, -0x1.e857da2fa6000p-3}, + {0x1.2b404cf13cd82p+0, -0x1.cd3c8633d8000p-3}, {0x1.288b02c7ccb50p+0, -0x1.b26034c14a000p-3}, {0x1.25e2263944de5p+0, -0x1.97c1c2f4fe000p-3}, + {0x1.234563d8615b1p+0, -0x1.7d6023f800000p-3}, {0x1.20b46e33eaf38p+0, -0x1.633a71a05e000p-3}, {0x1.1e2eefdcda3ddp+0, -0x1.494f5e9570000p-3}, + {0x1.1bb4a580b3930p+0, -0x1.2f9e424e0a000p-3}, {0x1.19453847f2200p+0, -0x1.162595afdc000p-3}, {0x1.16e06c0d5d73cp+0, -0x1.f9c9a75bd8000p-4}, + {0x1.1485f47b7e4c2p+0, -0x1.c7b575bf9c000p-4}, {0x1.12358ad0085d1p+0, -0x1.960c60ff48000p-4}, {0x1.0fef00f532227p+0, -0x1.64ce247b60000p-4}, + {0x1.0db2077d03a8fp+0, -0x1.33f78b2014000p-4}, {0x1.0b7e6d65980d9p+0, -0x1.0387d1a42c000p-4}, {0x1.0953efe7b408dp+0, -0x1.a6f9208b50000p-5}, + {0x1.07325cac53b83p+0, -0x1.47a954f770000p-5}, {0x1.05197e40d1b5cp+0, -0x1.d23a8c50c0000p-6}, {0x1.03091c1208ea2p+0, -0x1.16a2629780000p-6}, + {0x1.0101025b37e21p+0, -0x1.720f8d8e80000p-8}, {0x1.fc07ef9caa76bp-1, 0x1.6fe53b1500000p-7}, {0x1.f4465d3f6f184p-1, 0x1.11ccce10f8000p-5}, + {0x1.ecc079f84107fp-1, 0x1.c4dfc8c8b8000p-5}, {0x1.e573a99975ae8p-1, 0x1.3aa321e574000p-4}, {0x1.de5d6f0bd3de6p-1, 0x1.918a0d08b8000p-4}, + {0x1.d77b681ff38b3p-1, 0x1.e72e9da044000p-4}, {0x1.d0cb5724de943p-1, 0x1.1dcd2507f6000p-3}, {0x1.ca4b2dc0e7563p-1, 0x1.476ab03dea000p-3}, + {0x1.c3f8ee8d6cb51p-1, 0x1.7074377e22000p-3}, {0x1.bdd2b4f020c4cp-1, 0x1.98ede8ba94000p-3}, {0x1.b7d6c006015cap-1, 0x1.c0db86ad2e000p-3}, + {0x1.b20366e2e338fp-1, 0x1.e840aafcee000p-3}, {0x1.ac57026295039p-1, 0x1.0790ab4678000p-2}, {0x1.a6d01bc2731ddp-1, 0x1.1ac056801c000p-2}, + {0x1.a16d3bc3ff18bp-1, 0x1.2db11d4fee000p-2}, {0x1.9c2d14967feadp-1, 0x1.406464ec58000p-2}, {0x1.970e4f47c9902p-1, 0x1.52dbe093af000p-2}, + {0x1.920fb3982bcf2p-1, 0x1.651902050d000p-2}, {0x1.8d30187f759f1p-1, 0x1.771d2cdeaf000p-2}, {0x1.886e5ebb9f66dp-1, 0x1.88e9c857d9000p-2}, + {0x1.83c97b658b994p-1, 0x1.9a80155e16000p-2}, {0x1.7f405ffc61022p-1, 0x1.abe186ed3d000p-2}, {0x1.7ad22181415cap-1, 0x1.bd0f2aea0e000p-2}, + {0x1.767dcf99eff8cp-1, 0x1.ce0a43dbf4000p-2}, + }; + + struct + { + double chi; + double clo; + } tab2[1 << k_log2TableBitsDbl] = { + {0x1.6200012b90a8ep-1, 0x1.904ab0644b605p-55}, {0x1.66000045734a6p-1, 0x1.1ff9bea62f7a9p-57}, {0x1.69fffc325f2c5p-1, 0x1.27ecfcb3c90bap-55}, + {0x1.6e00038b95a04p-1, 0x1.8ff8856739326p-55}, {0x1.71fffe09994e3p-1, 0x1.afd40275f82b1p-55}, {0x1.7600015590e1p-1, -0x1.2fd75b4238341p-56}, + {0x1.7a00012655bd5p-1, 0x1.808e67c242b76p-56}, {0x1.7e0003259e9a6p-1, -0x1.208e426f622b7p-57}, {0x1.81fffedb4b2d2p-1, -0x1.402461ea5c92fp-55}, + {0x1.860002dfafcc3p-1, 0x1.df7f4a2f29a1fp-57}, {0x1.89ffff78c6b5p-1, -0x1.e0453094995fdp-55}, {0x1.8e00039671566p-1, -0x1.a04f3bec77b45p-55}, + {0x1.91fffe2bf1745p-1, -0x1.7fa34400e203cp-56}, {0x1.95fffcc5c9fd1p-1, -0x1.6ff8005a0695dp-56}, {0x1.9a0003bba4767p-1, 0x1.0f8c4c4ec7e03p-56}, + {0x1.9dfffe7b92da5p-1, 0x1.e7fd9478c4602p-55}, {0x1.a1fffd72efdafp-1, -0x1.a0c554dcdae7ep-57}, {0x1.a5fffde04ff95p-1, 0x1.67da98ce9b26bp-55}, + {0x1.a9fffca5e8d2bp-1, -0x1.284c9b54c13dep-55}, {0x1.adfffddad03eap-1, 0x1.812c8ea602e3cp-58}, {0x1.b1ffff10d3d4dp-1, -0x1.efaddad27789cp-55}, + {0x1.b5fffce21165ap-1, 0x1.3cb1719c61237p-58}, {0x1.b9fffd950e674p-1, 0x1.3f7d94194cep-56}, {0x1.be000139ca8afp-1, 0x1.50ac4215d9bcp-56}, + {0x1.c20005b46df99p-1, 0x1.beea653e9c1c9p-57}, {0x1.c600040b9f7aep-1, -0x1.c079f274a70d6p-56}, {0x1.ca0006255fd8ap-1, -0x1.a0b4076e84c1fp-56}, + {0x1.cdfffd94c095dp-1, 0x1.8f933f99ab5d7p-55}, {0x1.d1ffff975d6cfp-1, -0x1.82c08665fe1bep-58}, {0x1.d5fffa2561c93p-1, -0x1.b04289bd295f3p-56}, + {0x1.d9fff9d228b0cp-1, 0x1.70251340fa236p-55}, {0x1.de00065bc7e16p-1, -0x1.5011e16a4d80cp-56}, {0x1.e200002f64791p-1, 0x1.9802f09ef62ep-55}, + {0x1.e600057d7a6d8p-1, -0x1.e0b75580cf7fap-56}, {0x1.ea00027edc00cp-1, -0x1.c848309459811p-55}, {0x1.ee0006cf5cb7cp-1, -0x1.f8027951576f4p-55}, + {0x1.f2000782b7dccp-1, -0x1.f81d97274538fp-55}, {0x1.f6000260c450ap-1, -0x1.071002727ffdcp-59}, {0x1.f9fffe88cd533p-1, -0x1.81bdce1fda8bp-58}, + {0x1.fdfffd50f8689p-1, 0x1.7f91acb918e6ep-55}, {0x1.0200004292367p+0, 0x1.b7ff365324681p-54}, {0x1.05fffe3e3d668p+0, 0x1.6fa08ddae957bp-55}, + {0x1.0a0000a85a757p+0, -0x1.7e2de80d3fb91p-58}, {0x1.0e0001a5f3fccp+0, -0x1.1823305c5f014p-54}, {0x1.11ffff8afbaf5p+0, -0x1.bfabb6680bac2p-55}, + {0x1.15fffe54d91adp+0, -0x1.d7f121737e7efp-54}, {0x1.1a00011ac36e1p+0, 0x1.c000a0516f5ffp-54}, {0x1.1e00019c84248p+0, -0x1.082fbe4da5dap-54}, + {0x1.220000ffe5e6ep+0, -0x1.8fdd04c9cfb43p-55}, {0x1.26000269fd891p+0, 0x1.cfe2a7994d182p-55}, {0x1.2a00029a6e6dap+0, -0x1.00273715e8bc5p-56}, + {0x1.2dfffe0293e39p+0, 0x1.b7c39dab2a6f9p-54}, {0x1.31ffff7dcf082p+0, 0x1.df1336edc5254p-56}, {0x1.35ffff05a8b6p+0, -0x1.e03564ccd31ebp-54}, + {0x1.3a0002e0eaeccp+0, 0x1.5f0e74bd3a477p-56}, {0x1.3e000043bb236p+0, 0x1.c7dcb149d8833p-54}, {0x1.4200002d187ffp+0, 0x1.e08afcf2d3d28p-56}, + {0x1.460000d387cb1p+0, 0x1.20837856599a6p-55}, {0x1.4a00004569f89p+0, -0x1.9fa5c904fbcd2p-55}, {0x1.4e000043543f3p+0, -0x1.81125ed175329p-56}, + {0x1.51fffcc027f0fp+0, 0x1.883d8847754dcp-54}, {0x1.55ffffd87b36fp+0, -0x1.709e731d02807p-55}, {0x1.59ffff21df7bap+0, 0x1.7f79f68727b02p-55}, + {0x1.5dfffebfc3481p+0, -0x1.180902e30e93ep-54}, + }; + }; + + template <> + struct log2_data : log2_data + { + }; +} // namespace ccm::internal diff --git a/include/ccmath/detail/exponential/details/log2_double_impl.hpp b/include/ccmath/detail/exponential/details/log2_double_impl.hpp new file mode 100644 index 00000000..04ea97bd --- /dev/null +++ b/include/ccmath/detail/exponential/details/log2_double_impl.hpp @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2024-Present Ian Pike + * Copyright (c) 2024-Present ccmath contributors + * + * This library is provided under the MIT License. + * See LICENSE for more information. + */ + +#pragma once + +#include "ccmath/detail/compare/isnan.hpp" + +#include +#include +#include +#include + +#include "ccmath/detail/exponential/details/log2_data.hpp" +#include "ccmath/internal/helpers/bits.hpp" +#include "ccmath/internal/helpers/floating_point_type.hpp" +#include "ccmath/internal/predef/unlikely.hpp" + +namespace ccm::internal +{ + namespace + { + namespace impl + { + constexpr ccm::internal::log2_data internalLogDataDbl = ccm::internal::log2_data(); + constexpr auto tab_values_dbl = internalLogDataDbl.tab; + constexpr auto tab2_values_dbl = internalLogDataDbl.tab2; + constexpr auto poly_values_dbl = internalLogDataDbl.poly; + constexpr auto poly1_values_dbl = internalLogDataDbl.poly1; + constexpr auto inverse_ln2_high_value_dbl = internalLogDataDbl.invln2hi; + constexpr auto inverse_ln2_low_value_dbl = internalLogDataDbl.invln2lo; + constexpr auto k_logTableN_dbl = (1 << ccm::internal::k_log2TableBitsDbl); + constexpr auto k_logTableOff_dbl = 0x3fe6000000000000; + + inline constexpr double log2_double_impl(double x) + { + // double_t tends to have better performance on platforms with FLT_EVAL_METHOD==2. + ccm::double_t normVal{}; + ccm::double_t rem{}; + ccm::double_t remSqr{}; + ccm::double_t remQuad{}; + ccm::double_t result{}; + ccm::double_t inverseCoeff{}; + ccm::double_t logarithmCoeff{}; + ccm::double_t expoDbl{}; + ccm::double_t highPart{}; + ccm::double_t lowPart{}; + ccm::double_t remHighPart{}; + ccm::double_t remLowPart{}; + ccm::double_t logExpoSum{}; + ccm::double_t polynomialTerm{}; + + std::uint64_t intX{}; + std::uint64_t intNorm{}; + std::uint64_t tmp{}; + + std::uint32_t top{}; + + int expo{}; + int i{}; + + intX = ccm::helpers::double_to_uint64(x); + top = ccm::helpers::top16_bits_of_double(x); + + constexpr std::uint64_t low = ccm::helpers::double_to_uint64(1.0 - 0x1.5b51p-5); + constexpr std::uint64_t high = ccm::helpers::double_to_uint64(1.0 + 0x1.6ab2p-5); + + // Handle special cases where x is very close to 1.0 + if (CCM_UNLIKELY(intX - low < high - low)) + { + // Handle the case where x is exactly 1.0 + if (CCM_UNLIKELY(intX == ccm::helpers::double_to_uint64(1.0))) { return 0.0; } + + rem = x - 1.0; + + ccm::double_t remHi{}; + ccm::double_t remLo{}; + remHi = ccm::helpers::uint64_to_double(ccm::helpers::double_to_uint64(rem) & -1ULL << 32); + remLo = rem - remHi; + highPart = remHi * ccm::internal::impl::inverse_ln2_high_value_dbl; + lowPart = remLo * ccm::internal::impl::inverse_ln2_high_value_dbl + rem * ccm::internal::impl::inverse_ln2_low_value_dbl; + + remSqr = rem * rem; // rounding error: 0x1p-62. + remQuad = remSqr * remSqr; + + // Worst-case error is less than 0.55 ULP + polynomialTerm = remSqr * (poly1_values_dbl[0] + rem * poly1_values_dbl[1]); + result = highPart + polynomialTerm; + lowPart += highPart - result + polynomialTerm; + lowPart += + remQuad * (poly1_values_dbl[2] + rem * poly1_values_dbl[3] + remSqr * (poly1_values_dbl[4] + rem * poly1_values_dbl[5]) + + remQuad * (poly1_values_dbl[6] + rem * poly1_values_dbl[7] + remSqr * (poly1_values_dbl[8] + rem * poly1_values_dbl[9]))); + result += lowPart; + + return result; + } + + if (CCM_UNLIKELY(top - 0x0010 >= 0x7ff0 - 0x0010)) + { + + // x is subnormal, normalize it. + intX = ccm::helpers::double_to_uint64(x * 0x1p52); // 0x1p52 = 4.5036e+15 = 2^52 + intX -= 52ULL << 52; + } + + // x = 2^expo normVal; where normVal is in range [k_logTableOff_dbl, 2 * k_logTableOff_dbl) and exact. + // The range is split into N sub-intervals. + // The ith sub-interval contains normVal and c is near its center. + tmp = intX - k_logTableOff_dbl; + // NOLINTBEGIN + i = (tmp >> (52 - ccm::internal::k_log2TableBitsDbl)) % k_logTableN_dbl; + expo = static_cast(tmp) >> 52; // Arithmetic shift. + // NOLINTEND + intNorm = intX - (tmp & 0xfffULL << 52); + inverseCoeff = tab_values_dbl[i].invc; + logarithmCoeff = tab_values_dbl[i].logc; + normVal = ccm::helpers::uint64_to_double(intNorm); + expoDbl = static_cast(expo); + + ccm::double_t remHi{}; + ccm::double_t remLo{}; + + // rounding error: 0x1p-55/N + 0x1p-65. + rem = (normVal - tab2_values_dbl[i].chi - tab2_values_dbl[i].clo) * inverseCoeff; + remHi = ccm::helpers::uint64_to_double(ccm::helpers::double_to_uint64(rem) & -1ULL << 32); + remLo = rem - remHi; + remHighPart = remHi * inverse_ln2_high_value_dbl; + remLowPart = remLo * inverse_ln2_high_value_dbl + rem * inverse_ln2_low_value_dbl; + + // hi + lo = rem/ln2 + log2(c) + expo + logExpoSum = expoDbl + logarithmCoeff; + highPart = logExpoSum + remHighPart; + lowPart = logExpoSum - highPart + remHighPart + remLowPart; + + // log2(rem+1) = rem/ln2 + rem^2*poly(rem) + // Evaluation is optimized assuming super scalar pipelined execution. + remSqr = rem * rem; // rounding error: 0x1p-62. + remQuad = remSqr * remSqr; + + // Worst-case error if |result| > 0x1p-4: 0.550 ULP. + // ~ 0.5 + 2/N/ln2 + abs-poly-error*0x1p56+0.003 ULP. + polynomialTerm = poly_values_dbl[0] + rem * poly_values_dbl[1] + remSqr * (poly_values_dbl[2] + rem * poly_values_dbl[3]) + + remQuad * (poly_values_dbl[4] + rem * poly_values_dbl[5]); + result = lowPart + remSqr * polynomialTerm + highPart; + + return result; + } + } // namespace impl + } // namespace + + template + [[nodiscard]] inline constexpr T log2_double(T num) noexcept + { + return static_cast(impl::log2_double_impl(static_cast(num))); + } +} // namespace ccm::internal diff --git a/include/ccmath/detail/exponential/details/log2_float_impl.hpp b/include/ccmath/detail/exponential/details/log2_float_impl.hpp new file mode 100644 index 00000000..54f43a65 --- /dev/null +++ b/include/ccmath/detail/exponential/details/log2_float_impl.hpp @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2024-Present Ian Pike + * Copyright (c) 2024-Present ccmath contributors + * + * This library is provided under the MIT License. + * See LICENSE for more information. + */ + +#pragma once + +#include "ccmath/detail/compare/isnan.hpp" + +#include +#include +#include +#include + +#include "ccmath/internal/helpers/bits.hpp" +#include "ccmath/internal/helpers/floating_point_type.hpp" +#include "ccmath/internal/predef/unlikely.hpp" + +#include "log2_data.hpp" + +namespace ccm::internal +{ + namespace + { + namespace impl + { + constexpr ccm::internal::log2_data internalLogDataFlt = ccm::internal::log2_data(); + constexpr auto tab_values_flt = internalLogDataFlt.tab; + constexpr auto poly_values_flt = internalLogDataFlt.poly; + constexpr auto k_logTableN_flt = (1 << ccm::internal::k_log2TableBitsFlt); + constexpr auto k_logTableOff_flt = 0x3f330000; + + inline constexpr double log2_float_impl(float x) + { + ccm::double_t normVal{}; + ccm::double_t rem{}; + ccm::double_t remSqr{}; + ccm::double_t result{}; + ccm::double_t result0{}; + ccm::double_t polynomialTerm{}; + ccm::double_t inverseCoeff{}; + ccm::double_t logarithmCoeff{}; + + std::uint32_t intX{}; + std::uint32_t intNorm{}; + std::uint32_t tmp{}; + std::uint32_t top{}; + + int expo{}; + int i{}; + + intX = ccm::helpers::float_to_uint32(x); + + // If x == 1 then fix the result to 0 with downward rounding + if (CCM_UNLIKELY(intX == 0x3f800000)) + { + return 0; + } + + if (CCM_UNLIKELY(intX - 0x00800000 >= 0x7f800000 - 0x00800000)) + { + // TODO: Maybe handle division by zero here? + + // If x is NaN, return NaN + if (intX & 0x80000000 || intX * 2 >= 0xff000000) + { + float invalidResult = (x - x) / (x - x); + return invalidResult; + } + + // If x is subnormal, normalize it + intX = ccm::helpers::float_to_uint32(x * 0x1p23f); + intX -= 23 << 23; + } + + // x = 2^expo * normVal; where normVal is in range [k_logTableOff_flt, 2 * k_logTableOff_flt] and exact. + // We split the rang into N sub-intervals. + // The i-th sub-interval contains normVal and c is near its center. + tmp = intX - k_logTableOff_flt; + i = (tmp >> (23 - ccm::internal::k_log2TableBitsFlt)) % k_logTableN_flt; // NOLINT + top = tmp & 0xff800000; + intNorm = intX - top; + // NOLINTNEXTLINE + expo = static_cast(tmp) >> 23; // Arithmetic shift. + inverseCoeff = tab_values_flt[i].invc; + logarithmCoeff = tab_values_flt[i].logc; + normVal = static_cast(ccm::helpers::uint32_to_float(intNorm)); + + // log2(x) = log1p(normVal/c-1)/ln2 + log2(c) + expo + rem = normVal * inverseCoeff - 1.0; + result0 = logarithmCoeff + static_cast(expo); + + // Pipelined polynomial evaluation to approximate log1p(r)/ln2. + remSqr = rem * rem; + result = poly_values_flt[1] * rem + poly_values_flt[2]; + result = poly_values_flt[0] * remSqr + result; + polynomialTerm = poly_values_flt[3] * rem + result0; + result = result * remSqr + polynomialTerm; + + return result; + } + } + } + + template + [[nodiscard]] inline constexpr T log2_float(T num) noexcept + { + return static_cast(impl::log2_float_impl(static_cast(num))); + } +} diff --git a/include/ccmath/detail/exponential/details/log_data.hpp b/include/ccmath/detail/exponential/details/log_data.hpp new file mode 100644 index 00000000..7ada8fc4 --- /dev/null +++ b/include/ccmath/detail/exponential/details/log_data.hpp @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2024-Present Ian Pike + * Copyright (c) 2024-Present ccmath contributors + * + * This library is provided under the MIT License. + * See LICENSE for more information. + */ + +#pragma once + +#include +#include + +namespace ccm::internal +{ + // Values borrowed from glibc + + // Float constants + constexpr std::size_t k_logTableBitsFlt = 4; + constexpr std::size_t k_logPolyOrderFlt = 4; + + // Double constants + constexpr std::size_t k_logTableBitsDbl = 7; + constexpr std::size_t k_logPolyOrderDbl = 6; + constexpr std::size_t k_logPoly1OrderDbl = 12; + + template , int> = 0> + struct log_data; + + template <> + struct log_data + { + struct + { + double invc; + double logc; + } tab[1 << k_logTableBitsFlt] = { + {0x1.661ec79f8f3bep+0, -0x1.57bf7808caadep-2}, {0x1.571ed4aaf883dp+0, -0x1.2bef0a7c06ddbp-2}, + {0x1.49539f0f010bp+0, -0x1.01eae7f513a67p-2}, {0x1.3c995b0b80385p+0, -0x1.b31d8a68224e9p-3}, + {0x1.30d190c8864a5p+0, -0x1.6574f0ac07758p-3}, {0x1.25e227b0b8eap+0, -0x1.1aa2bc79c81p-3}, + {0x1.1bb4a4a1a343fp+0, -0x1.a4e76ce8c0e5ep-4}, {0x1.12358f08ae5bap+0, -0x1.1973c5a611cccp-4}, + {0x1.0953f419900a7p+0, -0x1.252f438e10c1ep-5}, {0x1p+0, 0x0p+0}, + {0x1.e608cfd9a47acp-1, 0x1.aa5aa5df25984p-5}, {0x1.ca4b31f026aap-1, 0x1.c5e53aa362eb4p-4}, + {0x1.b2036576afce6p-1, 0x1.526e57720db08p-3}, {0x1.9c2d163a1aa2dp-1, 0x1.bc2860d22477p-3}, + {0x1.886e6037841edp-1, 0x1.1058bc8a07ee1p-2}, {0x1.767dcf5534862p-1, 0x1.4043057b6ee09p-2}, + + }; + + double ln2 = 0x1.62e42fefa39efp-1; + + // First order polynomial coefficient is 1. + double poly[k_logPolyOrderFlt - 1] = { + -0x1.00ea348b88334p-2, + 0x1.5575b0be00b6ap-2, + -0x1.ffffef20a4123p-2, + }; + }; + + template <> + struct log_data + { + double ln2hi{0x1.62e42fefa3800p-1}; + double ln2lo{0x1.ef35793c76730p-45}; + + // relative error: 0x1.926199e8p-56 + // abs error: 0x1.882ff33p-65 + // in -0x1.fp-9 0x1.fp-9n + std::array poly{ + -0x1.0000000000001p-1, 0x1.555555551305bp-2, -0x1.fffffffeb459p-3, 0x1.999b324f10111p-3, -0x1.55575e506c89fp-3, + }; + + // relative error: 0x1.c04d76cp-63 + // in -0x1p-4 0x1.09p-4 (|log(1+x)| > 0x1p-4 outside the interval) + std::array poly1{ + -0x1p-1, + 0x1.5555555555577p-2, + -0x1.ffffffffffdcbp-3, + 0x1.999999995dd0cp-3, + -0x1.55555556745a7p-3, + 0x1.24924a344de3p-3, + -0x1.fffffa4423d65p-4, + 0x1.c7184282ad6cap-4, + -0x1.999eb43b068ffp-4, + 0x1.78182f7afd085p-4, + -0x1.5521375d145cdp-4, + }; + struct + { + double invc; + double logc; + } tab[1 << k_logTableBitsDbl] = { + {0x1.734f0c3e0de9fp+0, -0x1.7cc7f79e69000p-2}, {0x1.713786a2ce91fp+0, -0x1.76feec20d0000p-2}, {0x1.6f26008fab5a0p+0, -0x1.713e31351e000p-2}, + {0x1.6d1a61f138c7dp+0, -0x1.6b85b38287800p-2}, {0x1.6b1490bc5b4d1p+0, -0x1.65d5590807800p-2}, {0x1.69147332f0cbap+0, -0x1.602d076180000p-2}, + {0x1.6719f18224223p+0, -0x1.5a8ca86909000p-2}, {0x1.6524f99a51ed9p+0, -0x1.54f4356035000p-2}, {0x1.63356aa8f24c4p+0, -0x1.4f637c36b4000p-2}, + {0x1.614b36b9ddc14p+0, -0x1.49da7fda85000p-2}, {0x1.5f66452c65c4cp+0, -0x1.445923989a800p-2}, {0x1.5d867b5912c4fp+0, -0x1.3edf439b0b800p-2}, + {0x1.5babccb5b90dep+0, -0x1.396ce448f7000p-2}, {0x1.59d61f2d91a78p+0, -0x1.3401e17bda000p-2}, {0x1.5805612465687p+0, -0x1.2e9e2ef468000p-2}, + {0x1.56397cee76bd3p+0, -0x1.2941b3830e000p-2}, {0x1.54725e2a77f93p+0, -0x1.23ec58cda8800p-2}, {0x1.52aff42064583p+0, -0x1.1e9e129279000p-2}, + {0x1.50f22dbb2bddfp+0, -0x1.1956d2b48f800p-2}, {0x1.4f38f4734ded7p+0, -0x1.141679ab9f800p-2}, {0x1.4d843cfde2840p+0, -0x1.0edd094ef9800p-2}, + {0x1.4bd3ec078a3c8p+0, -0x1.09aa518db1000p-2}, {0x1.4a27fc3e0258ap+0, -0x1.047e65263b800p-2}, {0x1.4880524d48434p+0, -0x1.feb224586f000p-3}, + {0x1.46dce1b192d0bp+0, -0x1.f474a7517b000p-3}, {0x1.453d9d3391854p+0, -0x1.ea4443d103000p-3}, {0x1.43a2744b4845ap+0, -0x1.e020d44e9b000p-3}, + {0x1.420b54115f8fbp+0, -0x1.d60a22977f000p-3}, {0x1.40782da3ef4b1p+0, -0x1.cc00104959000p-3}, {0x1.3ee8f5d57fe8fp+0, -0x1.c202956891000p-3}, + {0x1.3d5d9a00b4ce9p+0, -0x1.b81178d811000p-3}, {0x1.3bd60c010c12bp+0, -0x1.ae2c9ccd3d000p-3}, {0x1.3a5242b75dab8p+0, -0x1.a45402e129000p-3}, + {0x1.38d22cd9fd002p+0, -0x1.9a877681df000p-3}, {0x1.3755bc5847a1cp+0, -0x1.90c6d69483000p-3}, {0x1.35dce49ad36e2p+0, -0x1.87120a645c000p-3}, + {0x1.34679984dd440p+0, -0x1.7d68fb4143000p-3}, {0x1.32f5cceffcb24p+0, -0x1.73cb83c627000p-3}, {0x1.3187775a10d49p+0, -0x1.6a39a9b376000p-3}, + {0x1.301c8373e3990p+0, -0x1.60b3154b7a000p-3}, {0x1.2eb4ebb95f841p+0, -0x1.5737d76243000p-3}, {0x1.2d50a0219a9d1p+0, -0x1.4dc7b8fc23000p-3}, + {0x1.2bef9a8b7fd2ap+0, -0x1.4462c51d20000p-3}, {0x1.2a91c7a0c1babp+0, -0x1.3b08abc830000p-3}, {0x1.293726014b530p+0, -0x1.31b996b490000p-3}, + {0x1.27dfa5757a1f5p+0, -0x1.2875490a44000p-3}, {0x1.268b39b1d3bbfp+0, -0x1.1f3b9f879a000p-3}, {0x1.2539d838ff5bdp+0, -0x1.160c8252ca000p-3}, + {0x1.23eb7aac9083bp+0, -0x1.0ce7f57f72000p-3}, {0x1.22a012ba940b6p+0, -0x1.03cdc49fea000p-3}, {0x1.2157996cc4132p+0, -0x1.f57bdbc4b8000p-4}, + {0x1.201201dd2fc9bp+0, -0x1.e370896404000p-4}, {0x1.1ecf4494d480bp+0, -0x1.d17983ef94000p-4}, {0x1.1d8f5528f6569p+0, -0x1.bf9674ed8a000p-4}, + {0x1.1c52311577e7cp+0, -0x1.adc79202f6000p-4}, {0x1.1b17c74cb26e9p+0, -0x1.9c0c3e7288000p-4}, {0x1.19e010c2c1ab6p+0, -0x1.8a646b372c000p-4}, + {0x1.18ab07bb670bdp+0, -0x1.78d01b3ac0000p-4}, {0x1.1778a25efbcb6p+0, -0x1.674f145380000p-4}, {0x1.1648d354c31dap+0, -0x1.55e0e6d878000p-4}, + {0x1.151b990275fddp+0, -0x1.4485cdea1e000p-4}, {0x1.13f0ea432d24cp+0, -0x1.333d94d6aa000p-4}, {0x1.12c8b7210f9dap+0, -0x1.22079f8c56000p-4}, + {0x1.11a3028ecb531p+0, -0x1.10e4698622000p-4}, {0x1.107fbda8434afp+0, -0x1.ffa6c6ad20000p-5}, {0x1.0f5ee0f4e6bb3p+0, -0x1.dda8d4a774000p-5}, + {0x1.0e4065d2a9fcep+0, -0x1.bbcece4850000p-5}, {0x1.0d244632ca521p+0, -0x1.9a1894012c000p-5}, {0x1.0c0a77ce2981ap+0, -0x1.788583302c000p-5}, + {0x1.0af2f83c636d1p+0, -0x1.5715e67d68000p-5}, {0x1.09ddb98a01339p+0, -0x1.35c8a49658000p-5}, {0x1.08cabaf52e7dfp+0, -0x1.149e364154000p-5}, + {0x1.07b9f2f4e28fbp+0, -0x1.e72c082eb8000p-6}, {0x1.06ab58c358f19p+0, -0x1.a55f152528000p-6}, {0x1.059eea5ecf92cp+0, -0x1.63d62cf818000p-6}, + {0x1.04949cdd12c90p+0, -0x1.228fb8caa0000p-6}, {0x1.038c6c6f0ada9p+0, -0x1.c317b20f90000p-7}, {0x1.02865137932a9p+0, -0x1.419355daa0000p-7}, + {0x1.0182427ea7348p+0, -0x1.81203c2ec0000p-8}, {0x1.008040614b195p+0, -0x1.0040979240000p-9}, {0x1.fe01ff726fa1ap-1, 0x1.feff384900000p-9}, + {0x1.fa11cc261ea74p-1, 0x1.7dc41353d0000p-7}, {0x1.f6310b081992ep-1, 0x1.3cea3c4c28000p-6}, {0x1.f25f63ceeadcdp-1, 0x1.b9fc114890000p-6}, + {0x1.ee9c8039113e7p-1, 0x1.1b0d8ce110000p-5}, {0x1.eae8078cbb1abp-1, 0x1.58a5bd001c000p-5}, {0x1.e741aa29d0c9bp-1, 0x1.95c8340d88000p-5}, + {0x1.e3a91830a99b5p-1, 0x1.d276aef578000p-5}, {0x1.e01e009609a56p-1, 0x1.07598e598c000p-4}, {0x1.dca01e577bb98p-1, 0x1.253f5e30d2000p-4}, + {0x1.d92f20b7c9103p-1, 0x1.42edd8b380000p-4}, {0x1.d5cac66fb5ccep-1, 0x1.606598757c000p-4}, {0x1.d272caa5ede9dp-1, 0x1.7da76356a0000p-4}, + {0x1.cf26e3e6b2ccdp-1, 0x1.9ab434e1c6000p-4}, {0x1.cbe6da2a77902p-1, 0x1.b78c7bb0d6000p-4}, {0x1.c8b266d37086dp-1, 0x1.d431332e72000p-4}, + {0x1.c5894bd5d5804p-1, 0x1.f0a3171de6000p-4}, {0x1.c26b533bb9f8cp-1, 0x1.067152b914000p-3}, {0x1.bf583eeece73fp-1, 0x1.147858292b000p-3}, + {0x1.bc4fd75db96c1p-1, 0x1.2266ecdca3000p-3}, {0x1.b951e0c864a28p-1, 0x1.303d7a6c55000p-3}, {0x1.b65e2c5ef3e2cp-1, 0x1.3dfc33c331000p-3}, + {0x1.b374867c9888bp-1, 0x1.4ba366b7a8000p-3}, {0x1.b094b211d304ap-1, 0x1.5933928d1f000p-3}, {0x1.adbe885f2ef7ep-1, 0x1.66acd2418f000p-3}, + {0x1.aaf1d31603da2p-1, 0x1.740f8ec669000p-3}, {0x1.a82e63fd358a7p-1, 0x1.815c0f51af000p-3}, {0x1.a5740ef09738bp-1, 0x1.8e92954f68000p-3}, + {0x1.a2c2a90ab4b27p-1, 0x1.9bb3602f84000p-3}, {0x1.a01a01393f2d1p-1, 0x1.a8bed1c2c0000p-3}, {0x1.9d79f24db3c1bp-1, 0x1.b5b515c01d000p-3}, + {0x1.9ae2505c7b190p-1, 0x1.c2967ccbcc000p-3}, {0x1.9852ef297ce2fp-1, 0x1.cf635d5486000p-3}, {0x1.95cbaeea44b75p-1, 0x1.dc1bd3446c000p-3}, + {0x1.934c69de74838p-1, 0x1.e8c01b8cfe000p-3}, {0x1.90d4f2f6752e6p-1, 0x1.f5509c0179000p-3}, {0x1.8e6528effd79dp-1, 0x1.00e6c121fb800p-2}, + {0x1.8bfce9fcc007cp-1, 0x1.071b80e93d000p-2}, {0x1.899c0dabec30ep-1, 0x1.0d46b9e867000p-2}, {0x1.87427aa2317fbp-1, 0x1.13687334bd000p-2}, + {0x1.84f00acb39a08p-1, 0x1.1980d67234800p-2}, {0x1.82a49e8653e55p-1, 0x1.1f8ffe0cc8000p-2}, {0x1.8060195f40260p-1, 0x1.2595fd7636800p-2}, + {0x1.7e22563e0a329p-1, 0x1.2b9300914a800p-2}, {0x1.7beb377dcb5adp-1, 0x1.3187210436000p-2}, {0x1.79baa679725c2p-1, 0x1.377266dec1800p-2}, + {0x1.77907f2170657p-1, 0x1.3d54ffbaf3000p-2}, + }; + + struct + { + double chi; + double clo; + } tab2[1 << k_logTableBitsDbl] = { + {0x1.61000014fb66bp-1, 0x1.e026c91425b3cp-56}, {0x1.63000034db495p-1, 0x1.dbfea48005d41p-55}, {0x1.650000d94d478p-1, 0x1.e7fa786d6a5b7p-55}, + {0x1.67000074e6fadp-1, 0x1.1fcea6b54254cp-57}, {0x1.68ffffedf0faep-1, -0x1.c7e274c590efdp-56}, {0x1.6b0000763c5bcp-1, -0x1.ac16848dcda01p-55}, + {0x1.6d0001e5cc1f6p-1, 0x1.33f1c9d499311p-55}, {0x1.6efffeb05f63ep-1, -0x1.e80041ae22d53p-56}, {0x1.710000e86978p-1, 0x1.bff6671097952p-56}, + {0x1.72ffffc67e912p-1, 0x1.c00e226bd8724p-55}, {0x1.74fffdf81116ap-1, -0x1.e02916ef101d2p-57}, {0x1.770000f679c9p-1, -0x1.7fc71cd549c74p-57}, + {0x1.78ffffa7ec835p-1, 0x1.1bec19ef50483p-55}, {0x1.7affffe20c2e6p-1, -0x1.07e1729cc6465p-56}, {0x1.7cfffed3fc9p-1, -0x1.08072087b8b1cp-55}, + {0x1.7efffe9261a76p-1, 0x1.dc0286d9df9aep-55}, {0x1.81000049ca3e8p-1, 0x1.97fd251e54c33p-55}, {0x1.8300017932c8fp-1, -0x1.afee9b630f381p-55}, + {0x1.850000633739cp-1, 0x1.9bfbf6b6535bcp-55}, {0x1.87000204289c6p-1, -0x1.bbf65f3117b75p-55}, {0x1.88fffebf57904p-1, -0x1.9006ea23dcb57p-55}, + {0x1.8b00022bc04dfp-1, -0x1.d00df38e04b0ap-56}, {0x1.8cfffe50c1b8ap-1, -0x1.8007146ff9f05p-55}, {0x1.8effffc918e43p-1, 0x1.3817bd07a7038p-55}, + {0x1.910001efa5fc7p-1, 0x1.93e9176dfb403p-55}, {0x1.9300013467bb9p-1, 0x1.f804e4b980276p-56}, {0x1.94fffe6ee076fp-1, -0x1.f7ef0d9ff622ep-55}, + {0x1.96fffde3c12d1p-1, -0x1.082aa962638bap-56}, {0x1.98ffff4458a0dp-1, -0x1.7801b9164a8efp-55}, {0x1.9afffdd982e3ep-1, -0x1.740e08a5a9337p-55}, + {0x1.9cfffed49fb66p-1, 0x1.fce08c19bep-60}, {0x1.9f00020f19c51p-1, -0x1.a3faa27885b0ap-55}, {0x1.a10001145b006p-1, 0x1.4ff489958da56p-56}, + {0x1.a300007bbf6fap-1, 0x1.cbeab8a2b6d18p-55}, {0x1.a500010971d79p-1, 0x1.8fecadd78793p-55}, {0x1.a70001df52e48p-1, -0x1.f41763dd8abdbp-55}, + {0x1.a90001c593352p-1, -0x1.ebf0284c27612p-55}, {0x1.ab0002a4f3e4bp-1, -0x1.9fd043cff3f5fp-57}, {0x1.acfffd7ae1ed1p-1, -0x1.23ee7129070b4p-55}, + {0x1.aefffee510478p-1, 0x1.a063ee00edea3p-57}, {0x1.b0fffdb650d5bp-1, 0x1.a06c8381f0ab9p-58}, {0x1.b2ffffeaaca57p-1, -0x1.9011e74233c1dp-56}, + {0x1.b4fffd995badcp-1, -0x1.9ff1068862a9fp-56}, {0x1.b7000249e659cp-1, 0x1.aff45d0864f3ep-55}, {0x1.b8ffff987164p-1, 0x1.cfe7796c2c3f9p-56}, + {0x1.bafffd204cb4fp-1, -0x1.3ff27eef22bc4p-57}, {0x1.bcfffd2415c45p-1, -0x1.cffb7ee3bea21p-57}, {0x1.beffff86309dfp-1, -0x1.14103972e0b5cp-55}, + {0x1.c0fffe1b57653p-1, 0x1.bc16494b76a19p-55}, {0x1.c2ffff1fa57e3p-1, -0x1.4feef8d30c6edp-57}, {0x1.c4fffdcbfe424p-1, -0x1.43f68bcec4775p-55}, + {0x1.c6fffed54b9f7p-1, 0x1.47ea3f053e0ecp-55}, {0x1.c8fffeb998fd5p-1, 0x1.383068df992f1p-56}, {0x1.cb0002125219ap-1, -0x1.8fd8e64180e04p-57}, + {0x1.ccfffdd94469cp-1, 0x1.e7ebe1cc7ea72p-55}, {0x1.cefffeafdc476p-1, 0x1.ebe39ad9f88fep-55}, {0x1.d1000169af82bp-1, 0x1.57d91a8b95a71p-56}, + {0x1.d30000d0ff71dp-1, 0x1.9c1906970c7dap-55}, {0x1.d4fffea790fc4p-1, -0x1.80e37c558fe0cp-58}, {0x1.d70002edc87e5p-1, -0x1.f80d64dc10f44p-56}, + {0x1.d900021dc82aap-1, -0x1.47c8f94fd5c5cp-56}, {0x1.dafffd86b0283p-1, 0x1.c7f1dc521617ep-55}, {0x1.dd000296c4739p-1, 0x1.8019eb2ffb153p-55}, + {0x1.defffe54490f5p-1, 0x1.e00d2c652cc89p-57}, {0x1.e0fffcdabf694p-1, -0x1.f8340202d69d2p-56}, {0x1.e2fffdb52c8ddp-1, 0x1.b00c1ca1b0864p-56}, + {0x1.e4ffff24216efp-1, 0x1.2ffa8b094ab51p-56}, {0x1.e6fffe88a5e11p-1, -0x1.7f673b1efbe59p-58}, {0x1.e9000119eff0dp-1, -0x1.4808d5e0bc801p-55}, + {0x1.eafffdfa51744p-1, 0x1.80006d54320b5p-56}, {0x1.ed0001a127fa1p-1, -0x1.002f860565c92p-58}, {0x1.ef00007babcc4p-1, -0x1.540445d35e611p-55}, + {0x1.f0ffff57a8d02p-1, -0x1.ffb3139ef9105p-59}, {0x1.f30001ee58ac7p-1, 0x1.a81acf2731155p-55}, {0x1.f4ffff5823494p-1, 0x1.a3f41d4d7c743p-55}, + {0x1.f6ffffca94c6bp-1, -0x1.202f41c987875p-57}, {0x1.f8fffe1f9c441p-1, 0x1.77dd1f477e74bp-56}, {0x1.fafffd2e0e37ep-1, -0x1.f01199a7ca331p-57}, + {0x1.fd0001c77e49ep-1, 0x1.181ee4bceacb1p-56}, {0x1.feffff7e0c331p-1, -0x1.e05370170875ap-57}, {0x1.00ffff465606ep+0, -0x1.a7ead491c0adap-55}, + {0x1.02ffff3867a58p+0, -0x1.77f69c3fcb2ep-54}, {0x1.04ffffdfc0d17p+0, 0x1.7bffe34cb945bp-54}, {0x1.0700003cd4d82p+0, 0x1.20083c0e456cbp-55}, + {0x1.08ffff9f2cbe8p+0, -0x1.dffdfbe37751ap-57}, {0x1.0b000010cda65p+0, -0x1.13f7faee626ebp-54}, {0x1.0d00001a4d338p+0, 0x1.07dfa79489ff7p-55}, + {0x1.0effffadafdfdp+0, -0x1.7040570d66bcp-56}, {0x1.110000bbafd96p+0, 0x1.e80d4846d0b62p-55}, {0x1.12ffffae5f45dp+0, 0x1.dbffa64fd36efp-54}, + {0x1.150000dd59ad9p+0, 0x1.a0077701250aep-54}, {0x1.170000f21559ap+0, 0x1.dfdf9e2e3deeep-55}, {0x1.18ffffc275426p+0, 0x1.10030dc3b7273p-54}, + {0x1.1b000123d3c59p+0, 0x1.97f7980030188p-54}, {0x1.1cffff8299eb7p+0, -0x1.5f932ab9f8c67p-57}, {0x1.1effff48ad4p+0, 0x1.37fbf9da75bebp-54}, + {0x1.210000c8b86a4p+0, 0x1.f806b91fd5b22p-54}, {0x1.2300003854303p+0, 0x1.3ffc2eb9fbf33p-54}, {0x1.24fffffbcf684p+0, 0x1.601e77e2e2e72p-56}, + {0x1.26ffff52921d9p+0, 0x1.ffcbb767f0c61p-56}, {0x1.2900014933a3cp+0, -0x1.202ca3c02412bp-56}, {0x1.2b00014556313p+0, -0x1.2808233f21f02p-54}, + {0x1.2cfffebfe523bp+0, -0x1.8ff7e384fdcf2p-55}, {0x1.2f0000bb8ad96p+0, -0x1.5ff51503041c5p-55}, {0x1.30ffffb7ae2afp+0, -0x1.10071885e289dp-55}, + {0x1.32ffffeac5f7fp+0, -0x1.1ff5d3fb7b715p-54}, {0x1.350000ca66756p+0, 0x1.57f82228b82bdp-54}, {0x1.3700011fbf721p+0, 0x1.000bac40dd5ccp-55}, + {0x1.38ffff9592fb9p+0, -0x1.43f9d2db2a751p-54}, {0x1.3b00004ddd242p+0, 0x1.57f6b707638e1p-55}, {0x1.3cffff5b2c957p+0, 0x1.a023a10bf1231p-56}, + {0x1.3efffeab0b418p+0, 0x1.87f6d66b152bp-54}, {0x1.410001532aff4p+0, 0x1.7f8375f198524p-57}, {0x1.4300017478b29p+0, 0x1.301e672dc5143p-55}, + {0x1.44fffe795b463p+0, 0x1.9ff69b8b2895ap-55}, {0x1.46fffe80475ep+0, -0x1.5c0b19bc2f254p-54}, {0x1.48fffef6fc1e7p+0, 0x1.b4009f23a2a72p-54}, + {0x1.4afffe5bea704p+0, -0x1.4ffb7bf0d7d45p-54}, {0x1.4d000171027dep+0, -0x1.9c06471dc6a3dp-54}, {0x1.4f0000ff03ee2p+0, 0x1.77f890b85531cp-54}, + {0x1.5100012dc4bd1p+0, 0x1.004657166a436p-57}, {0x1.530001605277ap+0, -0x1.6bfcece233209p-54}, {0x1.54fffecdb704cp+0, -0x1.902720505a1d7p-55}, + {0x1.56fffef5f54a9p+0, 0x1.bbfe60ec96412p-54}, {0x1.5900017e61012p+0, 0x1.87ec581afef9p-55}, {0x1.5b00003c93e92p+0, -0x1.f41080abf0ccp-54}, + {0x1.5d0001d4919bcp+0, -0x1.8812afb254729p-54}, {0x1.5efffe7b87a89p+0, -0x1.47eb780ed6904p-54}, + }; + }; + + template <> + struct log_data : log_data + { + }; +} // namespace ccm::internal diff --git a/include/ccmath/detail/exponential/details/log_double_impl.hpp b/include/ccmath/detail/exponential/details/log_double_impl.hpp new file mode 100644 index 00000000..d339bb8b --- /dev/null +++ b/include/ccmath/detail/exponential/details/log_double_impl.hpp @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2024-Present Ian Pike + * Copyright (c) 2024-Present ccmath contributors + * + * This library is provided under the MIT License. + * See LICENSE for more information. + */ + +#pragma once + +#include "ccmath/detail/compare/isnan.hpp" + +#include +#include +#include +#include + +#include "ccmath/detail/exponential/details/log_data.hpp" +#include "ccmath/internal/helpers/bits.hpp" +#include "ccmath/internal/helpers/floating_point_type.hpp" +#include "ccmath/internal/predef/unlikely.hpp" + +namespace ccm::internal +{ + namespace + { + namespace impl + { + constexpr ccm::internal::log_data internalLogDataDbl = ccm::internal::log_data(); + constexpr auto tab_values_dbl = internalLogDataDbl.tab; + constexpr auto tab2_values_dbl = internalLogDataDbl.tab2; + constexpr auto poly_values_dbl = internalLogDataDbl.poly; + constexpr auto poly1_values_dbl = internalLogDataDbl.poly1; + constexpr auto ln2hi_value_dbl = internalLogDataDbl.ln2hi; + constexpr auto ln2lo_value_dbl = internalLogDataDbl.ln2lo; + constexpr auto k_logTableN_dbl = (1 << ccm::internal::k_logTableBitsDbl); + constexpr auto k_logTableOff_dbl = 0x3fe6000000000000; + + inline constexpr double log_double_impl(double x) + { + // Declare variables for intermediate calculations + ccm::double_t workspace{}; + ccm::double_t normVal{}; + ccm::double_t rem{}; + ccm::double_t remSqr{}; + ccm::double_t remCubed{}; + ccm::double_t result{}; + ccm::double_t inverseCoeff{}; + ccm::double_t logarithmCoeff{}; + ccm::double_t scaleFactor{}; + ccm::double_t highPart{}; + ccm::double_t lowPart{}; + + // Declare variables for bitwise operations + std::uint64_t intX{}; + std::uint64_t intNorm{}; + std::uint64_t tmp{}; + std::uint32_t top{}; + + // Declare variables for exponent and loop iteration + int expo{}; + int i{}; + + // Convert input double to uint64_t and extract top 16 bits + intX = ccm::helpers::double_to_uint64(x); + top = ccm::helpers::top16_bits_of_double(x); + + // Constants for comparison + constexpr std::uint64_t low = ccm::helpers::double_to_uint64(1.0 - 0x1p-4); + constexpr std::uint64_t high = ccm::helpers::double_to_uint64(1.0 + 0x1p-4); + + // Handle special cases where input is close to 1.0 + if (CCM_UNLIKELY(intX - low < high - low)) + { + // Handle the case where x is exactly 1.0 + if (CCM_UNLIKELY(intX == ccm::helpers::double_to_uint64(1.0))) { return 0; } + + // Compute the logarithm using polynomial approximation + rem = x - 1.0; + remSqr = rem * rem; + remCubed = rem * remSqr; + result = remCubed * (poly1_values_dbl[1] + rem * poly1_values_dbl[2] + remSqr * poly1_values_dbl[3] + + remCubed * (poly1_values_dbl[4] + rem * poly1_values_dbl[5] + remSqr * poly1_values_dbl[6] + + remCubed * (poly1_values_dbl[7] + rem * poly1_values_dbl[8] + remSqr * poly1_values_dbl[9] + + remCubed * poly1_values_dbl[10]))); + + // Additional error correction + // Worst-case error is around 0.507 ULP. + workspace = rem * 0x1p27; + ccm::double_t rhi = rem + workspace - workspace; + ccm::double_t rlo = rem - rhi; + workspace = rhi * rhi * poly1_values_dbl[0]; // poly1_values[0] == -0.5. + highPart = rem + workspace; + lowPart = rem - highPart + workspace; + lowPart += poly1_values_dbl[0] * rlo * (rhi + rem); + result += lowPart; + result += highPart; + return static_cast(result); + } + + // Handle special cases for very small or very large inputs + if (CCM_UNLIKELY(top - 0x0010 >= 0x7ff0 - 0x0010)) + { + // x is subnormal, normalize it. + intX = ccm::helpers::double_to_uint64(x * 0x1p52); + intX -= 52ULL << 52; + } + + /* + * x = 2^expo normVal; where normVal is in range [0x3fe6000000000000, 2 * 0x3fe6000000000000) and exact. + * The range is split into k_logTableN sub-intervals. + * The i-th sub-interval contains normVal and c is near its center. + */ + + // Calculate logarithm for normalized inputs + tmp = intX - k_logTableOff_dbl; + // NOLINTBEGIN + i = (tmp >> (52 - ccm::internal::k_logTableBitsDbl)) % k_logTableN_dbl; + expo = static_cast(tmp) >> 52; + // NOLINTEND + intNorm = intX - (tmp & 0xfffULL << 52); // Arithmetic shift + inverseCoeff = tab_values_dbl[i].invc; + logarithmCoeff = tab_values_dbl[i].logc; + normVal = ccm::helpers::uint64_to_double(intNorm); + + // Calculate intermediate value for logarithm computation + // log(x) = log1p(normVal/c-1) + log(c) + expo*Ln2. + // r ~= z/c - 1, |r| < 1/(2*N) + rem = (normVal - tab2_values_dbl[i].chi - tab2_values_dbl[i].clo) * inverseCoeff; + scaleFactor = static_cast(expo); + + // Calculate high and low parts of logarithm + // hi + lo = r + log(c) + expo*Ln2. + workspace = scaleFactor * ln2hi_value_dbl + logarithmCoeff; + highPart = workspace + rem; + lowPart = workspace - highPart + rem + scaleFactor * ln2lo_value_dbl; + + // Final computation of logarithm + // log(x) = lo + (log1p(rem) - rem) + hi. + remSqr = rem * rem; // rounding error: 0x1p-54/k_logTableN^2. + // Worst case error if |result| > 0x1p-4: 0.520 ULP + // 0.5 + 2.06/k_logTableN + abs-poly-error*2^56+0.001 ULP + result = lowPart + remSqr * poly_values_dbl[0] + + rem * remSqr * (poly_values_dbl[1] + rem * poly_values_dbl[2] + remSqr * (poly_values_dbl[3] + rem * poly_values_dbl[4])) + highPart; + return static_cast(result); + } + } // namespace impl + } // namespace + + template + [[nodiscard]] inline constexpr T log_double(T num) noexcept + { + return static_cast(impl::log_double_impl(static_cast(num))); + } +} // namespace ccm::internal diff --git a/include/ccmath/detail/exponential/details/log_float_impl.hpp b/include/ccmath/detail/exponential/details/log_float_impl.hpp new file mode 100644 index 00000000..638e8405 --- /dev/null +++ b/include/ccmath/detail/exponential/details/log_float_impl.hpp @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2024-Present Ian Pike + * Copyright (c) 2024-Present ccmath contributors + * + * This library is provided under the MIT License. + * See LICENSE for more information. + */ + +#pragma once + +#include "ccmath/detail/compare/isnan.hpp" + +#include +#include +#include +#include + +#include "ccmath/internal/helpers/bits.hpp" +#include "ccmath/internal/helpers/floating_point_type.hpp" +#include "ccmath/internal/predef/unlikely.hpp" + +namespace ccm::internal +{ + namespace + { + namespace impl + { + constexpr ccm::internal::log_data internalLogDataFlt = ccm::internal::log_data(); + constexpr auto tab_values_flt = internalLogDataFlt.tab; + constexpr auto poly_values_flt = internalLogDataFlt.poly; + constexpr auto ln2_value_flt = internalLogDataFlt.ln2; + constexpr auto k_logTableN_flt = (1 << ccm::internal::k_logTableBitsFlt); + constexpr auto k_logTableOff_flt = 0x3f330000; + + inline constexpr float log_float_impl(float x) + { + // Declare variables for intermediate calculations + ccm::double_t normVal{}; + ccm::double_t rem{}; + ccm::double_t remSqr{}; + ccm::double_t result{}; + ccm::double_t result0{}; + ccm::double_t inverseCoeff{}; + ccm::double_t logarithmCoeff{}; + + // Declare variables for bitwise operations + std::uint32_t intX{}; + std::uint32_t intNorm{}; + std::uint32_t tmp{}; + + // Declare variables for exponent and loop iteration + int expo{}; + int i{}; + + intX = ccm::helpers::float_to_uint32(x); + + // Correct the sign of zero with downward rounding when x is equal to 1.0 + if (CCM_UNLIKELY(intX == 0x3f800000)) { return 0; } + + if (CCM_UNLIKELY(intX - 0x00800000 >= 0x7f800000 - 0x00800000)) + { + // If x is subnormal, normalize it. + intX = ccm::helpers::float_to_uint32(x * 0x1p23f); + intX -= 23 << 23; + } + + // x = 2^exp normVal; where normVal is in range [k_logTableOff_flt, 2 * k_logTableOff_flt] and exact + tmp = intX - k_logTableOff_flt; + i = (tmp >> (23 - ccm::internal::k_logTableBitsFlt)) % k_logTableN_flt; // NOLINT + expo = static_cast(tmp) >> 23; + intNorm = intX - (tmp & static_cast(0x1ff << 23)); + inverseCoeff = tab_values_flt[i].invc; + logarithmCoeff = tab_values_flt[i].logc; + normVal = static_cast(ccm::helpers::uint32_to_float(intNorm)); + + // log(x) = log1p(normVal / inverseCoeff - 1) + log(inverseCoeff) + expo * Ln2 + rem - normVal * inverseCoeff - 1; + result0 = logarithmCoeff + static_cast(expo) * ln2_value_flt; + + // Polynomial approximation for log1p(rem) + remSqr = rem * rem; + result = poly_values_flt[1] * rem + poly_values_flt[2]; + result = poly_values_flt[0] * remSqr + result; + result = result * remSqr + (result0 + rem); + + return static_cast(result); + } + + } // namespace impl + } // namespace + + template + [[nodiscard]] inline constexpr T log_float(T num) noexcept + { + return static_cast(impl::log_float_impl(static_cast(num))); + } +} // namespace ccm::internal diff --git a/include/ccmath/detail/exponential/exp.hpp b/include/ccmath/detail/exponential/exp.hpp index 3f3c50a9..582eb0e5 100644 --- a/include/ccmath/detail/exponential/exp.hpp +++ b/include/ccmath/detail/exponential/exp.hpp @@ -8,36 +8,69 @@ #pragma once +#include "ccmath/detail/basic/abs.hpp" +#include "ccmath/detail/compare/isinf.hpp" +#include "ccmath/detail/compare/isnan.hpp" +#include "ccmath/detail/compare/signbit.hpp" +#include "ccmath/internal/helpers/find_number.hpp" +#include "ccmath/internal/helpers/pow_integral.hpp" +#include "ccmath/numbers.hpp" + +#include + namespace ccm { namespace { - constexpr int ExpMaxIterDepth = 25; - - namespace detail + namespace impl { // see https://en.wikipedia.org/wiki/Euler%27s_continued_fraction_formula template - constexpr T exp_cont_frac_recursive(const T x, const int depth_end) noexcept + constexpr T exp_cont_frac_recursive(const T x, const int depth_end, const std::size_t maxIterationDepth = 25) noexcept { - int depth = ExpMaxIterDepth - 1; + int depth = static_cast(maxIterationDepth) - 1; T result = T{1}; while (depth > depth_end) { - result = T{1} + x / T{depth - 1} - x / depth / result; + result = static_cast(1) + x / static_cast(depth - 1) - x / depth / result; --depth; } + + return result; } - } // namespace detail + template + constexpr T exp_cont_frac(const T x) noexcept + { + return static_cast(1) / (static_cast(1) - x / exp_cont_frac_recursive(x, 2)); + } - } // namespace + template + constexpr T exp_split(const T x) noexcept + { + return static_cast(ccm::helpers::pow_integral(ccm::numbers::e_v, ccm::helpers::find_num_whole_part(x))) * + exp_cont_frac(ccm::helpers::find_num_fract_part(x)); + } + + template + constexpr T exp_impl(const T x) noexcept + { + if (ccm::isnan(x)) { return std::numeric_limits::quiet_NaN(); } + + if (ccm::isinf(x)) { return ccm::signbit(x) ? T{0} : std::numeric_limits::infinity(); } + + if (ccm::abs(x) < std::numeric_limits::min()) { return T{1}; } + + return ccm::abs(x) < static_cast(2) ? exp_cont_frac(x) : exp_split(x); + } + } // namespace impl + } // namespace template - constexpr T exp(T x) + inline constexpr T exp(T x) { - return x * x; + return impl::exp_impl(x); } } // namespace ccm diff --git a/include/ccmath/detail/exponential/log.hpp b/include/ccmath/detail/exponential/log.hpp index b950a84d..3203b44f 100644 --- a/include/ccmath/detail/exponential/log.hpp +++ b/include/ccmath/detail/exponential/log.hpp @@ -8,7 +8,71 @@ #pragma once +#include "ccmath/detail/exponential/details/log_double_impl.hpp" +#include "ccmath/detail/exponential/details/log_float_impl.hpp" + namespace ccm { + /** + * @brief Computes the natural (base e) logarithm (lnx) of a number. + * @tparam T The type of the number. + * @param num A floating-point or integer value to find the natural logarithm of. + * @return If no errors occur, the natural (base-e) logarithm of num (ln(num) or loge(num)) is returned. + * + * @warning ccm::log is currently only ensured to work on little-endian systems. There is currently no guarantee this it will work on big-endian systems. + */ + template + inline constexpr T log(const T num) noexcept + { + // If the argument is ±0, -∞ is returned. + if (num == static_cast(0)) { return -std::numeric_limits::infinity(); } + + // If the number is 1, return +0. + if (num == static_cast(1)) { return static_cast(0); } + + // If the argument is negative, -NaN is returned. + if (num < static_cast(0)) { return -std::numeric_limits::quiet_NaN(); } + + // If the argument is +∞, +∞ is returned. + if (num == std::numeric_limits::infinity()) { return std::numeric_limits::infinity(); } + + // If the argument is NaN, NaN is returned. + if (ccm::isnan(num)) { return std::numeric_limits::quiet_NaN(); } + + // Select the correct implementation based on the type. + if constexpr (std::is_same_v) { return ccm::internal::log_float(num); } + else { return ccm::internal::log_double(num); } + } + + /** + * @brief Computes the natural (base e) logarithm (lnx) of a number. + * @tparam Integer The type of the integer. + * @param num An integer value to find the natural logarithm of. + * @return If no errors occur, the natural (base-e) logarithm of num (ln(num) or loge(num)) is returned. + */ + template , bool> = true> + inline constexpr double log(const Integer num) noexcept + { + return ccm::log(static_cast(num)); + } + + /** + * @brief Computes the natural (base e) logarithm (lnx) of a number. + * @param num A floating-point value to find the natural logarithm of. + * @return If no errors occur, the natural (base-e) logarithm of num (ln(num) or loge(num)) is returned. + */ + inline constexpr float logf(const float num) noexcept + { + return ccm::log(num); + } + /** + * @brief Computes the natural (base e) logarithm (lnx) of a number. + * @param num A floating-point value to find the natural logarithm of. + * @return If no errors occur, the natural (base-e) logarithm of num (ln(num) or loge(num)) is returned. + */ + inline constexpr double logl(const double num) noexcept + { + return ccm::log(num); + } } // namespace ccm diff --git a/include/ccmath/detail/exponential/log2.hpp b/include/ccmath/detail/exponential/log2.hpp index b950a84d..d1fc0cfd 100644 --- a/include/ccmath/detail/exponential/log2.hpp +++ b/include/ccmath/detail/exponential/log2.hpp @@ -8,7 +8,61 @@ #pragma once +#include "ccmath/detail/compare/isnan.hpp" +#include "ccmath/detail/compare/signbit.hpp" +#include "ccmath/detail/exponential/details/log2_double_impl.hpp" +#include "ccmath/detail/exponential/details/log2_float_impl.hpp" + +#include +#include + namespace ccm { + /** + * @brief Returns the base 2 logarithm of a number. + * + * @param num The number to calculate the base 2 logarithm of. + * @return The base 2 logarithm of the number. + */ + template + inline constexpr T log2(T num) noexcept + { + // If the argument is ±0, -∞ is returned + if (num == static_cast(0)) { return -std::numeric_limits::infinity(); } + + // If the argument is 1, +0 is returned. + if (num == static_cast(1)) { return 0; } + + // If the argument is negative, -NaN is returned + if (ccm::signbit(num)) { return -std::numeric_limits::quiet_NaN(); } + + // If the argument is +∞, +∞ is returned. + if (num == std::numeric_limits::infinity()) { return std::numeric_limits::infinity(); } + + // If the argument is NaN, NaN is returned. + if (ccm::isnan(num)) + { + if (ccm::signbit(num)) { return -std::numeric_limits::quiet_NaN(); } + else { return std::numeric_limits::quiet_NaN(); } + } + + if constexpr (std::is_same_v) { return ccm::internal::log2_float(num); } + else { return ccm::internal::log2_double(num); } + } + + template , int> = 0> + inline constexpr double log2(Integer num) noexcept + { + return ccm::log2(static_cast(num)); + } + + inline constexpr float log2f(float num) + { + return ccm::log2(num); + } + inline constexpr long double log2l(long double num) + { + return ccm::log2(num); + } } // namespace ccm diff --git a/include/ccmath/detail/lerp.hpp b/include/ccmath/detail/lerp.hpp index b950a84d..64585630 100644 --- a/include/ccmath/detail/lerp.hpp +++ b/include/ccmath/detail/lerp.hpp @@ -10,5 +10,40 @@ namespace ccm { + namespace + { + namespace impl + { + template + inline constexpr Real lerp_impl(Real a, Real b, Real dt) + { + if ((a <= 0 && b >= 0) || (a >= 0 && b <= 0)) + { + return dt * b + (1 - dt) * a; + } + + if (dt == 1) + { + return b; + } + + const Real x = a + dt * (b - a); + if ((dt > 1) == (b > a)) + { + return b < x ? x : b; + } + else + { + return x < b ? x : b; + } + } + } + } + template + inline constexpr T lerp(T a, T b, T t) + { + // TODO: Implement promotion and edge cases. + return impl::lerp_impl(a, b, t); + } } // namespace ccm diff --git a/include/ccmath/detail/nearest/floor.hpp b/include/ccmath/detail/nearest/floor.hpp index b950a84d..ae423ddb 100644 --- a/include/ccmath/detail/nearest/floor.hpp +++ b/include/ccmath/detail/nearest/floor.hpp @@ -8,7 +8,72 @@ #pragma once +#include "ccmath/detail/compare/isinf.hpp" +#include "ccmath/detail/compare/isnan.hpp" + +#include +#include + namespace ccm { + /** + * @brief Computes the largest integer value not greater than num. + * @tparam T The type of the number. + * @param num A floating-point or integer value. + * @return If no errors occur, the largest integer value not greater than num, that is ⌊num⌋, is returned. + */ + template + inline constexpr T floor(T num) noexcept + { + if constexpr (std::is_floating_point_v) + { + // If num is NaN, NaN is returned. + if (ccm::isnan(num)) + { + return std::numeric_limits::quiet_NaN(); + } + + // If num is ±∞ or ±0, num is returned, unmodified. + if (ccm::isinf(num) || num == static_cast(0)) + { + return num; + } + } + + // Compute the largest integer value not greater than num. + return static_cast(static_cast(num)); + } + /** + * @brief Computes the largest integer value not greater than num. + * @param num A integer value. + * @return If no errors occur, the largest integer value not greater than num, that is ⌊num⌋, is returned. + */ + template , bool> = true> + inline constexpr double floor(Integer num) noexcept + { + return static_cast(num); // All integers already have a floor value. Just cast to double and return. + } + + /** + * @brief Computes the largest integer value not greater than num. + * @param num A floating-point value. + * @return If no errors occur, the largest integer value not greater than num, that is ⌊num⌋, is returned. + */ + inline constexpr float floorf(float num) noexcept + { + return floor(num); + } + + /** + * @brief Computes the largest integer value not greater than num. + * @param num A floating-point value. + * @return If no errors occur, the largest integer value not greater than num, that is ⌊num⌋, is returned. + */ + inline constexpr double floorl(double num) noexcept + { + return floor(num); + } } // namespace ccm + +/// @ingroup nearest diff --git a/include/ccmath/detail/power/pow.hpp b/include/ccmath/detail/power/pow.hpp index de07e440..fa5ff6ae 100644 --- a/include/ccmath/detail/power/pow.hpp +++ b/include/ccmath/detail/power/pow.hpp @@ -8,17 +8,44 @@ #pragma once +#include +#include + +#include "ccmath/internal/helpers/is_odd.hpp" + +#include + namespace ccm { namespace { namespace detail { + // check that exponent does not have a fractional part + template , bool> = true> + [[nodiscard]] + inline constexpr bool is_integral(T exponent) noexcept + { + return exponent == static_cast(static_cast(exponent)); + } + + //lookup table for 2^i template - constexpr T pow_dbl(const T base, const T exp) noexcept - { - return exp == 0 ? 1 : base * pow_dbl(base, exp - 1); - } + [[nodiscard]] + inline constexpr T two_to_pow_lookup_table(const T exponent) noexcept + { + constexpr T lookup[] = { + static_cast(1), static_cast(2), static_cast(4), static_cast(8), static_cast(16), static_cast(32), static_cast(64), static_cast(128), + static_cast(256), static_cast(512), static_cast(1024), static_cast(2048), static_cast(4096), static_cast(8192), static_cast(16384), static_cast(32768), + static_cast(65536), static_cast(131072), static_cast(262144), static_cast(524288), static_cast(1048576), static_cast(2097152), static_cast(4194304), static_cast(8388608), + static_cast(16777216), static_cast(33554432), static_cast(67108864), static_cast(134217728), static_cast(268435456), static_cast(536870912), static_cast(1073741824), static_cast(2147483648) + }; + + return lookup[exponent]; + } + + + } // namespace detail } // namespace diff --git a/include/ccmath/internal/helpers/bits.hpp b/include/ccmath/internal/helpers/bits.hpp new file mode 100644 index 00000000..f569d6c1 --- /dev/null +++ b/include/ccmath/internal/helpers/bits.hpp @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2024-Present Ian Pike + * Copyright (c) 2024-Present ccmath contributors + * + * This library is provided under the MIT License. + * See LICENSE for more information. + */ + +#pragma once + +#include +#include + +namespace ccm::helpers +{ + + /** + * @brief + * @tparam To + * @tparam From + * @param src + * @return + */ + template + std::enable_if_t< + sizeof(To) == sizeof(From) && + std::is_trivially_copyable_v && + std::is_trivially_copyable_v, + To> + inline constexpr bit_cast(const From& src) noexcept + { + static_assert(std::is_trivially_constructible_v, + "This implementation additionally requires " + "destination type to be trivially constructible"); + + return __builtin_bit_cast(To, src); + } + + inline constexpr std::uint32_t get_exponent_of_double(double x) noexcept + { + // Reinterpret the binary representation of x as an std::uint64_t + std::uint64_t bits = bit_cast(x); + + // Extract the exponent bits (bits 62-52) and remove bias (1023) + std::uint32_t exponent = static_cast((bits >> 52) & 0x7FF) - 1023; + + return exponent; + } + + + + /** + * @brief Helper function to get the top 16-bits of a double. + * @param x Double to get the bits from. + * @return + */ + inline constexpr std::uint32_t top16_bits_of_double(double x) noexcept + { + return bit_cast(x) >> 48; + } + + inline constexpr std::uint32_t top12_bits_of_double(double x) noexcept + { + return bit_cast(x) >> 52; + } + + inline constexpr std::uint64_t double_to_uint64(double x) noexcept + { + return bit_cast(x); + } + + inline constexpr double uint64_to_double(std::uint64_t x) noexcept + { + return bit_cast(x); + } + + inline constexpr std::uint32_t float_to_uint32(float x) noexcept + { + return bit_cast(x); + } + + inline constexpr double uint32_to_float(std::uint32_t x) noexcept + { + return bit_cast(x); + } + + +} diff --git a/include/ccmath/internal/helpers/exponentiation_helpers.hpp b/include/ccmath/internal/helpers/exponentiation_helpers.hpp new file mode 100644 index 00000000..6b16c2f3 --- /dev/null +++ b/include/ccmath/internal/helpers/exponentiation_helpers.hpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2024-Present Ian Pike + * Copyright (c) 2024-Present ccmath contributors + * + * This library is provided under the MIT License. + * See LICENSE for more information. + */ + +#pragma once + +#include + +namespace ccm::helpers +{ + /** + * @brief Computes is a value is odd. + * @tparam T + * @param value + * @return + */ + template + [[nodiscard]] inline constexpr bool is_odd(T value) noexcept + { + return value % 2 != 0; + } + + /** + * @brief Computes the exponentiation of a base to a power using the exponentiation by squaring algorithm. + * @tparam T The type of the base and exponent. + * @param base The base value. + * @param exponent The exponent value. + * @return The result of the exponentiation. + * + * @attention This function is only available for signed integral types. + */ + template && std::is_signed_v, bool> = true> + [[nodiscard]] inline constexpr T exponentiation_by_squaring(T base, T exponent) noexcept + { + if (exponent < static_cast(0)) + { + base = static_cast(1) / base; + exponent = -exponent; + } + + if (exponent == static_cast(0)) { return static_cast(1); } + + auto result = static_cast(1); + while (exponent > static_cast(1)) + { + if (ccm::helpers::is_odd(exponent)) + { + result *= base; + exponent -= static_cast(1); + } + + base *= base; + exponent /= static_cast(2); + } + + return result * base; + } + + // Implement taylor series + template + [[nodiscard]] inline constexpr T taylor_series(T x, T n) noexcept + { + T result = static_cast(1); + for (T i = static_cast(1); i < n; ++i) + { + result += exponentiation_by_squaring(x, i) / i; + } + return result; + } + +} // namespace ccm::helpers diff --git a/include/ccmath/internal/helpers/find_number.hpp b/include/ccmath/internal/helpers/find_number.hpp new file mode 100644 index 00000000..8d23d645 --- /dev/null +++ b/include/ccmath/internal/helpers/find_number.hpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024-Present Ian Pike + * Copyright (c) 2024-Present ccmath contributors + * + * This library is provided under the MIT License. + * See LICENSE for more information. + */ + +#pragma once + +#include "ccmath/detail/basic/abs.hpp" +#include "ccmath/detail/nearest/floor.hpp" + +// TODO: Waiting on floor + +namespace ccm::helpers +{ + /** + * @brief Finds the whole number part of a floating-point number. + * @tparam T The type of the number. + * @param val The floating-point number to find the whole number part of. + * @return The whole number part of the floating-point number. + */ + template + inline constexpr long long int find_num_whole_part(const T val) noexcept + { + return static_cast(ccm::floor(val)); + } + + template + inline constexpr T find_num_fract_part(const T val) noexcept + { + return val - static_cast(find_num_whole_part(val)); + } + +} // namespace ccm::helpers diff --git a/include/ccmath/internal/helpers/floating_point_type.hpp b/include/ccmath/internal/helpers/floating_point_type.hpp new file mode 100644 index 00000000..237b990e --- /dev/null +++ b/include/ccmath/internal/helpers/floating_point_type.hpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2024-Present Ian Pike + * Copyright (c) 2024-Present ccmath contributors + * + * This library is provided under the MIT License. + * See LICENSE for more information. + */ + +#pragma once + +#include + +// Mirror float_t and double_t from to avoid having a dependency of . + +#ifdef FLT_EVAL_METHOD + # if FLT_EVAL_METHOD == -1 + # define CCM_FLT_EVAL_METHOD 2 + # else + # define CCM_FLT_EVAL_METHOD FLT_EVAL_METHOD + # endif +#elif defined __x86_64__ + # define CCM_FLT_EVAL_METHOD 0 +#else + # define CCM_FLT_EVAL_METHOD 2 +#endif + +namespace ccm +{ +#if CCM_FLT_EVAL_METHOD == 0 || CCM_FLT_EVAL_METHOD == 16 + using float_t = float; + using double_t = double; +#elif CCM_FLT_EVAL_METHOD == 1 + typedef double float_t; + typedef double double_t; +#elif CCM_FLT_EVAL_METHOD == 2 + typedef long double float_t; + typedef long double double_t; +# else + # error "Unknown __GLIBC_FLT_EVAL_METHOD" +# endif +} diff --git a/include/ccmath/internal/helpers/fpclassify_helper.hpp b/include/ccmath/internal/helpers/fpclassify_helper.hpp index f335bd40..ba71638d 100644 --- a/include/ccmath/internal/helpers/fpclassify_helper.hpp +++ b/include/ccmath/internal/helpers/fpclassify_helper.hpp @@ -51,7 +51,6 @@ namespace ccm::helpers eFP_ZERO, eFP_SUBNORMAL, eFP_NORMAL - // TODO: What the fuck? Why is this assertion being hit before the preprocessor has defined the compiler? static_assert(false, "FP_* macros are extremely implementation specific and are not defined for this compiler. Please add support for this compiler.") #endif }; diff --git a/include/ccmath/internal/helpers/is_odd.hpp b/include/ccmath/internal/helpers/is_odd.hpp new file mode 100644 index 00000000..b5afaf60 --- /dev/null +++ b/include/ccmath/internal/helpers/is_odd.hpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2024-Present Ian Pike + * Copyright (c) 2024-Present ccmath contributors + * + * This library is provided under the MIT License. + * See LICENSE for more information. + */ + +#pragma once + +namespace ccm::helpers +{ + template + [[nodiscard]] + inline constexpr bool is_odd(T value) noexcept + { + return value % 2 != 0; + } +} diff --git a/include/ccmath/internal/helpers/pow_integral.hpp b/include/ccmath/internal/helpers/pow_integral.hpp new file mode 100644 index 00000000..1ebcbd62 --- /dev/null +++ b/include/ccmath/internal/helpers/pow_integral.hpp @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2024-Present Ian Pike + * Copyright (c) 2024-Present ccmath contributors + * + * This library is provided under the MIT License. + * See LICENSE for more information. + */ + +#pragma once + +#include +#include + +namespace ccm::helpers +{ + template + constexpr T is_odd(const T val) noexcept + { + return val % static_cast(2) != static_cast(0); + } + + template + constexpr T compute_integral_pow(T base, U exponent) noexcept; + + // We calculate integral powers using exponentiation by squaring. + // https://en.wikipedia.org/wiki/Exponentiation_by_squaring + template + constexpr T compute_integral_pow_recursive(const T base, const T val, const U exponent) noexcept + { + if (exponent > static_cast(1)) + { + if (is_odd(exponent)) { return compute_integral_pow_recursive(base * base, val * base, exponent / static_cast(2)); } + else { return compute_integral_pow_recursive(base * base, val, exponent / static_cast(2)); } + } + else if (exponent == static_cast(1)) { return val * base; } + else { return val; } + } + + template >::value * = nullptr> + constexpr T check_sign_integral_pow(const T base, const U exponent) noexcept + { + if (exponent < static_cast(0)) { return static_cast(1) / compute_integral_pow(base, -exponent); } + else { return compute_integral_pow_recursive(base, static_cast(1), exponent); } + } + + template >::value * = nullptr> + constexpr T check_sign_integral_pow(const T base, const U exponent) noexcept + { + return compute_integral_pow_recursive(base, static_cast(1), exponent); + } + + template + constexpr T compute_integral_pow(T base, U exponent) noexcept + { + if (exponent == static_cast(0)) { return static_cast(1); } + + if (exponent == static_cast(1)) { return base; } + + if (exponent == static_cast(2)) { return base * base; } + + if (exponent == static_cast(3)) { return base * base * base; } + + if (exponent == std::numeric_limits::min()) { return static_cast(0); } + + if (exponent == std::numeric_limits::max()) { return std::numeric_limits::infinity(); } + + return check_sign_integral_pow(base, exponent); + } + + template >::value * = nullptr> + constexpr T type_check_integral_pow(const T base, const U exponent) noexcept + { + return compute_integral_pow(base, exponent); + } + + template >::value * = nullptr> + constexpr T type_check_integral_pow(const T base, const U exponent) noexcept + { + return compute_integral_pow(base, static_cast(exponent)); + } + + template + constexpr T pow_integral(const T base, const U exponent) noexcept + { + return ccm::helpers::type_check_integral_pow(base, exponent); + } + +} // namespace ccm::helpers diff --git a/include/ccmath/internal/predef/unlikely.hpp b/include/ccmath/internal/predef/unlikely.hpp new file mode 100644 index 00000000..2734a0e8 --- /dev/null +++ b/include/ccmath/internal/predef/unlikely.hpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2024-Present Ian Pike + * Copyright (c) 2024-Present ccmath contributors + * + * This library is provided under the MIT License. + * See LICENSE for more information. + */ + +#pragma once + +#ifndef CCM_UNLIKELY + #if defined(__cplusplus) && __cplusplus >= 202003L + #define CCM_UNLIKELY(x) [[unlikely]] (x) + #elif defined(__GNUC__) || defined(__clang__) + #define CCM_UNLIKELY(x) __builtin_expect((x), 0) + #else + #define CCM_UNLIKELY(x) (x) + #endif +#endif diff --git a/include/ccmath/numbers.hpp b/include/ccmath/numbers.hpp new file mode 100644 index 00000000..7894f811 --- /dev/null +++ b/include/ccmath/numbers.hpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2024-Present Ian Pike + * Copyright (c) 2024-Present ccmath contributors + * + * This library is provided under the MIT License. + * See LICENSE for more information. + */ + +#pragma once + +#include + +/* + * @brief This namespace contains a set of constants that are useful for mathematical operations. + */ +namespace ccm::numbers +{ + // Values taken from llvm-project + // https://github.com/llvm/llvm-project/blob/main/libcxx/include/numbers + + // These are added here to still offer support for c++17. + + template , bool> = true> + inline constexpr Real e_v = Real{2.718281828459045235360287471352662}; + + template , bool> = true> + inline constexpr Real log2e_v = Real{1.442695040888963407359924681001892}; + + template , bool> = true> + inline constexpr Real log10e_v = Real{0.434294481903251827651128918916605}; + + template , bool> = true> + inline constexpr Real pi_v = Real{3.141592653589793238462643383279502}; + + template , bool> = true> + inline constexpr Real inv_pi_v = Real{0.318309886183790671537767526745028}; + + template , bool> = true> + inline constexpr Real inv_sqrtpi_v = Real{0.564189583547756286948079451560772}; + + template , bool> = true> + inline constexpr Real ln2_v = Real{0.693147180559945309417232121458176}; + + template , bool> = true> + inline constexpr Real ln10_v = Real{2.302585092994045684017991454684364}; + + template , bool> = true> + inline constexpr Real sqrt2_v = Real{1.414213562373095048801688724209698}; + + template , bool> = true> + inline constexpr Real sqrt3_v = Real{1.732050807568877293527446341505872}; + + template , bool> = true> + inline constexpr Real inv_sqrt3_v = Real{0.577350269189625764509148780501957}; + + template , bool> = true> + inline constexpr Real egamma_v = Real{0.577215664901532860606512090082402}; + + template , bool> = true> + inline constexpr Real phi_v = Real{1.618033988749894848204586834365638}; + + inline constexpr double e = e_v; + inline constexpr double log2e = log2e_v; + inline constexpr double log10e = log10e_v; + inline constexpr double pi = pi_v; + inline constexpr double inv_pi = inv_pi_v; + inline constexpr double inv_sqrtpi = inv_sqrtpi_v; + inline constexpr double ln2 = ln2_v; + inline constexpr double ln10 = ln10_v; + inline constexpr double sqrt2 = sqrt2_v; + inline constexpr double sqrt3 = sqrt3_v; + inline constexpr double inv_sqrt3 = inv_sqrt3_v; + inline constexpr double egamma = egamma_v; + inline constexpr double phi = phi_v; +} // namespace ccm::numbers diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 3f59fbf8..d37e695e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -43,9 +43,16 @@ target_link_libraries(${PROJECT_NAME}-basic PRIVATE add_executable(${PROJECT_NAME}-compare) target_sources(${PROJECT_NAME}-compare PRIVATE compare/fpclassify_test.cpp - compare/isnan_test.cpp compare/isfinite_test.cpp + compare/isgreater_test.cpp + compare/isgreaterequal_test.cpp compare/isinf_test.cpp + compare/isless_test.cpp + compare/islessequal_test.cpp + compare/islessgreater_test.cpp + compare/isnan_test.cpp + compare/isnormal_test.cpp + compare/isunordered_test.cpp compare/signbit_test.cpp ) @@ -54,6 +61,39 @@ target_link_libraries(${PROJECT_NAME}-compare PRIVATE gtest::gtest ) +add_executable(${PROJECT_NAME}-exponential) +target_sources(${PROJECT_NAME}-exponential PRIVATE + exponential/exp2_test.cpp + exponential/exp_test.cpp + exponential/expm1_test.cpp + exponential/log1p_test.cpp + exponential/log2_test.cpp + exponential/log10_test.cpp + exponential/log_test.cpp + + +) +target_link_libraries(${PROJECT_NAME}-exponential PRIVATE + ccmath::test + gtest::gtest +) + +add_executable(${PROJECT_NAME}-fmanip) +target_sources(${PROJECT_NAME}-fmanip PRIVATE + fmanip/copysign_test.cpp + fmanip/frexp_test.cpp + fmanip/ilogb_test.cpp + fmanip/ldexp_test.cpp + fmanip/logb_test.cpp + fmanip/modf_test.cpp + fmanip/nextafter_test.cpp + fmanip/scalbn_test.cpp +) +target_link_libraries(${PROJECT_NAME}-fmanip PRIVATE + ccmath::test + gtest::gtest +) + add_executable(${PROJECT_NAME}-nearest) target_sources(${PROJECT_NAME}-nearest PRIVATE nearest/trunc_test.cpp @@ -85,6 +125,8 @@ endif() add_test(NAME ${PROJECT_NAME}-basic COMMAND ${PROJECT_NAME}-basic) add_test(NAME ${PROJECT_NAME}-compare COMMAND ${PROJECT_NAME}-compare) add_test(NAME ${PROJECT_NAME}-nearest COMMAND ${PROJECT_NAME}-nearest) +add_test(NAME ${PROJECT_NAME}-exponential COMMAND ${PROJECT_NAME}-exponential) +add_test(NAME ${PROJECT_NAME}-fmanip COMMAND ${PROJECT_NAME}-fmanip) diff --git a/test/basic/abs_test.cpp b/test/basic/abs_test.cpp index 7a6020e9..4db3ca37 100644 --- a/test/basic/abs_test.cpp +++ b/test/basic/abs_test.cpp @@ -15,6 +15,9 @@ TEST(CcmathBasicTests, Abs) { + // Verify that ccm::abs works with static_assert + static_assert(ccm::abs(1) == 1, "abs has failed testing that it is static_assert-able!"); + EXPECT_EQ(ccm::abs(-0.0), std::abs(-0.0)); // Test the ccm::abs function against std::abs diff --git a/test/basic/fdim_test.cpp b/test/basic/fdim_test.cpp index efdc0304..2807a80a 100644 --- a/test/basic/fdim_test.cpp +++ b/test/basic/fdim_test.cpp @@ -15,6 +15,8 @@ TEST(CcmathBasicTests, Fdim) { + static_assert(ccm::fdim(1.0, 1.0) == 0.0, "fdim has failed testing that it is static_assert-able!"); + EXPECT_EQ(ccm::fdim(1.0, 1.0), std::fdim(1.0, 1.0)); EXPECT_EQ(ccm::fdim(1.0, 0.0), std::fdim(1.0, 0.0)); EXPECT_EQ(ccm::fdim(0.0, 1.0), std::fdim(0.0, 1.0)); diff --git a/test/basic/fma_test.cpp b/test/basic/fma_test.cpp index f253644d..8b86f730 100644 --- a/test/basic/fma_test.cpp +++ b/test/basic/fma_test.cpp @@ -11,8 +11,20 @@ #include #include +// TODO: Add more tests for fma TEST(CcmathBasicTests, Fma) { + // Test that fma works with static_assert + static_assert(ccm::fma(1, 2, 3) == 5, "fma has failed testing that it is static_assert-able!"); + EXPECT_EQ(ccm::fma(1.0, 2.0, 3.0), std::fma(1.0, 2.0, 3.0)); + EXPECT_EQ(ccm::fma(1.0f, 2.0f, 3.0f), std::fma(1.0f, 2.0f, 3.0f)); + EXPECT_EQ(ccm::fma(1.0L, 2.0L, 3.0L), std::fma(1.0L, 2.0L, 3.0L)); + + // Test edge cases + EXPECT_EQ(ccm::fma(0.0, 0.0, 0.0), std::fma(0.0, 0.0, 0.0)); + EXPECT_EQ(ccm::fma(-0.0, -0.0, -0.0), std::fma(-0.0, -0.0, -0.0)); + + } diff --git a/test/basic/fmod_test.cpp b/test/basic/fmod_test.cpp index 3e7e5971..857f6cfc 100644 --- a/test/basic/fmod_test.cpp +++ b/test/basic/fmod_test.cpp @@ -13,15 +13,13 @@ #include #include -namespace tes -{ - -} - TEST(CcmathBasicTests, Fmod) { + // Test that fmod works with static_assert + static_assert(ccm::fmod(1, 2) == 1, "fmod has failed testing that it is static_assert-able!"); + // Test fmod with floating point numbers EXPECT_FLOAT_EQ(ccm::fmod(10.0f, 3.0f), std::fmod(10.0f, 3.0f)); diff --git a/test/basic/max_test.cpp b/test/basic/max_test.cpp index 42dc8a62..f6a56907 100644 --- a/test/basic/max_test.cpp +++ b/test/basic/max_test.cpp @@ -15,6 +15,9 @@ TEST(CcmathBasicTests, Fmax) { + // Test that fmax works with static_assert + static_assert(ccm::max(1.0, 2.0) == 2, "max has failed testing that it is static_assert-able!"); + EXPECT_EQ(ccm::fmax(1.0, 2.0), std::fmax(1.0, 2.0)); EXPECT_EQ(ccm::fmax(2.0, 1.0), std::fmax(2.0, 1.0)); EXPECT_EQ(ccm::fmax(1.0, 1.0), std::fmax(1.0, 1.0)); diff --git a/test/basic/min_test.cpp b/test/basic/min_test.cpp index 77a7941e..0d171e9c 100644 --- a/test/basic/min_test.cpp +++ b/test/basic/min_test.cpp @@ -15,6 +15,9 @@ TEST(CcmathBasicTests, Min) { + // Verify that ccm::min works with static_assert + static_assert(ccm::min(1, 2) == 1, "min has failed testing that it is static_assert-able!"); + // Test the ccm::abs function against std::abs EXPECT_EQ(ccm::min(1, 2), std::min(1, 2)); EXPECT_EQ(ccm::min(2, 1), std::min(2, 1)); diff --git a/test/basic/remainder_test.cpp b/test/basic/remainder_test.cpp index dcd4dbc2..faad044e 100644 --- a/test/basic/remainder_test.cpp +++ b/test/basic/remainder_test.cpp @@ -14,6 +14,8 @@ TEST(CcmathBasicTests, Remainder) { + static_assert(ccm::remainder(1.0, 1.0) == 0.0, "remainder has failed testing that it is static_assert-able!"); + EXPECT_EQ(ccm::remainder(1.0, 1.0), std::remainder(1.0, 1.0)); EXPECT_EQ(std::isnan(ccm::remainder(1.0, 0.0)), std::isnan(std::remainder(1.0, 0.0))); EXPECT_EQ(std::isnan(ccm::remainder(0.0, 1.0)), std::isnan(std::remainder(0.0, 1.0))); diff --git a/test/compare/fpclassify_test.cpp b/test/compare/fpclassify_test.cpp index d9ce6c32..5aad8b98 100644 --- a/test/compare/fpclassify_test.cpp +++ b/test/compare/fpclassify_test.cpp @@ -14,6 +14,10 @@ TEST(CcmathCompareTests, Fpclassify) { + // Test that fpclassify is static_assert-able + // TODO: Having issues with static_assert for fpclassify on windows and macos + //static_assert(ccm::fpclassify(1.0) == std::fpclassify(1.0), "fpclassify has failed testing that it is static_assert-able!"); + EXPECT_EQ(ccm::fpclassify(1.0), std::fpclassify(1.0)); EXPECT_EQ(ccm::fpclassify(0.0), std::fpclassify(0.0)); EXPECT_EQ(ccm::fpclassify(-1.0), std::fpclassify(-1.0)); diff --git a/test/compare/isfinite_test.cpp b/test/compare/isfinite_test.cpp index ba8f23b5..8230ece0 100644 --- a/test/compare/isfinite_test.cpp +++ b/test/compare/isfinite_test.cpp @@ -12,8 +12,13 @@ #include #include +// TODO: add more tests for isfinite TEST(CcmathCompareTests, IsFinite) { + // test isfinite is static_assert-able + static_assert(ccm::isfinite(1.0), "isfinite has failed testing that it is static_assert-able!"); + + EXPECT_EQ(ccm::isfinite(1.0), std::isfinite(1.0)); } diff --git a/test/compare/isgreater_test.cpp b/test/compare/isgreater_test.cpp new file mode 100644 index 00000000..8a6534d2 --- /dev/null +++ b/test/compare/isgreater_test.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2024-Present Ian Pike + * Copyright (c) 2024-Present ccmath contributors + * + * This library is provided under the MIT License. + * See LICENSE for more information. + */ + +#include + +#include +#include +#include "ccmath/detail/compare/isgreater.hpp" + +TEST(CcmathCompareTests, IsGreater) +{ + // Test that isgreater is static_assert-able + static_assert(ccm::isgreater(1.0, 0.0) == true, "isgreater has failed testing that it is static_assert-able!"); + + // Test the basic functionality of isgreater + EXPECT_EQ(ccm::isgreater(1.0, 0.0), std::isgreater(1.0, 0.0)); + EXPECT_EQ(ccm::isgreater(0.0, 1.0), std::isgreater(0.0, 1.0)); + EXPECT_EQ(ccm::isgreater(0.0, 0.0), std::isgreater(0.0, 0.0)); + EXPECT_EQ(ccm::isgreater(1.0, 1.0), std::isgreater(1.0, 1.0)); + EXPECT_EQ(ccm::isgreater(-1.0, 0.0), std::isgreater(-1.0, 0.0)); + EXPECT_EQ(ccm::isgreater(0.0, -1.0), std::isgreater(0.0, -1.0)); + EXPECT_EQ(ccm::isgreater(-1.0, -1.0), std::isgreater(-1.0, -1.0)); +} diff --git a/test/compare/isgreaterequal_test.cpp b/test/compare/isgreaterequal_test.cpp new file mode 100644 index 00000000..fd3c400d --- /dev/null +++ b/test/compare/isgreaterequal_test.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2024-Present Ian Pike + * Copyright (c) 2024-Present ccmath contributors + * + * This library is provided under the MIT License. + * See LICENSE for more information. + */ + +#include + +#include +#include +#include "ccmath/detail/compare/isgreaterequal.hpp" + +TEST(CcmathCompareTests, IsGreaterEqual) +{ + // Test that isgreaterequal is static_assert-able + static_assert(ccm::isgreaterequal(1.0, 0.0) == true, "isgreaterequal has failed testing that it is static_assert-able!"); + + EXPECT_EQ(ccm::isgreaterequal(1.0, 1.0), std::isgreaterequal(1.0, 1.0)); + EXPECT_EQ(ccm::isgreaterequal(1.0, 0.0), std::isgreaterequal(1.0, 0.0)); + EXPECT_EQ(ccm::isgreaterequal(0.0, 1.0), std::isgreaterequal(0.0, 1.0)); + EXPECT_EQ(ccm::isgreaterequal(0.0, 0.0), std::isgreaterequal(0.0, 0.0)); + EXPECT_EQ(ccm::isgreaterequal(-1.0, 0.0), std::isgreaterequal(-1.0, 0.0)); + EXPECT_EQ(ccm::isgreaterequal(0.0, -1.0), std::isgreaterequal(0.0, -1.0)); + EXPECT_EQ(ccm::isgreaterequal(-1.0, -1.0), std::isgreaterequal(-1.0, -1.0)); + +} diff --git a/test/compare/isinf_test.cpp b/test/compare/isinf_test.cpp index 110a7d20..a2f18fc9 100644 --- a/test/compare/isinf_test.cpp +++ b/test/compare/isinf_test.cpp @@ -12,7 +12,17 @@ #include #include "ccmath/detail/compare/isinf.hpp" +// TODO: add more tests for isinf + TEST(CcmathCompareTests, IsInf) { + // test isinf is static_assert-able + static_assert(ccm::isinf(1.0) == false, "isinf has failed testing that it is static_assert-able!"); + + EXPECT_EQ(ccm::isinf(1.0), std::isinf(1.0)); + EXPECT_EQ(ccm::isinf(0.0), std::isinf(0.0)); + EXPECT_EQ(ccm::isinf(-1.0), std::isinf(-1.0)); + EXPECT_EQ(ccm::isinf(std::numeric_limits::infinity()), std::isinf(std::numeric_limits::infinity())); + EXPECT_EQ(ccm::isinf(-std::numeric_limits::infinity()), std::isinf(-std::numeric_limits::infinity())); } diff --git a/test/compare/isless_test.cpp b/test/compare/isless_test.cpp new file mode 100644 index 00000000..ffbfa242 --- /dev/null +++ b/test/compare/isless_test.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2024-Present Ian Pike + * Copyright (c) 2024-Present ccmath contributors + * + * This library is provided under the MIT License. + * See LICENSE for more information. + */ + +#include + +#include +#include +#include "ccmath/detail/compare/isless.hpp" + +TEST(CcmathCompareTests, IsLess) +{ + // Test that isless is static_assert-able + static_assert(ccm::isless(1.0, 0.0) == false, "isless has failed testing that it is static_assert-able!"); + + EXPECT_EQ(ccm::isless(1.0, 1.0), std::isless(1.0, 1.0)); + EXPECT_EQ(ccm::isless(1.0, 0.0), std::isless(1.0, 0.0)); + EXPECT_EQ(ccm::isless(0.0, 1.0), std::isless(0.0, 1.0)); + EXPECT_EQ(ccm::isless(0.0, 0.0), std::isless(0.0, 0.0)); + EXPECT_EQ(ccm::isless(-1.0, 0.0), std::isless(-1.0, 0.0)); + EXPECT_EQ(ccm::isless(0.0, -1.0), std::isless(0.0, -1.0)); + EXPECT_EQ(ccm::isless(-1.0, -1.0), std::isless(-1.0, -1.0)); + +} diff --git a/test/compare/islessequal_test.cpp b/test/compare/islessequal_test.cpp new file mode 100644 index 00000000..3019f041 --- /dev/null +++ b/test/compare/islessequal_test.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2024-Present Ian Pike + * Copyright (c) 2024-Present ccmath contributors + * + * This library is provided under the MIT License. + * See LICENSE for more information. + */ + +#include + +#include +#include +#include "ccmath/detail/compare/islessequal.hpp" + +TEST(CcmathCompareTests, IsLessEqual) +{ + // Test that islessequal is static_assert-able + static_assert(ccm::islessequal(1.0, 0.0) == false, "islessequal has failed testing that it is static_assert-able!"); + + EXPECT_EQ(ccm::islessequal(1.0, 1.0), std::islessequal(1.0, 1.0)); + EXPECT_EQ(ccm::islessequal(1.0, 0.0), std::islessequal(1.0, 0.0)); + EXPECT_EQ(ccm::islessequal(0.0, 1.0), std::islessequal(0.0, 1.0)); + EXPECT_EQ(ccm::islessequal(0.0, 0.0), std::islessequal(0.0, 0.0)); + EXPECT_EQ(ccm::islessequal(-1.0, 0.0), std::islessequal(-1.0, 0.0)); + EXPECT_EQ(ccm::islessequal(0.0, -1.0), std::islessequal(0.0, -1.0)); + EXPECT_EQ(ccm::islessequal(-1.0, -1.0), std::islessequal(-1.0, -1.0)); + +} diff --git a/test/compare/islessgreater_test.cpp b/test/compare/islessgreater_test.cpp new file mode 100644 index 00000000..e723de6f --- /dev/null +++ b/test/compare/islessgreater_test.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2024-Present Ian Pike + * Copyright (c) 2024-Present ccmath contributors + * + * This library is provided under the MIT License. + * See LICENSE for more information. + */ + +#include + +#include +#include +#include "ccmath/detail/compare/islessgreater.hpp" + +TEST(CcmathCompareTests, IsLessGreater) +{ + // Test that islessgreater is static_assert-able + static_assert(ccm::islessgreater(1.0, 0.0) == true, "islessgreater has failed testing that it is static_assert-able!"); + + EXPECT_EQ(ccm::islessgreater(1.0, 1.0), std::islessgreater(1.0, 1.0)); + EXPECT_EQ(ccm::islessgreater(1.0, 0.0), std::islessgreater(1.0, 0.0)); + EXPECT_EQ(ccm::islessgreater(0.0, 1.0), std::islessgreater(0.0, 1.0)); + EXPECT_EQ(ccm::islessgreater(0.0, 0.0), std::islessgreater(0.0, 0.0)); + EXPECT_EQ(ccm::islessgreater(-1.0, 0.0), std::islessgreater(-1.0, 0.0)); + EXPECT_EQ(ccm::islessgreater(0.0, -1.0), std::islessgreater(0.0, -1.0)); + EXPECT_EQ(ccm::islessgreater(-1.0, -1.0), std::islessgreater(-1.0, -1.0)); +} diff --git a/test/compare/isnan_test.cpp b/test/compare/isnan_test.cpp index 43c89076..c89fa9a4 100644 --- a/test/compare/isnan_test.cpp +++ b/test/compare/isnan_test.cpp @@ -14,5 +14,15 @@ TEST(CcmathCompareTests, IsNan) { + // Test that isnan is static_assert-able + static_assert(ccm::isnan(1.0) == false, "isnan has failed testing that it is static_assert-able!"); + + EXPECT_EQ(ccm::isnan(1.0), std::isnan(1.0)); + EXPECT_EQ(ccm::isnan(0.0), std::isnan(0.0)); + EXPECT_EQ(ccm::isnan(-1.0), std::isnan(-1.0)); + EXPECT_EQ(ccm::isnan(std::numeric_limits::infinity()), std::isnan(std::numeric_limits::infinity())); + EXPECT_EQ(ccm::isnan(-std::numeric_limits::infinity()), std::isnan(-std::numeric_limits::infinity())); + EXPECT_EQ(ccm::isnan(std::numeric_limits::quiet_NaN()), std::isnan(std::numeric_limits::quiet_NaN())); + EXPECT_EQ(ccm::isnan(std::numeric_limits::signaling_NaN()), std::isnan(std::numeric_limits::signaling_NaN())); } diff --git a/test/compare/isnormal_test.cpp b/test/compare/isnormal_test.cpp new file mode 100644 index 00000000..579dc765 --- /dev/null +++ b/test/compare/isnormal_test.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2024-Present Ian Pike + * Copyright (c) 2024-Present ccmath contributors + * + * This library is provided under the MIT License. + * See LICENSE for more information. + */ + +#include + +#include +#include +#include "ccmath/detail/compare/isnormal.hpp" + +TEST(CcmathCompareTests, IsNormal) +{ + // Test that isnormal is static_assert-able + static_assert(ccm::isnormal(1.0) == true, "isnormal has failed testing that it is static_assert-able!"); + + // TODO: Add more tests for isnormal + EXPECT_EQ(ccm::isnormal(1.0), std::isnormal(1.0)); + EXPECT_EQ(ccm::isnormal(0.0), std::isnormal(0.0)); + EXPECT_EQ(ccm::isnormal(-1.0), std::isnormal(-1.0)); + EXPECT_EQ(ccm::isnormal(std::numeric_limits::infinity()), std::isnormal(std::numeric_limits::infinity())); + EXPECT_EQ(ccm::isnormal(-std::numeric_limits::infinity()), std::isnormal(-std::numeric_limits::infinity())); + EXPECT_EQ(ccm::isnormal(std::numeric_limits::quiet_NaN()), std::isnormal(std::numeric_limits::quiet_NaN())); + //EXPECT_EQ(ccm::isnormal(std::numeric_limits::signaling_NaN()), std::isnormal(std::numeric_limits::signaling_NaN)); // Won't work with std::isnormal +} diff --git a/test/compare/isunordered_test.cpp b/test/compare/isunordered_test.cpp new file mode 100644 index 00000000..d33c52b7 --- /dev/null +++ b/test/compare/isunordered_test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2024-Present Ian Pike + * Copyright (c) 2024-Present ccmath contributors + * + * This library is provided under the MIT License. + * See LICENSE for more information. + */ + +#include + +#include +#include +#include "ccmath/detail/compare/isunordered.hpp" + +TEST(CcmathCompareTests, IsUnordered) +{ + // Test that isunordered is static_assert-able + static_assert(ccm::isunordered(1.0, 0.0) == false, "isunordered has failed testing that it is static_assert-able!"); + + EXPECT_EQ(ccm::isunordered(1.0, 1.0), std::isunordered(1.0, 1.0)); + EXPECT_EQ(ccm::isunordered(1.0, 0.0), std::isunordered(1.0, 0.0)); + EXPECT_EQ(ccm::isunordered(0.0, 1.0), std::isunordered(0.0, 1.0)); + EXPECT_EQ(ccm::isunordered(0.0, 0.0), std::isunordered(0.0, 0.0)); + EXPECT_EQ(ccm::isunordered(-1.0, 0.0), std::isunordered(-1.0, 0.0)); + EXPECT_EQ(ccm::isunordered(0.0, -1.0), std::isunordered(0.0, -1.0)); + EXPECT_EQ(ccm::isunordered(-1.0, -1.0), std::isunordered(-1.0, -1.0)); + + // Check for NaN + EXPECT_EQ(ccm::isunordered(std::numeric_limits::quiet_NaN(), 1.0), std::isunordered(std::numeric_limits::quiet_NaN(), 1.0)); + EXPECT_EQ(ccm::isunordered(1.0, std::numeric_limits::quiet_NaN()), std::isunordered(1.0, std::numeric_limits::quiet_NaN())); + EXPECT_EQ(ccm::isunordered(std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN()), std::isunordered(std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN())); + +} diff --git a/test/compare/signbit_test.cpp b/test/compare/signbit_test.cpp index 7a379c44..b93ff371 100644 --- a/test/compare/signbit_test.cpp +++ b/test/compare/signbit_test.cpp @@ -14,6 +14,9 @@ TEST(CcmathCompareTests, Signbit) { + // Test that signbit is static_assert-able + static_assert(ccm::signbit(1.0) == false, "signbit has failed testing that it is static_assert-able!"); + // MSVC has issues when std::signbit is called using an integer due to ambiguity EXPECT_EQ(ccm::signbit(1.0), std::signbit(1.0)); EXPECT_EQ(ccm::signbit(-1.0), std::signbit(-1.0)); diff --git a/test/exponential/exp2_test.cpp b/test/exponential/exp2_test.cpp new file mode 100644 index 00000000..a056a2bd --- /dev/null +++ b/test/exponential/exp2_test.cpp @@ -0,0 +1,18 @@ +/* +* Copyright (c) 2024-Present Ian Pike +* Copyright (c) 2024-Present ccmath contributors +* +* This library is provided under the MIT License. +* See LICENSE for more information. +*/ + +#include + +#include +#include +#include "ccmath/detail/exponential/exp2.hpp" + +TEST(CcmathExponentialTests, Exp2) +{ + +} diff --git a/test/exponential/exp_test.cpp b/test/exponential/exp_test.cpp new file mode 100644 index 00000000..f8904d48 --- /dev/null +++ b/test/exponential/exp_test.cpp @@ -0,0 +1,21 @@ +/* +* Copyright (c) 2024-Present Ian Pike +* Copyright (c) 2024-Present ccmath contributors +* +* This library is provided under the MIT License. +* See LICENSE for more information. +*/ + +#include + +#include +#include +#include "ccmath/detail/exponential/exp.hpp" + +TEST(CcmathExponentialTests, Exp) +{ + //EXPECT_EQ(ccm::exp(1.0), std::exp(1.0)); + //EXPECT_EQ(ccm::exp(2.0), std::exp(2.0)); + //EXPECT_EQ(ccm::exp(4.0), std::exp(4.0)); + +} diff --git a/test/exponential/expm1_test.cpp b/test/exponential/expm1_test.cpp new file mode 100644 index 00000000..eef6a481 --- /dev/null +++ b/test/exponential/expm1_test.cpp @@ -0,0 +1,18 @@ +/* +* Copyright (c) 2024-Present Ian Pike +* Copyright (c) 2024-Present ccmath contributors +* +* This library is provided under the MIT License. +* See LICENSE for more information. +*/ + +#include + +#include +#include +#include "ccmath/detail/exponential/expm1.hpp" + +TEST(CcmathExponentialTests, Expm1) +{ + +} diff --git a/test/exponential/log10_test.cpp b/test/exponential/log10_test.cpp new file mode 100644 index 00000000..866423b7 --- /dev/null +++ b/test/exponential/log10_test.cpp @@ -0,0 +1,18 @@ +/* +* Copyright (c) 2024-Present Ian Pike +* Copyright (c) 2024-Present ccmath contributors +* +* This library is provided under the MIT License. +* See LICENSE for more information. +*/ + +#include + +#include +#include +#include "ccmath/detail/exponential/log10.hpp" + +TEST(CcmathExponentialTests, Log10) +{ + +} diff --git a/test/exponential/log1p_test.cpp b/test/exponential/log1p_test.cpp new file mode 100644 index 00000000..8dfe8032 --- /dev/null +++ b/test/exponential/log1p_test.cpp @@ -0,0 +1,18 @@ +/* +* Copyright (c) 2024-Present Ian Pike +* Copyright (c) 2024-Present ccmath contributors +* +* This library is provided under the MIT License. +* See LICENSE for more information. +*/ + +#include + +#include +#include +#include "ccmath/detail/exponential/log1p.hpp" + +TEST(CcmathExponentialTests, Log1p) +{ + +} diff --git a/test/exponential/log2_test.cpp b/test/exponential/log2_test.cpp new file mode 100644 index 00000000..8a2727c4 --- /dev/null +++ b/test/exponential/log2_test.cpp @@ -0,0 +1,70 @@ +/* +* Copyright (c) 2024-Present Ian Pike +* Copyright (c) 2024-Present ccmath contributors +* +* This library is provided under the MIT License. +* See LICENSE for more information. +*/ + +#include + +#include +#include +#include "ccmath/detail/exponential/log2.hpp" + +TEST(CcmathExponentialTests, Log2) +{ + static_assert(ccm::log2(1.0) == 0.0, "log2 has failed testing that it is static_assert-able!"); // Check that we can use static_assert with this function + + EXPECT_EQ(ccm::log2(1.0), std::log2(1.0)); + EXPECT_EQ(ccm::log2(2.0), std::log2(2.0)); + EXPECT_EQ(ccm::log2(4.0), std::log2(4.0)); + EXPECT_EQ(ccm::log2(8.0), std::log2(8.0)); + EXPECT_EQ(ccm::log2(16.0), std::log2(16.0)); + EXPECT_EQ(ccm::log2(32.0), std::log2(32.0)); + EXPECT_EQ(ccm::log2(64.0), std::log2(64.0)); + EXPECT_EQ(ccm::log2(128.0), std::log2(128.0)); + EXPECT_EQ(ccm::log2(256.0), std::log2(256.0)); + EXPECT_EQ(ccm::log2(512.0), std::log2(512.0)); + EXPECT_EQ(ccm::log2(1024.0), std::log2(1024.0)); + EXPECT_EQ(ccm::log2(2048.0), std::log2(2048.0)); + EXPECT_EQ(ccm::log2(4096.0), std::log2(4096.0)); + + // Test floating point precision + EXPECT_EQ(ccm::log2(1.0F), std::log2(1.0F)); + EXPECT_EQ(ccm::log2(2.0F), std::log2(2.0F)); + EXPECT_EQ(ccm::log2(4.0F), std::log2(4.0F)); + EXPECT_EQ(ccm::log2(8.0F), std::log2(8.0F)); + EXPECT_EQ(ccm::log2(16.0F), std::log2(16.0F)); + EXPECT_EQ(ccm::log2(32.0F), std::log2(32.0F)); + EXPECT_EQ(ccm::log2(64.0F), std::log2(64.0F)); + EXPECT_EQ(ccm::log2(128.0F), std::log2(128.0F)); + EXPECT_EQ(ccm::log2(256.0F), std::log2(256.0F)); + EXPECT_EQ(ccm::log2(512.0F), std::log2(512.0F)); + EXPECT_EQ(ccm::log2(1024.0F), std::log2(1024.0F)); + EXPECT_EQ(ccm::log2(2048.0F), std::log2(2048.0F)); + EXPECT_EQ(ccm::log2(4096.0F), std::log2(4096.0F)); + + // Test edge cases + EXPECT_EQ(ccm::log2(0.0), std::log2(0.0)); + EXPECT_EQ(ccm::log2(-0.0), std::log2(-0.0)); + EXPECT_EQ(ccm::log2(std::numeric_limits::infinity()), std::log2(std::numeric_limits::infinity())); + + bool testCcmLog2ThatNegInfReturnsNan = std::isnan(ccm::log2(-std::numeric_limits::infinity())); + bool testStdLog2ThatNegInfReturnsNan = std::isnan(std::log2(-std::numeric_limits::infinity())); + bool testCcmLog2ThatNegInfIsNegative = std::signbit(ccm::log2(-std::numeric_limits::infinity())); + bool testStdLog2ThatNegInfIsNegative = std::signbit(std::log2(-std::numeric_limits::infinity())); + EXPECT_EQ(testCcmLog2ThatNegInfReturnsNan, testStdLog2ThatNegInfReturnsNan); + EXPECT_EQ(testCcmLog2ThatNegInfIsNegative, testStdLog2ThatNegInfIsNegative); + + bool testCcmLog2ThatQuietNanReturnsNan = std::isnan(ccm::log2(std::numeric_limits::quiet_NaN())); + bool testStdLog2ThatQuietNanReturnsNan = std::isnan(std::log2(std::numeric_limits::quiet_NaN())); + EXPECT_EQ(testCcmLog2ThatQuietNanReturnsNan, testStdLog2ThatQuietNanReturnsNan); + + bool testCcmLog2ThatNegQuietNanReturnsNan = std::isnan(ccm::log2(-std::numeric_limits::quiet_NaN())); + bool testStdLog2ThatNegQuietNanReturnsNan = std::isnan(std::log2(-std::numeric_limits::quiet_NaN())); + bool testCcmLog2ThatNegQuietNanIsNegative = std::signbit(ccm::log2(-std::numeric_limits::quiet_NaN())); + bool testStdLog2ThatNegQuietNanIsNegative = std::signbit(std::log2(-std::numeric_limits::quiet_NaN())); + EXPECT_EQ(testCcmLog2ThatNegQuietNanReturnsNan, testStdLog2ThatNegQuietNanReturnsNan); + EXPECT_EQ(testCcmLog2ThatNegQuietNanIsNegative, testStdLog2ThatNegQuietNanIsNegative); +} diff --git a/test/exponential/log_test.cpp b/test/exponential/log_test.cpp new file mode 100644 index 00000000..002250a0 --- /dev/null +++ b/test/exponential/log_test.cpp @@ -0,0 +1,57 @@ +/* +* Copyright (c) 2024-Present Ian Pike +* Copyright (c) 2024-Present ccmath contributors +* +* This library is provided under the MIT License. +* See LICENSE for more information. +*/ + +#include + +#include +#include +#include "ccmath/detail/exponential/log.hpp" + + +TEST(CcmathExponentialTests, Log) +{ + // Verify function is static assert-able + // 1.3862943611198906 was generated with std::log(4.0) + static_assert(ccm::log(4.0) == 1.3862943611198906, "log has failed testing that it is static_assert-able!"); + + EXPECT_EQ(ccm::log(1.0), std::log(1.0)); + EXPECT_EQ(ccm::log(2.0), std::log(2.0)); + EXPECT_EQ(ccm::log(4.0), std::log(4.0)); + EXPECT_EQ(ccm::log(8.0), std::log(8.0)); + EXPECT_EQ(ccm::log(16.0), std::log(16.0)); + EXPECT_EQ(ccm::log(32.0), std::log(32.0)); + EXPECT_EQ(ccm::log(64.0), std::log(64.0)); + EXPECT_EQ(ccm::log(128.0), std::log(128.0)); + EXPECT_EQ(ccm::log(256.0), std::log(256.0)); + EXPECT_EQ(ccm::log(512.0), std::log(512.0)); + EXPECT_EQ(ccm::log(1024.0), std::log(1024.0)); + EXPECT_EQ(ccm::log(2048.0), std::log(2048.0)); + EXPECT_EQ(ccm::log(4096.0), std::log(4096.0)); + EXPECT_EQ(ccm::log(8192.0), std::log(8192.0)); + EXPECT_EQ(ccm::log(16384.0), std::log(16384.0)); + EXPECT_EQ(ccm::log(32768.0), std::log(32768.0)); + EXPECT_EQ(ccm::log(65536.0), std::log(65536.0)); + EXPECT_EQ(ccm::log(131072.0), std::log(131072.0)); + EXPECT_EQ(ccm::log(262144.0), std::log(262144.0)); + EXPECT_EQ(ccm::log(524288.0), std::log(524288.0)); + EXPECT_EQ(ccm::log(1048576.0), std::log(1048576.0)); + + + // Check for edge cases + bool ccmCheckForNan = std::isnan(ccm::log(std::numeric_limits::quiet_NaN())); + bool stdCheckForNan = std::isnan(std::log(std::numeric_limits::quiet_NaN())); + EXPECT_EQ(ccmCheckForNan, stdCheckForNan); + EXPECT_EQ(ccm::log(std::numeric_limits::infinity()), std::log(std::numeric_limits::infinity())); + bool ccmCheckForNegativeNan = std::isnan(ccm::log(-1.0)); + bool stdCheckForNegativeNan = std::isnan(std::log(-1.0)); + EXPECT_EQ(ccmCheckForNegativeNan, stdCheckForNegativeNan); + EXPECT_EQ(ccm::log(0.0), std::log(0.0)); + EXPECT_EQ(ccm::log(-0.0), std::log(-0.0)); + + +} diff --git a/test/fmanip/copysign_test.cpp b/test/fmanip/copysign_test.cpp new file mode 100644 index 00000000..d0b7da87 --- /dev/null +++ b/test/fmanip/copysign_test.cpp @@ -0,0 +1,19 @@ +/* +* Copyright (c) 2024-Present Ian Pike +* Copyright (c) 2024-Present ccmath contributors +* +* This library is provided under the MIT License. +* See LICENSE for more information. +*/ + +#include + +#include +#include +#include "ccmath/detail/fmanip/copysign.hpp" + +TEST(CcmathFmanipTests, Copysign) +{ + +} + diff --git a/test/fmanip/frexp_test.cpp b/test/fmanip/frexp_test.cpp new file mode 100644 index 00000000..b3133714 --- /dev/null +++ b/test/fmanip/frexp_test.cpp @@ -0,0 +1,19 @@ +/* +* Copyright (c) 2024-Present Ian Pike +* Copyright (c) 2024-Present ccmath contributors +* +* This library is provided under the MIT License. +* See LICENSE for more information. +*/ + +#include + +#include +#include +#include "ccmath/detail/fmanip/frexp.hpp" + +TEST(CcmathFmanipTests, Frexp) +{ + +} + diff --git a/test/fmanip/ilogb_test.cpp b/test/fmanip/ilogb_test.cpp new file mode 100644 index 00000000..80f95c41 --- /dev/null +++ b/test/fmanip/ilogb_test.cpp @@ -0,0 +1,19 @@ +/* +* Copyright (c) 2024-Present Ian Pike +* Copyright (c) 2024-Present ccmath contributors +* +* This library is provided under the MIT License. +* See LICENSE for more information. +*/ + +#include + +#include +#include +#include "ccmath/detail/fmanip/ilogb.hpp" + +TEST(CcmathFmanipTests, ILogb) +{ + +} + diff --git a/test/fmanip/ldexp_test.cpp b/test/fmanip/ldexp_test.cpp new file mode 100644 index 00000000..57b03d26 --- /dev/null +++ b/test/fmanip/ldexp_test.cpp @@ -0,0 +1,19 @@ +/* +* Copyright (c) 2024-Present Ian Pike +* Copyright (c) 2024-Present ccmath contributors +* +* This library is provided under the MIT License. +* See LICENSE for more information. +*/ + +#include + +#include +#include +#include "ccmath/detail/fmanip/ldexp.hpp" + +TEST(CcmathFmanipTests, Ldexp) +{ + +} + diff --git a/test/fmanip/logb_test.cpp b/test/fmanip/logb_test.cpp new file mode 100644 index 00000000..9c0210d3 --- /dev/null +++ b/test/fmanip/logb_test.cpp @@ -0,0 +1,19 @@ +/* +* Copyright (c) 2024-Present Ian Pike +* Copyright (c) 2024-Present ccmath contributors +* +* This library is provided under the MIT License. +* See LICENSE for more information. +*/ + +#include + +#include +#include +#include "ccmath/detail/fmanip/logb.hpp" + +TEST(CcmathFmanipTests, Logb) +{ + +} + diff --git a/test/fmanip/modf_test.cpp b/test/fmanip/modf_test.cpp new file mode 100644 index 00000000..2ea8c699 --- /dev/null +++ b/test/fmanip/modf_test.cpp @@ -0,0 +1,19 @@ +/* +* Copyright (c) 2024-Present Ian Pike +* Copyright (c) 2024-Present ccmath contributors +* +* This library is provided under the MIT License. +* See LICENSE for more information. +*/ + +#include + +#include +#include +#include "ccmath/detail/fmanip/modf.hpp" + +TEST(CcmathFmanipTests, Modf) +{ + +} + diff --git a/test/fmanip/nextafter_test.cpp b/test/fmanip/nextafter_test.cpp new file mode 100644 index 00000000..7bb548ec --- /dev/null +++ b/test/fmanip/nextafter_test.cpp @@ -0,0 +1,19 @@ +/* +* Copyright (c) 2024-Present Ian Pike +* Copyright (c) 2024-Present ccmath contributors +* +* This library is provided under the MIT License. +* See LICENSE for more information. +*/ + +#include + +#include +#include +#include "ccmath/detail/fmanip/nextafter.hpp" + +TEST(CcmathFmanipTests, Nextafter) +{ + +} + diff --git a/test/fmanip/scalbn_test.cpp b/test/fmanip/scalbn_test.cpp new file mode 100644 index 00000000..c670ec46 --- /dev/null +++ b/test/fmanip/scalbn_test.cpp @@ -0,0 +1,19 @@ +/* +* Copyright (c) 2024-Present Ian Pike +* Copyright (c) 2024-Present ccmath contributors +* +* This library is provided under the MIT License. +* See LICENSE for more information. +*/ + +#include + +#include +#include +#include "ccmath/detail/fmanip/scalbn.hpp" + +TEST(CcmathFmanipTests, Scalbn) +{ + +} + diff --git a/test/hyperbolic/_test.cpp b/test/hyperbolic/_test.cpp new file mode 100644 index 00000000..d0b7da87 --- /dev/null +++ b/test/hyperbolic/_test.cpp @@ -0,0 +1,19 @@ +/* +* Copyright (c) 2024-Present Ian Pike +* Copyright (c) 2024-Present ccmath contributors +* +* This library is provided under the MIT License. +* See LICENSE for more information. +*/ + +#include + +#include +#include +#include "ccmath/detail/fmanip/copysign.hpp" + +TEST(CcmathFmanipTests, Copysign) +{ + +} + From 6fc97918e4ec4b6311e9d2bc46cb4bd5ee4e8320 Mon Sep 17 00:00:00 2001 From: Ian Pike Date: Tue, 12 Mar 2024 18:14:47 -0400 Subject: [PATCH 2/3] Bring in multiple hot patches for the library into main (#15) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Remove not_null (not used anymore) * Bring version 0.1.1 into main (#12) (#13) * Add support for builtin detection * Update comments to be more eye catching * Add clang-cl and apple clang support * Fix bug with MSVC where not checking for nan * Remove msvc specific code in favor of generic approach * Prepare for 0.1.0 release. * Add doxygen docs * Finalize min/max * Add todo for later work on remquo * Cleanup main for 0.1.0 release * Bring standard back to C++17 * Minor cleanup to cmake list * Prep files for v0.1.0 release * Prepare for 0.1.0 release. * Add doxygen docs * Finalize min/max * Add todo for later work on remquo * Cleanup main for 0.1.0 release * Bring standard back to C++17 * Minor cleanup to cmake list * Prep files for v0.1.0 release * Bring everything in * Remove artifacts that mistakenly got pushed to main * Implement isgreater * Implement isgreaterequal * Implement isless * Implement islessequal * Implement islessgreater * Implement isnormal * Implement isunordered * Add test cases for all new compare functions * Add boilerplate code for future unit tests * Update progress * A lot of work done so far on log * log now far more efficient and accurate * Update readme progress * Continue work on log for double * remove unnecessary include * remove msvc section of unlikely * improve variable names * remove old log impl * Cleanup double implementation of log * cleanup of log details and add more comments. * Finalize work on log function * Update current progress * Fix bug with ±0 in log * Update test cases to cover all major edge cases * Move to correct folder * Add headers for log and log2 * Remove dependency on Threads * Initial addition of Contribution Guide * Cleanup README and add extra information * Update project version to v0.1.1 * Add top12 bits function for upcoming work * Finalize work with log function * Initial working revision of log2 * Push current work. Need to revise log2 to resolve bugs later. * Initial implementation of lerp * Fix scope bug * remove the direct use of cmath. * Update include to use cfloat instead of float.h * Cleanup macros and fix minor bugs * Fix bug where we were improperly assigning a correct value to the wrong variable * Update docs to mention not accounting for big-endian * Add comment to mention we are mirroring impl from cmath * Remove MSVC fallback that was inconsistent with every other impl * Add additional helpers for future work * Add test for static_assert support and some additional tests * Finalize implementation of log2 * Remove static_assert for now * Update README.md * Bring version 0.1.1 into main (#12) (#14) * Add support for builtin detection * Update comments to be more eye catching * Add clang-cl and apple clang support * Fix bug with MSVC where not checking for nan * Remove msvc specific code in favor of generic approach * Prepare for 0.1.0 release. * Add doxygen docs * Finalize min/max * Add todo for later work on remquo * Cleanup main for 0.1.0 release * Bring standard back to C++17 * Minor cleanup to cmake list * Prep files for v0.1.0 release * Prepare for 0.1.0 release. * Add doxygen docs * Finalize min/max * Add todo for later work on remquo * Cleanup main for 0.1.0 release * Bring standard back to C++17 * Minor cleanup to cmake list * Prep files for v0.1.0 release * Bring everything in * Remove artifacts that mistakenly got pushed to main * Implement isgreater * Implement isgreaterequal * Implement isless * Implement islessequal * Implement islessgreater * Implement isnormal * Implement isunordered * Add test cases for all new compare functions * Add boilerplate code for future unit tests * Update progress * A lot of work done so far on log * log now far more efficient and accurate * Update readme progress * Continue work on log for double * remove unnecessary include * remove msvc section of unlikely * improve variable names * remove old log impl * Cleanup double implementation of log * cleanup of log details and add more comments. * Finalize work on log function * Update current progress * Fix bug with ±0 in log * Update test cases to cover all major edge cases * Move to correct folder * Add headers for log and log2 * Remove dependency on Threads * Initial addition of Contribution Guide * Cleanup README and add extra information * Update project version to v0.1.1 * Add top12 bits function for upcoming work * Finalize work with log function * Initial working revision of log2 * Push current work. Need to revise log2 to resolve bugs later. * Initial implementation of lerp * Fix scope bug * remove the direct use of cmath. * Update include to use cfloat instead of float.h * Cleanup macros and fix minor bugs * Fix bug where we were improperly assigning a correct value to the wrong variable * Update docs to mention not accounting for big-endian * Add comment to mention we are mirroring impl from cmath * Remove MSVC fallback that was inconsistent with every other impl * Add additional helpers for future work * Add test for static_assert support and some additional tests * Finalize implementation of log2 * Remove static_assert for now * Update README.md * Update bullet points for features of library * Remove unused helpers * add float_t and double_t to monolithic header * Cleanup and minor adjustments * Update sources to remove deleted headers * Fix ambiguity bug between float and integer * Fix ambiguity issues * Implement basic benchmarking for log and log2 * Update all test suite to use the monolithic header instead of function specific header. * Setup some basic benchmark suites for log and abs * Minor cleanup * Add first class support for Intel DPC++ * Fix small bug with compiler identification * Add first class support to Nvidia HPC C++ * Having internal issues with detection of NVidia HPC C++. Removing specific section for now * Add support for Nvidia HPC and remove use of enums. * Fix minor bug with NVHPC * Re-enable static_assert for fpclassify and verify it works with CI * Re-enable static_assert for fpclassify and verify it works with CI * Add extra comment to static_assert --- CMakeLists.txt | 20 +- CMakePresets.json | 20 ++ README.md | 8 +- benchmark/CMakeLists.txt | 1 + benchmark/ccmath_benchmark_main.cpp | 133 ++++++- ccmath_headers.cmake | 12 +- cmake/GlobalConfig.cmake | 13 +- .../calculate_half_life_of_carbon_14.cpp | 11 +- example/main.cpp | 7 + ext/CMakeLists.txt | 27 +- include/ccmath/ccmath.hpp | 3 + include/ccmath/detail/basic/abs.hpp | 2 - include/ccmath/detail/compare/isnan.hpp | 6 +- .../exponential/details/log2_double_impl.hpp | 56 ++- .../exponential/details/log2_float_impl.hpp | 24 +- .../exponential/details/log_double_impl.hpp | 48 ++- include/ccmath/detail/exponential/exp.hpp | 47 +-- include/ccmath/detail/exponential/log.hpp | 2 +- include/ccmath/detail/exponential/log2.hpp | 2 +- include/ccmath/detail/power/pow.hpp | 1 - include/ccmath/internal/helpers/bits.hpp | 57 +-- include/ccmath/internal/helpers/endian.hpp | 1 + .../helpers/exponentiation_helpers.hpp | 75 ---- .../internal/helpers/fpclassify_helper.hpp | 51 +-- .../ccmath/internal/helpers/make_mantisa.hpp | 105 ------ .../ccmath/internal/helpers/narrow_cast.hpp | 60 ---- include/ccmath/internal/helpers/not_null.hpp | 338 ------------------ .../ccmath/internal/helpers/pow_integral.hpp | 88 ----- include/ccmath/internal/helpers/promote.hpp | 68 ---- .../ccmath/internal/setup/compiler_def.hpp | 19 +- test/basic/abs_test.cpp | 2 +- test/basic/fdim_test.cpp | 2 +- test/basic/fma_test.cpp | 2 +- test/basic/fmod_test.cpp | 2 +- test/basic/max_test.cpp | 2 +- test/basic/min_test.cpp | 2 +- test/basic/remainder_test.cpp | 2 +- test/basic/remquo_test.cpp | 2 +- test/ccmath_test_main.cpp | 1 + test/compare/fpclassify_test.cpp | 7 +- test/compare/isfinite_test.cpp | 2 +- test/compare/isgreater_test.cpp | 2 +- test/compare/isgreaterequal_test.cpp | 2 +- test/compare/isinf_test.cpp | 2 +- test/compare/isless_test.cpp | 2 +- test/compare/islessequal_test.cpp | 2 +- test/compare/islessgreater_test.cpp | 2 +- test/compare/isnan_test.cpp | 2 +- test/compare/isnormal_test.cpp | 2 +- test/compare/isunordered_test.cpp | 2 +- test/compare/signbit_test.cpp | 2 +- test/exponential/exp2_test.cpp | 2 +- test/exponential/exp_test.cpp | 2 +- test/exponential/expm1_test.cpp | 2 +- test/exponential/log10_test.cpp | 2 +- test/exponential/log1p_test.cpp | 2 +- test/exponential/log2_test.cpp | 2 +- test/exponential/log_test.cpp | 2 +- test/fmanip/copysign_test.cpp | 2 +- test/fmanip/frexp_test.cpp | 2 +- test/fmanip/ilogb_test.cpp | 2 +- test/fmanip/ldexp_test.cpp | 2 +- test/fmanip/logb_test.cpp | 2 +- test/fmanip/modf_test.cpp | 2 +- test/fmanip/nextafter_test.cpp | 2 +- test/fmanip/scalbn_test.cpp | 2 +- test/nearest/trunc_test.cpp | 3 +- 67 files changed, 401 insertions(+), 983 deletions(-) rename include/ccmath/internal/helpers/is_odd.hpp => example/calculate_half_life_of_carbon_14.cpp (52%) delete mode 100644 include/ccmath/internal/helpers/exponentiation_helpers.hpp delete mode 100644 include/ccmath/internal/helpers/make_mantisa.hpp delete mode 100644 include/ccmath/internal/helpers/narrow_cast.hpp delete mode 100644 include/ccmath/internal/helpers/not_null.hpp delete mode 100644 include/ccmath/internal/helpers/pow_integral.hpp delete mode 100644 include/ccmath/internal/helpers/promote.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index a94d5765..4422df95 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.18) -enable_language(CXX) - set(CCMATH_BUILD_VERSION 0.1.1) set(INTERNAL_PROJ_DEFAULT_NAME ccmath) @@ -33,6 +31,12 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL Clang OR CMAKE_CXX_COMPILER_ID STREQUAL GNU) target_compile_options(${PROJECT_NAME}-compile-options INTERFACE -Wall -Wextra -Wpedantic -Wconversion -Werror=return-type ) + # TODO: Remove this later. + # Some variables have been provided but are not currently being used, but it would not atm make sense to remove them. + # So to clean up the warnings we are just silencing these specific cases. + target_compile_options(${PROJECT_NAME}-compile-options INTERFACE + -Wno-unused-but-set-variable -Wno-unused-value + ) endif() if(CMAKE_CXX_COMPILER_ID STREQUAL MSVC) @@ -58,16 +62,18 @@ target_link_libraries(${PROJECT_NAME} INTERFACE ) - - configure_file(cmake/version.hpp.in "${CMAKE_CURRENT_BINARY_DIR}/include/${PROJECT_NAME}/version.hpp" @ONLY) +if (CCMATH_BUILD_EXAMPLES OR CCMATH_BUILD_BENCHMARKS OR CCMATH_BUILD_TEST) + add_subdirectory(ext) +endif() + if (CCMATH_BUILD_EXAMPLES) add_subdirectory(example) endif() -if (CCMATH_BUILD_EXAMPLES OR CCMATH_BUILD_BENCHMARKS OR CCMATH_BUILD_TEST) - add_subdirectory(ext) +if (CCMATH_BUILD_BENCHMARKS) + add_subdirectory(benchmark) endif() if (CCMATH_BUILD_TEST) @@ -77,7 +83,7 @@ endif() if (CCMATH_USE_SIMD) # TODO: Add a better way to handle enabling simd internally. - add_compile_definitions(INTERNAL_ENABLE_CHECK_FOR_SIMD) + add_compile_definitions(INTERNAL_CCMATH_ENABLE_CHECK_FOR_SIMD) endif () diff --git a/CMakePresets.json b/CMakePresets.json index 8c7a4c5e..2cdcab1a 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -25,6 +25,26 @@ "CMAKE_CXX_COMPILER": "clang++" } }, + { + "name": "ninja-intel", + "description": "Build configuration using Ninja Multi-config / clang", + "inherits": "default", + "binaryDir": "${sourceDir}/out/intel", + "cacheVariables": { + "CMAKE_C_COMPILER": "icx", + "CMAKE_CXX_COMPILER": "icx" + } + }, + { + "name": "ninja-nvidia-hpc", + "description": "Build configuration using Ninja Multi-config / clang", + "inherits": "default", + "binaryDir": "${sourceDir}/out/nvidia-hpc", + "cacheVariables": { + "CMAKE_C_COMPILER": "nvc", + "CMAKE_CXX_COMPILER": "nvc++" + } + }, { "name": "ninja-ubsan", "description": "UBSan build configuration using Ninja Multi-config", diff --git a/README.md b/README.md index b0ee5938..5a4c80e5 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,11 @@ ccmath is a C++17 library that provides a re-implementation of the standard `` library, including trigonometric, exponential, logarithmic, and other common mathematical operations. If `` has it then it is likely ccmath has implemented it. +- **Dropin Replacement for Standard Math Library**: ccmath provides a comprehensive set of mathematical functions that are 1:1 compatible with the C++ standard library ``. The goal of ccmath is to effectively be a drop in replacement for `` with little to no discernable difference between the two. This includes trigonometric, exponential, logarithmic, and other common mathematical operations. If `` has it then it is likely ccmath has implemented it. -- **Performance Optimization**: By leveraging constexpr, ccmath aims to optimize performance by computing mathematical expressions at compile time when possible, reducing runtime overhead. +- **Performance Optimization**: Past all of the functions being able to be evaluated at compile time, ccmath was also built with speed in mind. We strive to have speeds nearly as fast as the standards implementation. - **No External Dependencies**: ccmath has no external dependencies and only requires a C++17-compliant compiler. @@ -33,7 +33,7 @@ int main() { ccmath has a comprehensive cmake setup and can be easily included in your project using fetchcontent like so: ```cmake -cmake_minimum_required(VERSION 3.11) # FetchContent is new in version 3.11. +cmake_minimum_required(VERSION 3.18) include(FetchContent) FetchContent_Declare( diff --git a/benchmark/CMakeLists.txt b/benchmark/CMakeLists.txt index 9cf50fbe..afbb31e0 100644 --- a/benchmark/CMakeLists.txt +++ b/benchmark/CMakeLists.txt @@ -14,6 +14,7 @@ add_executable(${PROJECT_NAME}) add_executable(${INTERNAL_PROJ_DEFAULT_NAME}::benchmark ALIAS ${PROJECT_NAME}) target_link_libraries(${PROJECT_NAME} PUBLIC ccmath::ccmath + benchmark::benchmark ) target_include_directories(${PROJECT_NAME} PUBLIC .) target_sources(${PROJECT_NAME} PRIVATE diff --git a/benchmark/ccmath_benchmark_main.cpp b/benchmark/ccmath_benchmark_main.cpp index 17d4fd19..5c2e424f 100644 --- a/benchmark/ccmath_benchmark_main.cpp +++ b/benchmark/ccmath_benchmark_main.cpp @@ -6,6 +6,135 @@ * See LICENSE for more information. */ -int main(int argc, char** argv) -{ +#include +#include +#include + +#include +#include "ccmath/ccmath.hpp" + +namespace bm = benchmark; + +// Global seed value for random number generator +constexpr unsigned int DefaultSeed = 937162211; // Using a long prime number as our default seed + +// Generate a fixed set of random integers for benchmarking +std::vector generateRandomIntegers(size_t count, unsigned int seed) { + std::vector randomIntegers; + std::mt19937 gen(seed); + std::uniform_int_distribution dist(std::numeric_limits::min(), std::numeric_limits::max()); + for (size_t i = 0; i < count; ++i) { + randomIntegers.push_back(dist(gen)); + } + return randomIntegers; +} + +// Generate a fixed set of random integers for benchmarking +std::vector generateRandomDoubles(size_t count, unsigned int seed) { + std::vector randomDouble; + std::mt19937 gen(seed); + std::uniform_real_distribution dist(std::numeric_limits::min(), std::numeric_limits::max()); + for (size_t i = 0; i < count; ++i) { + randomDouble.push_back(dist(gen)); + } + return randomDouble; +} + +// Benchmarking std::abs with the same set of random integers +static void BM_std_abs_rand_int(benchmark::State& state) { + auto randomIntegers = generateRandomIntegers(static_cast(state.range(0)), DefaultSeed); + while (state.KeepRunning()) { + for (auto x : randomIntegers) { + benchmark::DoNotOptimize(std::abs(x)); + } + } + state.SetComplexityN(state.range(0)); } +BENCHMARK(BM_std_abs_rand_int)->Range(8, 8<<10)->Complexity(); + +// Benchmarking ccm::abs with the same set of random integers +static void BM_ccm_abs_rand_int(benchmark::State& state) { + auto randomIntegers = generateRandomIntegers(static_cast(state.range(0)), DefaultSeed); + while (state.KeepRunning()) { + for (auto x : randomIntegers) { + benchmark::DoNotOptimize(ccm::abs(x)); + } + } + state.SetComplexityN(state.range(0)); +} +BENCHMARK(BM_ccm_abs_rand_int)->Range(8, 8<<10)->Complexity(); + +// Benchmarking std::abs with the same set of random integers +static void BM_std_abs_rand_double(benchmark::State& state) { + auto randomIntegers = generateRandomDoubles(static_cast(state.range(0)), DefaultSeed); + while (state.KeepRunning()) { + for (auto x : randomIntegers) { + benchmark::DoNotOptimize(std::abs(x)); + } + } + state.SetComplexityN(state.range(0)); +} +BENCHMARK(BM_std_abs_rand_double)->Range(8, 8<<10)->Complexity(); + +// Benchmarking ccm::abs with the same set of random integers +static void BM_ccm_abs_rand_double(benchmark::State& state) { + auto randomIntegers = generateRandomDoubles(static_cast(state.range(0)), DefaultSeed); + while (state.KeepRunning()) { + for (auto x : randomIntegers) { + benchmark::DoNotOptimize(ccm::abs(x)); + } + } + state.SetComplexityN(state.range(0)); +} +BENCHMARK(BM_ccm_abs_rand_double)->Range(8, 8<<10)->Complexity(); + + +static void BM_std_abs(benchmark::State& state) { + for (auto _ : state) { + benchmark::DoNotOptimize(std::abs(state.range(0))); + } + state.SetComplexityN(state.range(0)); +} +BENCHMARK(BM_std_abs)->Arg(16)->Arg(256)->Arg(4096)->Arg(65536)->Complexity(); + +static void BM_ccm_abs(benchmark::State& state) { + for (auto _ : state) { + benchmark::DoNotOptimize(ccm::abs(state.range(0))); + } + state.SetComplexityN(state.range(0)); +} +BENCHMARK(BM_ccm_abs)->Arg(16)->Arg(256)->Arg(4096)->Arg(65536)->Complexity(); + + +static void BM_ccm_log(bm::State& state) { + for (auto _ : state) { + bm::DoNotOptimize(ccm::log(state.range(0))); + } +} +BENCHMARK(BM_ccm_log)->Arg(16)->Arg(256)->Arg(4096)->Arg(65536)->Complexity(); + +static void BM_std_log(bm::State& state) { + for (auto _ : state) { + bm::DoNotOptimize(std::log(state.range(0))); + } + state.SetComplexityN(state.range(0)); +} +BENCHMARK(BM_std_log)->Arg(16)->Arg(256)->Arg(4096)->Arg(65536)->Complexity(); + +static void BM_ccm_log2(bm::State& state) { + for (auto _ : state) { + bm::DoNotOptimize(ccm::log2(state.range(0))); + } + state.SetComplexityN(state.range(0)); +} +BENCHMARK(BM_ccm_log2)->Arg(16)->Arg(256)->Arg(4096)->Arg(65536)->Complexity(); + +static void BM_std_log2(bm::State& state) { + for (auto _ : state) { + bm::DoNotOptimize(std::log2(state.range(0))); + } + state.SetComplexityN(state.range(0)); +} +BENCHMARK(BM_std_log2)->Arg(16)->Arg(256)->Arg(4096)->Arg(65536)->Complexity(); + +BENCHMARK_MAIN(); diff --git a/ccmath_headers.cmake b/ccmath_headers.cmake index fbb8466e..d1894b85 100644 --- a/ccmath_headers.cmake +++ b/ccmath_headers.cmake @@ -1,15 +1,9 @@ set(ccmath_internal_helpers_headers + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/helpers/bits.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/helpers/endian.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/helpers/narrow_cast.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/helpers/promote.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/helpers/make_mantisa.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/helpers/not_null.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/helpers/fpclassify_helper.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/helpers/pow_integral.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/helpers/find_number.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/helpers/is_odd.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/helpers/exponentiation_helpers.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/helpers/bits.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/helpers/floating_point_type.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/helpers/fpclassify_helper.hpp ) set(ccmath_internal_predef_headers diff --git a/cmake/GlobalConfig.cmake b/cmake/GlobalConfig.cmake index 41e275e6..0de848cd 100644 --- a/cmake/GlobalConfig.cmake +++ b/cmake/GlobalConfig.cmake @@ -25,19 +25,24 @@ endif() # - GNUCXX can still be set on macOS when using Clang if(MSVC) set(CCMATH_COMPILER_MSVC 1) - + # Identify if we are using clang-CL if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(CCMATH_COMPILER_CLANG_CL 1) endif() + elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(CCMATH_COMPILER_CLANG 1) +elseif (CMAKE_CXX_COMPILER_ID MATCHES "IntelLLVM") + set(CCMATH_COMPILER_INTEL 1) +elseif (CMAKE_CXX_COMPILER_ID MATCHES "NVHPC") + set(CCMATH_COMPILER_NVIDIA_HPC 1) elseif(CMAKE_COMPILER_IS_GNUCXX) set(CCMATH_COMPILER_GCC 1) execute_process(COMMAND "${CMAKE_CXX_COMPILER}" "--version" OUTPUT_VARIABLE GCC_COMPILER_VERSION) string(REGEX MATCHALL ".*(tdm[64]*-[1-9]).*" CCMATH_COMPILER_GCC_TDM "${GCC_COMPILER_VERSION}") else() - message(FATAL_ERROR "Unsupported compiler") + message(WARNING "Unsupported compiler: ${CMAKE_CXX_COMPILER_ID}") return() endif() @@ -55,3 +60,7 @@ else() message(FATAL_ERROR "Unsupported architecture") return() endif() + +message(STATUS "CCMake Detected OS: ${CMAKE_SYSTEM_NAME}") +message(STATUS "CCMake Detected Compiler: ${CMAKE_CXX_COMPILER_ID}") +message(STATUS "CCMake Detected Architecture: ${CMAKE_SYSTEM_PROCESSOR}") diff --git a/include/ccmath/internal/helpers/is_odd.hpp b/example/calculate_half_life_of_carbon_14.cpp similarity index 52% rename from include/ccmath/internal/helpers/is_odd.hpp rename to example/calculate_half_life_of_carbon_14.cpp index b5afaf60..730e47f2 100644 --- a/include/ccmath/internal/helpers/is_odd.hpp +++ b/example/calculate_half_life_of_carbon_14.cpp @@ -6,14 +6,7 @@ * See LICENSE for more information. */ -#pragma once - -namespace ccm::helpers +int main() { - template - [[nodiscard]] - inline constexpr bool is_odd(T value) noexcept - { - return value % 2 != 0; - } + } diff --git a/example/main.cpp b/example/main.cpp index 1508e41b..13424480 100644 --- a/example/main.cpp +++ b/example/main.cpp @@ -6,11 +6,18 @@ * See LICENSE for more information. */ +#include "ccmath/ccmath.hpp" +#include "iostream" int main() { + auto test = ccm::log(1.0); + + std::cout << test; // TODO: Implement actual examples showcasing the library + + return 0; } diff --git a/ext/CMakeLists.txt b/ext/CMakeLists.txt index 3c63687f..b88ecdfd 100644 --- a/ext/CMakeLists.txt +++ b/ext/CMakeLists.txt @@ -6,9 +6,28 @@ project(${INTERNAL_PROJ_DEFAULT_NAME}-ext) include(FetchContent) - +if (CCMATH_BUILD_BENCHMARKS) + # Intel does implement the regex library but google test is unable to detect this. + # Lets help them out and set the var ourselves. + if (CCMATH_COMPILER_INTEL) + set(HAVE_STD_REGEX ON CACHE BOOL "" FORCE) + endif () + set(BENCHMARK_ENABLE_TESTING NO) + FetchContent_Declare( + benchmark + GIT_REPOSITORY https://github.com/google/benchmark.git + GIT_TAG v1.8.3 + ) + FetchContent_MakeAvailable(benchmark) +endif () if (CCMATH_BUILD_TEST) + # Intel does implement the regex library but google test is unable to detect this. + # Lets help them out and set the var ourselves. + if (CCMATH_COMPILER_INTEL) + set(HAVE_STD_REGEX ON CACHE BOOL "" FORCE) + endif () + # PThreads are not available on Windows # So tell gtest to not use them. if(CCMATH_WINDOWS) @@ -42,3 +61,9 @@ if(CCMATH_BUILD_TEST) ) endif () +if(CCMATH_BUILD_BENCHMARKS) + target_link_libraries(${PROJECT_NAME} INTERFACE + benchmark::benchmark + ) +endif () + diff --git a/include/ccmath/ccmath.hpp b/include/ccmath/ccmath.hpp index 78b7f128..32a7d28a 100644 --- a/include/ccmath/ccmath.hpp +++ b/include/ccmath/ccmath.hpp @@ -19,6 +19,9 @@ CCMATH REQUIREMENTS: * Anything implemented by cmath that is already constexpr is allowed to be wrapped by ccmath and not implemented by ccmath. */ +// Includes ccm::float_t and ccm::double_t +#include "ccmath/internal/helpers/floating_point_type.hpp" + /// Basic math functions #include "ccmath/basic.hpp" diff --git a/include/ccmath/detail/basic/abs.hpp b/include/ccmath/detail/basic/abs.hpp index 22935fe5..a1ddcd01 100644 --- a/include/ccmath/detail/basic/abs.hpp +++ b/include/ccmath/detail/basic/abs.hpp @@ -9,7 +9,6 @@ #pragma once #include "ccmath/detail/compare/isnan.hpp" -#include "ccmath/internal/utility/unreachable.hpp" #include @@ -53,7 +52,6 @@ namespace ccm else { static_assert(sizeof(T) == 0, "Taking the absolute value of an unsigned type that cannot be converted to int by integral promotion is ill-formed."); - ccm::unreachable(); // Give the compiler a hint that this branch is unreachable. } } diff --git a/include/ccmath/detail/compare/isnan.hpp b/include/ccmath/detail/compare/isnan.hpp index 62409632..f8c9cbbf 100644 --- a/include/ccmath/detail/compare/isnan.hpp +++ b/include/ccmath/detail/compare/isnan.hpp @@ -10,10 +10,6 @@ #include -#if (defined(_MSC_VER) && _MSC_VER >= 1927) && !defined(__clang__) - #include "ccmath/internal/type_traits/floating_point_traits.hpp" -#endif - namespace ccm { /** @@ -44,3 +40,5 @@ namespace ccm } } // namespace ccm + +/// @ingroup compare diff --git a/include/ccmath/detail/exponential/details/log2_double_impl.hpp b/include/ccmath/detail/exponential/details/log2_double_impl.hpp index 04ea97bd..1aba995b 100644 --- a/include/ccmath/detail/exponential/details/log2_double_impl.hpp +++ b/include/ccmath/detail/exponential/details/log2_double_impl.hpp @@ -26,15 +26,15 @@ namespace ccm::internal { namespace impl { - constexpr ccm::internal::log2_data internalLogDataDbl = ccm::internal::log2_data(); - constexpr auto tab_values_dbl = internalLogDataDbl.tab; - constexpr auto tab2_values_dbl = internalLogDataDbl.tab2; - constexpr auto poly_values_dbl = internalLogDataDbl.poly; - constexpr auto poly1_values_dbl = internalLogDataDbl.poly1; - constexpr auto inverse_ln2_high_value_dbl = internalLogDataDbl.invln2hi; - constexpr auto inverse_ln2_low_value_dbl = internalLogDataDbl.invln2lo; - constexpr auto k_logTableN_dbl = (1 << ccm::internal::k_log2TableBitsDbl); - constexpr auto k_logTableOff_dbl = 0x3fe6000000000000; + constexpr ccm::internal::log2_data internalLog2DataDbl = ccm::internal::log2_data(); + constexpr auto log2_tab_values_dbl = internalLog2DataDbl.tab; + constexpr auto log2_tab2_values_dbl = internalLog2DataDbl.tab2; + constexpr auto log2_poly_values_dbl = internalLog2DataDbl.poly; + constexpr auto log2_poly1_values_dbl = internalLog2DataDbl.poly1; + constexpr auto log2_inverse_ln2_high_value_dbl = internalLog2DataDbl.invln2hi; + constexpr auto log2_inverse_ln2_low_value_dbl = internalLog2DataDbl.invln2lo; + constexpr auto k_log2TableN_dbl = (1 << ccm::internal::k_log2TableBitsDbl); + constexpr auto k_log2TableOff_dbl = 0x3fe6000000000000; inline constexpr double log2_double_impl(double x) { @@ -54,17 +54,15 @@ namespace ccm::internal ccm::double_t logExpoSum{}; ccm::double_t polynomialTerm{}; - std::uint64_t intX{}; std::uint64_t intNorm{}; std::uint64_t tmp{}; - std::uint32_t top{}; - int expo{}; - int i{}; + std::int64_t expo{}; + std::int64_t i{}; - intX = ccm::helpers::double_to_uint64(x); - top = ccm::helpers::top16_bits_of_double(x); + std::uint64_t intX = ccm::helpers::double_to_uint64(x); + std::uint32_t top = ccm::helpers::top16_bits_of_double(x); constexpr std::uint64_t low = ccm::helpers::double_to_uint64(1.0 - 0x1.5b51p-5); constexpr std::uint64_t high = ccm::helpers::double_to_uint64(1.0 + 0x1.6ab2p-5); @@ -81,19 +79,19 @@ namespace ccm::internal ccm::double_t remLo{}; remHi = ccm::helpers::uint64_to_double(ccm::helpers::double_to_uint64(rem) & -1ULL << 32); remLo = rem - remHi; - highPart = remHi * ccm::internal::impl::inverse_ln2_high_value_dbl; - lowPart = remLo * ccm::internal::impl::inverse_ln2_high_value_dbl + rem * ccm::internal::impl::inverse_ln2_low_value_dbl; + highPart = remHi * ccm::internal::impl::log2_inverse_ln2_high_value_dbl; + lowPart = remLo * ccm::internal::impl::log2_inverse_ln2_high_value_dbl + rem * ccm::internal::impl::log2_inverse_ln2_low_value_dbl; remSqr = rem * rem; // rounding error: 0x1p-62. remQuad = remSqr * remSqr; // Worst-case error is less than 0.55 ULP - polynomialTerm = remSqr * (poly1_values_dbl[0] + rem * poly1_values_dbl[1]); + polynomialTerm = remSqr * (log2_poly1_values_dbl[0] + rem * log2_poly1_values_dbl[1]); result = highPart + polynomialTerm; lowPart += highPart - result + polynomialTerm; lowPart += - remQuad * (poly1_values_dbl[2] + rem * poly1_values_dbl[3] + remSqr * (poly1_values_dbl[4] + rem * poly1_values_dbl[5]) + - remQuad * (poly1_values_dbl[6] + rem * poly1_values_dbl[7] + remSqr * (poly1_values_dbl[8] + rem * poly1_values_dbl[9]))); + remQuad * (log2_poly1_values_dbl[2] + rem * log2_poly1_values_dbl[3] + remSqr * (log2_poly1_values_dbl[4] + rem * log2_poly1_values_dbl[5]) + + remQuad * (log2_poly1_values_dbl[6] + rem * log2_poly1_values_dbl[7] + remSqr * (log2_poly1_values_dbl[8] + rem * log2_poly1_values_dbl[9]))); result += lowPart; return result; @@ -110,14 +108,14 @@ namespace ccm::internal // x = 2^expo normVal; where normVal is in range [k_logTableOff_dbl, 2 * k_logTableOff_dbl) and exact. // The range is split into N sub-intervals. // The ith sub-interval contains normVal and c is near its center. - tmp = intX - k_logTableOff_dbl; + tmp = intX - k_log2TableOff_dbl; // NOLINTBEGIN - i = (tmp >> (52 - ccm::internal::k_log2TableBitsDbl)) % k_logTableN_dbl; + i = (tmp >> (52 - ccm::internal::k_log2TableBitsDbl)) % k_log2TableN_dbl; expo = static_cast(tmp) >> 52; // Arithmetic shift. // NOLINTEND intNorm = intX - (tmp & 0xfffULL << 52); - inverseCoeff = tab_values_dbl[i].invc; - logarithmCoeff = tab_values_dbl[i].logc; + inverseCoeff = log2_tab_values_dbl[i].invc; + logarithmCoeff = log2_tab_values_dbl[i].logc; normVal = ccm::helpers::uint64_to_double(intNorm); expoDbl = static_cast(expo); @@ -125,11 +123,11 @@ namespace ccm::internal ccm::double_t remLo{}; // rounding error: 0x1p-55/N + 0x1p-65. - rem = (normVal - tab2_values_dbl[i].chi - tab2_values_dbl[i].clo) * inverseCoeff; + rem = (normVal - log2_tab2_values_dbl[i].chi - log2_tab2_values_dbl[i].clo) * inverseCoeff; remHi = ccm::helpers::uint64_to_double(ccm::helpers::double_to_uint64(rem) & -1ULL << 32); remLo = rem - remHi; - remHighPart = remHi * inverse_ln2_high_value_dbl; - remLowPart = remLo * inverse_ln2_high_value_dbl + rem * inverse_ln2_low_value_dbl; + remHighPart = remHi * log2_inverse_ln2_high_value_dbl; + remLowPart = remLo * log2_inverse_ln2_high_value_dbl + rem * log2_inverse_ln2_low_value_dbl; // hi + lo = rem/ln2 + log2(c) + expo logExpoSum = expoDbl + logarithmCoeff; @@ -143,8 +141,8 @@ namespace ccm::internal // Worst-case error if |result| > 0x1p-4: 0.550 ULP. // ~ 0.5 + 2/N/ln2 + abs-poly-error*0x1p56+0.003 ULP. - polynomialTerm = poly_values_dbl[0] + rem * poly_values_dbl[1] + remSqr * (poly_values_dbl[2] + rem * poly_values_dbl[3]) + - remQuad * (poly_values_dbl[4] + rem * poly_values_dbl[5]); + polynomialTerm = log2_poly_values_dbl[0] + rem * log2_poly_values_dbl[1] + remSqr * (log2_poly_values_dbl[2] + rem * log2_poly_values_dbl[3]) + + remQuad * (log2_poly_values_dbl[4] + rem * log2_poly_values_dbl[5]); result = lowPart + remSqr * polynomialTerm + highPart; return result; diff --git a/include/ccmath/detail/exponential/details/log2_float_impl.hpp b/include/ccmath/detail/exponential/details/log2_float_impl.hpp index 54f43a65..d82561e5 100644 --- a/include/ccmath/detail/exponential/details/log2_float_impl.hpp +++ b/include/ccmath/detail/exponential/details/log2_float_impl.hpp @@ -27,11 +27,11 @@ namespace ccm::internal { namespace impl { - constexpr ccm::internal::log2_data internalLogDataFlt = ccm::internal::log2_data(); - constexpr auto tab_values_flt = internalLogDataFlt.tab; - constexpr auto poly_values_flt = internalLogDataFlt.poly; - constexpr auto k_logTableN_flt = (1 << ccm::internal::k_log2TableBitsFlt); - constexpr auto k_logTableOff_flt = 0x3f330000; + constexpr ccm::internal::log2_data internalLog2DataFlt = ccm::internal::log2_data(); + constexpr auto log2_tab_values_flt = internalLog2DataFlt.tab; + constexpr auto log2_poly_values_flt = internalLog2DataFlt.poly; + constexpr auto k_log2TableN_flt = (1 << ccm::internal::k_log2TableBitsFlt); + constexpr auto k_log2TableOff_flt = 0x3f330000; inline constexpr double log2_float_impl(float x) { @@ -79,14 +79,14 @@ namespace ccm::internal // x = 2^expo * normVal; where normVal is in range [k_logTableOff_flt, 2 * k_logTableOff_flt] and exact. // We split the rang into N sub-intervals. // The i-th sub-interval contains normVal and c is near its center. - tmp = intX - k_logTableOff_flt; - i = (tmp >> (23 - ccm::internal::k_log2TableBitsFlt)) % k_logTableN_flt; // NOLINT + tmp = intX - k_log2TableOff_flt; + i = (tmp >> (23 - ccm::internal::k_log2TableBitsFlt)) % k_log2TableN_flt; // NOLINT top = tmp & 0xff800000; intNorm = intX - top; // NOLINTNEXTLINE expo = static_cast(tmp) >> 23; // Arithmetic shift. - inverseCoeff = tab_values_flt[i].invc; - logarithmCoeff = tab_values_flt[i].logc; + inverseCoeff = log2_tab_values_flt[i].invc; + logarithmCoeff = log2_tab_values_flt[i].logc; normVal = static_cast(ccm::helpers::uint32_to_float(intNorm)); // log2(x) = log1p(normVal/c-1)/ln2 + log2(c) + expo @@ -95,9 +95,9 @@ namespace ccm::internal // Pipelined polynomial evaluation to approximate log1p(r)/ln2. remSqr = rem * rem; - result = poly_values_flt[1] * rem + poly_values_flt[2]; - result = poly_values_flt[0] * remSqr + result; - polynomialTerm = poly_values_flt[3] * rem + result0; + result = log2_poly_values_flt[1] * rem + log2_poly_values_flt[2]; + result = log2_poly_values_flt[0] * remSqr + result; + polynomialTerm = log2_poly_values_flt[3] * rem + result0; result = result * remSqr + polynomialTerm; return result; diff --git a/include/ccmath/detail/exponential/details/log_double_impl.hpp b/include/ccmath/detail/exponential/details/log_double_impl.hpp index d339bb8b..9bc9eae6 100644 --- a/include/ccmath/detail/exponential/details/log_double_impl.hpp +++ b/include/ccmath/detail/exponential/details/log_double_impl.hpp @@ -27,12 +27,12 @@ namespace ccm::internal namespace impl { constexpr ccm::internal::log_data internalLogDataDbl = ccm::internal::log_data(); - constexpr auto tab_values_dbl = internalLogDataDbl.tab; - constexpr auto tab2_values_dbl = internalLogDataDbl.tab2; - constexpr auto poly_values_dbl = internalLogDataDbl.poly; - constexpr auto poly1_values_dbl = internalLogDataDbl.poly1; - constexpr auto ln2hi_value_dbl = internalLogDataDbl.ln2hi; - constexpr auto ln2lo_value_dbl = internalLogDataDbl.ln2lo; + constexpr auto log_tab_values_dbl = internalLogDataDbl.tab; + constexpr auto log_tab2_values_dbl = internalLogDataDbl.tab2; + constexpr auto log_poly_values_dbl = internalLogDataDbl.poly; + constexpr auto log_poly1_values_dbl = internalLogDataDbl.poly1; + constexpr auto log_ln2hi_value_dbl = internalLogDataDbl.ln2hi; + constexpr auto log_ln2lo_value_dbl = internalLogDataDbl.ln2lo; constexpr auto k_logTableN_dbl = (1 << ccm::internal::k_logTableBitsDbl); constexpr auto k_logTableOff_dbl = 0x3fe6000000000000; @@ -52,18 +52,16 @@ namespace ccm::internal ccm::double_t lowPart{}; // Declare variables for bitwise operations - std::uint64_t intX{}; std::uint64_t intNorm{}; std::uint64_t tmp{}; - std::uint32_t top{}; // Declare variables for exponent and loop iteration - int expo{}; - int i{}; + std::int64_t expo{}; + std::int64_t i{}; // Convert input double to uint64_t and extract top 16 bits - intX = ccm::helpers::double_to_uint64(x); - top = ccm::helpers::top16_bits_of_double(x); + std::uint64_t intX = ccm::helpers::double_to_uint64(x); + std::uint32_t top = ccm::helpers::top16_bits_of_double(x); // Constants for comparison constexpr std::uint64_t low = ccm::helpers::double_to_uint64(1.0 - 0x1p-4); @@ -79,20 +77,20 @@ namespace ccm::internal rem = x - 1.0; remSqr = rem * rem; remCubed = rem * remSqr; - result = remCubed * (poly1_values_dbl[1] + rem * poly1_values_dbl[2] + remSqr * poly1_values_dbl[3] + - remCubed * (poly1_values_dbl[4] + rem * poly1_values_dbl[5] + remSqr * poly1_values_dbl[6] + - remCubed * (poly1_values_dbl[7] + rem * poly1_values_dbl[8] + remSqr * poly1_values_dbl[9] + - remCubed * poly1_values_dbl[10]))); + result = remCubed * (log_poly1_values_dbl[1] + rem * log_poly1_values_dbl[2] + remSqr * log_poly1_values_dbl[3] + + remCubed * (log_poly1_values_dbl[4] + rem * log_poly1_values_dbl[5] + remSqr * log_poly1_values_dbl[6] + + remCubed * (log_poly1_values_dbl[7] + rem * log_poly1_values_dbl[8] + remSqr * log_poly1_values_dbl[9] + + remCubed * log_poly1_values_dbl[10]))); // Additional error correction // Worst-case error is around 0.507 ULP. workspace = rem * 0x1p27; ccm::double_t rhi = rem + workspace - workspace; ccm::double_t rlo = rem - rhi; - workspace = rhi * rhi * poly1_values_dbl[0]; // poly1_values[0] == -0.5. + workspace = rhi * rhi * log_poly1_values_dbl[0]; // poly1_values[0] == -0.5. highPart = rem + workspace; lowPart = rem - highPart + workspace; - lowPart += poly1_values_dbl[0] * rlo * (rhi + rem); + lowPart += log_poly1_values_dbl[0] * rlo * (rhi + rem); result += lowPart; result += highPart; return static_cast(result); @@ -119,29 +117,29 @@ namespace ccm::internal expo = static_cast(tmp) >> 52; // NOLINTEND intNorm = intX - (tmp & 0xfffULL << 52); // Arithmetic shift - inverseCoeff = tab_values_dbl[i].invc; - logarithmCoeff = tab_values_dbl[i].logc; + inverseCoeff = log_tab_values_dbl[i].invc; + logarithmCoeff = log_tab_values_dbl[i].logc; normVal = ccm::helpers::uint64_to_double(intNorm); // Calculate intermediate value for logarithm computation // log(x) = log1p(normVal/c-1) + log(c) + expo*Ln2. // r ~= z/c - 1, |r| < 1/(2*N) - rem = (normVal - tab2_values_dbl[i].chi - tab2_values_dbl[i].clo) * inverseCoeff; + rem = (normVal - log_tab2_values_dbl[i].chi - log_tab2_values_dbl[i].clo) * inverseCoeff; scaleFactor = static_cast(expo); // Calculate high and low parts of logarithm // hi + lo = r + log(c) + expo*Ln2. - workspace = scaleFactor * ln2hi_value_dbl + logarithmCoeff; + workspace = scaleFactor * log_ln2hi_value_dbl + logarithmCoeff; highPart = workspace + rem; - lowPart = workspace - highPart + rem + scaleFactor * ln2lo_value_dbl; + lowPart = workspace - highPart + rem + scaleFactor * log_ln2lo_value_dbl; // Final computation of logarithm // log(x) = lo + (log1p(rem) - rem) + hi. remSqr = rem * rem; // rounding error: 0x1p-54/k_logTableN^2. // Worst case error if |result| > 0x1p-4: 0.520 ULP // 0.5 + 2.06/k_logTableN + abs-poly-error*2^56+0.001 ULP - result = lowPart + remSqr * poly_values_dbl[0] + - rem * remSqr * (poly_values_dbl[1] + rem * poly_values_dbl[2] + remSqr * (poly_values_dbl[3] + rem * poly_values_dbl[4])) + highPart; + result = lowPart + remSqr * log_poly_values_dbl[0] + + rem * remSqr * (log_poly_values_dbl[1] + rem * log_poly_values_dbl[2] + remSqr * (log_poly_values_dbl[3] + rem * log_poly_values_dbl[4])) + highPart; return static_cast(result); } } // namespace impl diff --git a/include/ccmath/detail/exponential/exp.hpp b/include/ccmath/detail/exponential/exp.hpp index 582eb0e5..1f7eda90 100644 --- a/include/ccmath/detail/exponential/exp.hpp +++ b/include/ccmath/detail/exponential/exp.hpp @@ -9,11 +9,7 @@ #pragma once #include "ccmath/detail/basic/abs.hpp" -#include "ccmath/detail/compare/isinf.hpp" -#include "ccmath/detail/compare/isnan.hpp" -#include "ccmath/detail/compare/signbit.hpp" -#include "ccmath/internal/helpers/find_number.hpp" -#include "ccmath/internal/helpers/pow_integral.hpp" + #include "ccmath/numbers.hpp" #include @@ -24,53 +20,14 @@ namespace ccm { namespace impl { - // see https://en.wikipedia.org/wiki/Euler%27s_continued_fraction_formula - template - constexpr T exp_cont_frac_recursive(const T x, const int depth_end, const std::size_t maxIterationDepth = 25) noexcept - { - int depth = static_cast(maxIterationDepth) - 1; - T result = T{1}; - - while (depth > depth_end) - { - result = static_cast(1) + x / static_cast(depth - 1) - x / depth / result; - --depth; - } - - return result; - } - - template - constexpr T exp_cont_frac(const T x) noexcept - { - return static_cast(1) / (static_cast(1) - x / exp_cont_frac_recursive(x, 2)); - } - - template - constexpr T exp_split(const T x) noexcept - { - return static_cast(ccm::helpers::pow_integral(ccm::numbers::e_v, ccm::helpers::find_num_whole_part(x))) * - exp_cont_frac(ccm::helpers::find_num_fract_part(x)); - } - - template - constexpr T exp_impl(const T x) noexcept - { - if (ccm::isnan(x)) { return std::numeric_limits::quiet_NaN(); } - - if (ccm::isinf(x)) { return ccm::signbit(x) ? T{0} : std::numeric_limits::infinity(); } - - if (ccm::abs(x) < std::numeric_limits::min()) { return T{1}; } - return ccm::abs(x) < static_cast(2) ? exp_cont_frac(x) : exp_split(x); - } } // namespace impl } // namespace template inline constexpr T exp(T x) { - return impl::exp_impl(x); + return 0; } } // namespace ccm diff --git a/include/ccmath/detail/exponential/log.hpp b/include/ccmath/detail/exponential/log.hpp index 3203b44f..5238f603 100644 --- a/include/ccmath/detail/exponential/log.hpp +++ b/include/ccmath/detail/exponential/log.hpp @@ -21,7 +21,7 @@ namespace ccm * * @warning ccm::log is currently only ensured to work on little-endian systems. There is currently no guarantee this it will work on big-endian systems. */ - template + template , bool> = true> inline constexpr T log(const T num) noexcept { // If the argument is ±0, -∞ is returned. diff --git a/include/ccmath/detail/exponential/log2.hpp b/include/ccmath/detail/exponential/log2.hpp index d1fc0cfd..9cc11f0e 100644 --- a/include/ccmath/detail/exponential/log2.hpp +++ b/include/ccmath/detail/exponential/log2.hpp @@ -24,7 +24,7 @@ namespace ccm * @param num The number to calculate the base 2 logarithm of. * @return The base 2 logarithm of the number. */ - template + template , int> = 0> inline constexpr T log2(T num) noexcept { // If the argument is ±0, -∞ is returned diff --git a/include/ccmath/detail/power/pow.hpp b/include/ccmath/detail/power/pow.hpp index fa5ff6ae..469c11aa 100644 --- a/include/ccmath/detail/power/pow.hpp +++ b/include/ccmath/detail/power/pow.hpp @@ -11,7 +11,6 @@ #include #include -#include "ccmath/internal/helpers/is_odd.hpp" #include diff --git a/include/ccmath/internal/helpers/bits.hpp b/include/ccmath/internal/helpers/bits.hpp index f569d6c1..a59de61e 100644 --- a/include/ccmath/internal/helpers/bits.hpp +++ b/include/ccmath/internal/helpers/bits.hpp @@ -11,6 +11,24 @@ #include #include +namespace ccm::helpers::traits +{ + // clang-format off + template struct is_char_type : std::false_type {}; + template <> struct is_char_type : std::true_type {}; + template <> struct is_char_type : std::true_type {}; +#if (__cplusplus >= 202002L) || defined(__cpp_char8_t) || defined(__cpp_lib_char8_t) // C++20 defines char8_t + template <> struct is_char_type : std::true_type {}; +#endif + template <> struct is_char_type : std::true_type {}; + template <> struct is_char_type : std::true_type {}; + template <> struct is_char_type : std::true_type {}; + template <> struct is_char_type : std::true_type {}; + template inline constexpr bool is_char_type_v = is_char_type::value; + // clang-format on + +} // namespace ccm::helpers::traits + namespace ccm::helpers { @@ -21,20 +39,22 @@ namespace ccm::helpers * @param src * @return */ - template - std::enable_if_t< - sizeof(To) == sizeof(From) && - std::is_trivially_copyable_v && - std::is_trivially_copyable_v, - To> - inline constexpr bit_cast(const From& src) noexcept - { - static_assert(std::is_trivially_constructible_v, - "This implementation additionally requires " - "destination type to be trivially constructible"); - - return __builtin_bit_cast(To, src); - } + template + std::enable_if_t && std::is_trivially_copyable_v, To> inline constexpr bit_cast( + const From & src) noexcept + { + static_assert(std::is_trivially_constructible_v, "This implementation additionally requires " + "destination type to be trivially constructible"); + + return __builtin_bit_cast(To, src); + } + + template && std::is_unsigned_v && !ccm::helpers::traits::is_char_type_v && !std::is_same_v, + bool> = true> + constexpr bool has_single_bit(T x) noexcept + { + return x && !(x & (x - 1)); + } inline constexpr std::uint32_t get_exponent_of_double(double x) noexcept { @@ -47,8 +67,6 @@ namespace ccm::helpers return exponent; } - - /** * @brief Helper function to get the top 16-bits of a double. * @param x Double to get the bits from. @@ -56,12 +74,12 @@ namespace ccm::helpers */ inline constexpr std::uint32_t top16_bits_of_double(double x) noexcept { - return bit_cast(x) >> 48; + return static_cast(bit_cast(x) >> 48); } inline constexpr std::uint32_t top12_bits_of_double(double x) noexcept { - return bit_cast(x) >> 52; + return static_cast(bit_cast(x) >> 52); } inline constexpr std::uint64_t double_to_uint64(double x) noexcept @@ -84,5 +102,4 @@ namespace ccm::helpers return bit_cast(x); } - -} +} // namespace ccm::helpers diff --git a/include/ccmath/internal/helpers/endian.hpp b/include/ccmath/internal/helpers/endian.hpp index 2422b946..c35f12e8 100644 --- a/include/ccmath/internal/helpers/endian.hpp +++ b/include/ccmath/internal/helpers/endian.hpp @@ -7,6 +7,7 @@ */ #pragma once + namespace ccm::helpers { enum class endian diff --git a/include/ccmath/internal/helpers/exponentiation_helpers.hpp b/include/ccmath/internal/helpers/exponentiation_helpers.hpp deleted file mode 100644 index 6b16c2f3..00000000 --- a/include/ccmath/internal/helpers/exponentiation_helpers.hpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2024-Present Ian Pike - * Copyright (c) 2024-Present ccmath contributors - * - * This library is provided under the MIT License. - * See LICENSE for more information. - */ - -#pragma once - -#include - -namespace ccm::helpers -{ - /** - * @brief Computes is a value is odd. - * @tparam T - * @param value - * @return - */ - template - [[nodiscard]] inline constexpr bool is_odd(T value) noexcept - { - return value % 2 != 0; - } - - /** - * @brief Computes the exponentiation of a base to a power using the exponentiation by squaring algorithm. - * @tparam T The type of the base and exponent. - * @param base The base value. - * @param exponent The exponent value. - * @return The result of the exponentiation. - * - * @attention This function is only available for signed integral types. - */ - template && std::is_signed_v, bool> = true> - [[nodiscard]] inline constexpr T exponentiation_by_squaring(T base, T exponent) noexcept - { - if (exponent < static_cast(0)) - { - base = static_cast(1) / base; - exponent = -exponent; - } - - if (exponent == static_cast(0)) { return static_cast(1); } - - auto result = static_cast(1); - while (exponent > static_cast(1)) - { - if (ccm::helpers::is_odd(exponent)) - { - result *= base; - exponent -= static_cast(1); - } - - base *= base; - exponent /= static_cast(2); - } - - return result * base; - } - - // Implement taylor series - template - [[nodiscard]] inline constexpr T taylor_series(T x, T n) noexcept - { - T result = static_cast(1); - for (T i = static_cast(1); i < n; ++i) - { - result += exponentiation_by_squaring(x, i) / i; - } - return result; - } - -} // namespace ccm::helpers diff --git a/include/ccmath/internal/helpers/fpclassify_helper.hpp b/include/ccmath/internal/helpers/fpclassify_helper.hpp index ba71638d..a71d173f 100644 --- a/include/ccmath/internal/helpers/fpclassify_helper.hpp +++ b/include/ccmath/internal/helpers/fpclassify_helper.hpp @@ -25,37 +25,38 @@ namespace ccm::helpers { - enum class floating_point_defines - { + struct floating_point_defines + { #if defined(CCMATH_COMPILER_MSVC) // Mirrors the corecrt definitions - eFP_NAN = 2, - eFP_INFINITE = 1, - eFP_ZERO = 0, - eFP_SUBNORMAL = -2, - eFP_NORMAL = -1 + constexpr static int eFP_NAN = 2; + constexpr static int eFP_INFINITE = 1; + constexpr static int eFP_ZERO = 0; + constexpr static int eFP_SUBNORMAL = -2; + constexpr static int eFP_NORMAL = -1; #elif defined(CCMATH_COMPILER_APPLE_CLANG) // Apple Clang has a different set of defines than Clang - eFP_NAN = 1, - eFP_INFINITE = 2, - eFP_ZERO = 3, - eFP_SUBNORMAL = 5, - eFP_NORMAL = 4, + constexpr static int eFP_NAN = 1; + constexpr static int eFP_INFINITE = 2; + constexpr static int eFP_ZERO = 3; + constexpr static int eFP_SUBNORMAL = 5; + constexpr static int eFP_NORMAL = 4; +#elif defined(CCMATH_COMPILER_NVIDIA_HPC) // Nvidia HPC SDK has a different set of defines than Clang + constexpr static int eFP_NAN = 0; + constexpr static int eFP_INFINITE = 1; + constexpr static int eFP_ZERO = 2; + constexpr static int eFP_SUBNORMAL = 2; + constexpr static int eFP_NORMAL = 4; #elif defined(CCMATH_COMPILER_CLANG) || defined(CCMATH_COMPILER_GCC) || defined(CCMATH_COMPILER_CLANG_BASED) // Clang and GCC have the same defines - eFP_NAN = 0, - eFP_INFINITE = 1, - eFP_ZERO = 2, - eFP_SUBNORMAL = 3, - eFP_NORMAL = 4, -#else // Unknown compiler throws a static_assert. Since FP_* macros are extremely implementation specific, we can't provide a default. - eFP_NAN = 0, - eFP_INFINITE, - eFP_ZERO, - eFP_SUBNORMAL, - eFP_NORMAL + constexpr static int eFP_NAN = 0; + constexpr static int eFP_INFINITE = 1; + constexpr static int eFP_ZERO = 2; + constexpr static int eFP_SUBNORMAL = 3; + constexpr static int eFP_NORMAL = 4; +#else static_assert(false, "FP_* macros are extremely implementation specific and are not defined for this compiler. Please add support for this compiler.") #endif - }; + }; } // Clean up the global namespace -//#include "ccmath/internal/setup/compiler_undef.hpp" +#include "ccmath/internal/setup/compiler_undef.hpp" diff --git a/include/ccmath/internal/helpers/make_mantisa.hpp b/include/ccmath/internal/helpers/make_mantisa.hpp deleted file mode 100644 index ad191660..00000000 --- a/include/ccmath/internal/helpers/make_mantisa.hpp +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (c) 2024-Present Ian Pike - * Copyright (c) 2024-Present ccmath contributors - * - * This library is provided under the MIT License. - * See LICENSE for more information. - */ - -#pragma once - -#include - -#include "ccmath/internal/helpers/not_null.hpp" - -#ifndef NON_NULL - #if defined(__has_attribute) - #if __has_attribute(nonnull) - #define NON_NULL __attribute__((nonnull)) - #else - #define NON_NULL - #endif - #else - #define NON_NULL - #endif -#endif - -namespace ccm::helpers -{ - - constexpr uint64_t make_mantissa_base8(const char * tagp NON_NULL) - { - uint64_t r = 0; - while (*tagp != '\0') - { - char tmp = *tagp; - - if (tmp >= '0' && tmp <= '7') - r = (r * 8u) + static_cast(tmp) - '0'; - else - return 0; - - ++tagp; - } - - return r; // This should never be reached - } - - constexpr uint64_t make_mantissa_base10(const char * tagp NON_NULL) - { - uint64_t r = 0; - while (*tagp != '\0') - { - char tmp = *tagp; - - if (tmp >= '0' && tmp <= '9') - r = (r * 10u) + static_cast(tmp) - '0'; - else - return 0; - - ++tagp; - } - - return r; // This should never be reached - } - - constexpr uint64_t make_mantissa_base16(const char * tagp NON_NULL) - { - uint64_t r = 0; - while (*tagp != '\0') - { - char tmp = *tagp; - - if (tmp >= '0' && tmp <= '9') - r = (r * 16u) + static_cast(tmp) - '0'; - else if (tmp >= 'a' && tmp <= 'f') - r = (r * 16u) + static_cast(tmp) - 'a' + 10; - else if (tmp >= 'A' && tmp <= 'F') - r = (r * 16u) + static_cast(tmp) - 'A' + 10; - else - return 0; - - ++tagp; - } - - return r; // This should never be reached - } - - constexpr uint64_t make_mantissa(const char * tagp NON_NULL) - { - if (*tagp == '0') - { - ++tagp; - - if (*tagp == 'x' || *tagp == 'X') - return make_mantissa_base16(tagp); - else - return make_mantissa_base8(tagp); - } - - return make_mantissa_base10(tagp); - } - -} // namespace ccm::helpers - -#undef NON_NULL diff --git a/include/ccmath/internal/helpers/narrow_cast.hpp b/include/ccmath/internal/helpers/narrow_cast.hpp deleted file mode 100644 index 12981fc0..00000000 --- a/include/ccmath/internal/helpers/narrow_cast.hpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2024-Present Ian Pike - * Copyright (c) 2024-Present ccmath contributors - * - * This library is provided under the MIT License. - * See LICENSE for more information. - */ - -#pragma once - -#include - - -// -// make suppress attributes parse for some compilers -// Hopefully temporary until suppression standardization occurs -// -#if defined(__clang__) - #define CCM_SUPPRESS(x) [[gsl::suppress(#x)]] -#else - #if defined(_MSC_VER) && !defined(__INTEL_COMPILER) && !defined(__NVCC__) - #define CCM_SUPPRESS(x) [[gsl::suppress(x)]] - #else - #define CCM_SUPPRESS(x) - #endif // _MSC_VER -#endif // __clang__ - - - -namespace ccm::helpers -{ - // NOLINTBEGIN - - // disable narrowing -#ifdef __GNUC__ - _Pragma("GCC diagnostic push") - _Pragma("GCC diagnostic ignored \"-Wnarrowing\"") -#endif -#ifdef __clang__ - _Pragma("clang diagnostic push") - _Pragma("clang diagnostic ignored \"-Wc++11-narrowing\"") -#endif - template - CCM_SUPPRESS(type.1) // NO-FORMAT: attribute - inline constexpr To narrow_cast(From&& from) noexcept - { - return static_cast(std::forward(from)); - } -#ifdef __GNUC__ - _Pragma("GCC diagnostic pop") -#endif -#ifdef __clang__ - _Pragma("clang diagnostic pop") -#endif - // NOLINTEND - -} - - -#undef GSL_SUPPRESS diff --git a/include/ccmath/internal/helpers/not_null.hpp b/include/ccmath/internal/helpers/not_null.hpp deleted file mode 100644 index c152f459..00000000 --- a/include/ccmath/internal/helpers/not_null.hpp +++ /dev/null @@ -1,338 +0,0 @@ -// Taken from gsl library - -/////////////////////////////////////////////////////////////////////////////// -// -// Copyright (c) 2015 Microsoft Corporation. All rights reserved. -// -// This code is licensed under the MIT License (MIT). -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// -/////////////////////////////////////////////////////////////////////////////// - -#ifndef CCM_POINTERS_H -#define CCM_POINTERS_H - -//#include "assert" // for Ensures, Expects - -#include // for forward -#include // for ptrdiff_t, nullptr_t, size_t -#include // for shared_ptr, unique_ptr -#include // for hash -#include // for enable_if_t, is_convertible, is_assignable -#include // for declval - - -namespace ccm::helpers -{ - - namespace details - { - template - struct is_comparable_to_nullptr : std::false_type - { - }; - - template - struct is_comparable_to_nullptr< - T, - std::enable_if_t() != nullptr), bool>::value>> - : std::true_type - { - }; - - // Resolves to the more efficient of `const T` or `const T&`, in the context of returning a const-qualified value - // of type T. - // - // Copied from cppfront's implementation of the CppCoreGuidelines F.16 (https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rf-in) - template - using value_or_reference_return_t = std::conditional_t< - sizeof(T) < 2*sizeof(void*) && std::is_trivially_copy_constructible::value, - const T, - const T&>; - - } // namespace details - - // - // GSL.owner: ownership pointers - // - using std::shared_ptr; - using std::unique_ptr; - - // - // owner - // - // `gsl::owner` is designed as a safety mechanism for code that must deal directly with raw pointers that own memory. - // Ideally such code should be restricted to the implementation of low-level abstractions. `gsl::owner` can also be used - // as a stepping point in converting legacy code to use more modern RAII constructs, such as smart pointers. - // - // T must be a pointer type - // - disallow construction from any type other than pointer type - // - template ::value>> - using owner = T; - - // - // not_null - // - // Restricts a pointer or smart pointer to only hold non-null values. - // - // Has zero size overhead over T. - // - // If T is a pointer (i.e. T == U*) then - // - allow construction from U* - // - disallow construction from nullptr_t - // - disallow default construction - // - ensure construction from null U* fails - // - allow implicit conversion to U* - // - template - class not_null - { - public: - static_assert(details::is_comparable_to_nullptr::value, "T cannot be compared to nullptr."); - - template ::value>> - constexpr not_null(U&& u) noexcept(std::is_nothrow_move_constructible::value) : ptr_(std::forward(u)) - { - Expects(ptr_ != nullptr); - } - - template ::value>> - constexpr not_null(T u) noexcept(std::is_nothrow_move_constructible::value) : ptr_(std::move(u)) - { - Expects(ptr_ != nullptr); - } - - template ::value>> - constexpr not_null(const not_null& other) noexcept(std::is_nothrow_move_constructible::value) : not_null(other.get()) - {} - - not_null(const not_null& other) = default; - not_null& operator=(const not_null& other) = default; - constexpr details::value_or_reference_return_t get() const - noexcept(noexcept(details::value_or_reference_return_t{std::declval()})) - { - return ptr_; - } - - constexpr operator T() const { return get(); } - constexpr decltype(auto) operator->() const { return get(); } - constexpr decltype(auto) operator*() const { return *get(); } - - // prevents compilation when someone attempts to assign a null pointer constant - not_null(std::nullptr_t) = delete; - not_null& operator=(std::nullptr_t) = delete; - - // unwanted operators...pointers only point to single objects! - not_null& operator++() = delete; - not_null& operator--() = delete; - not_null operator++(int) = delete; - not_null operator--(int) = delete; - not_null& operator+=(std::ptrdiff_t) = delete; - not_null& operator-=(std::ptrdiff_t) = delete; - void operator[](std::ptrdiff_t) const = delete; - - private: - T ptr_; - }; - - template - auto make_not_null(T&& t) noexcept - { - return not_null>>{std::forward(t)}; - } - - template - auto operator==(const not_null& lhs, - const not_null& rhs) noexcept(noexcept(lhs.get() == rhs.get())) - -> decltype(lhs.get() == rhs.get()) - { - return lhs.get() == rhs.get(); - } - - template - auto operator!=(const not_null& lhs, - const not_null& rhs) noexcept(noexcept(lhs.get() != rhs.get())) - -> decltype(lhs.get() != rhs.get()) - { - return lhs.get() != rhs.get(); - } - - template - auto operator<(const not_null& lhs, - const not_null& rhs) noexcept(noexcept(std::less<>{}(lhs.get(), rhs.get()))) - -> decltype(std::less<>{}(lhs.get(), rhs.get())) - { - return std::less<>{}(lhs.get(), rhs.get()); - } - - template - auto operator<=(const not_null& lhs, - const not_null& rhs) noexcept(noexcept(std::less_equal<>{}(lhs.get(), rhs.get()))) - -> decltype(std::less_equal<>{}(lhs.get(), rhs.get())) - { - return std::less_equal<>{}(lhs.get(), rhs.get()); - } - - template - auto operator>(const not_null& lhs, - const not_null& rhs) noexcept(noexcept(std::greater<>{}(lhs.get(), rhs.get()))) - -> decltype(std::greater<>{}(lhs.get(), rhs.get())) - { - return std::greater<>{}(lhs.get(), rhs.get()); - } - - template - auto operator>=(const not_null& lhs, - const not_null& rhs) noexcept(noexcept(std::greater_equal<>{}(lhs.get(), rhs.get()))) - -> decltype(std::greater_equal<>{}(lhs.get(), rhs.get())) - { - return std::greater_equal<>{}(lhs.get(), rhs.get()); - } - - // more unwanted operators - template - std::ptrdiff_t operator-(const not_null&, const not_null&) = delete; - template - not_null operator-(const not_null&, std::ptrdiff_t) = delete; - template - not_null operator+(const not_null&, std::ptrdiff_t) = delete; - template - not_null operator+(std::ptrdiff_t, const not_null&) = delete; - - - template ().get()), bool = std::is_default_constructible>::value> - struct not_null_hash - { - std::size_t operator()(const T& value) const { return std::hash{}(value.get()); } - }; - - template - struct not_null_hash - { - not_null_hash() = delete; - not_null_hash(const not_null_hash&) = delete; - not_null_hash& operator=(const not_null_hash&) = delete; - }; - -} // namespace ccm::helpers - -namespace std -{ - template - struct hash> : ccm::helpers::not_null_hash> - { - }; - -} // namespace std - -namespace ccm::helpers -{ - - // - // strict_not_null - // - // Restricts a pointer or smart pointer to only hold non-null values, - // - // - provides a strict (i.e. explicit constructor from T) wrapper of not_null - // - to be used for new code that wishes the design to be cleaner and make not_null - // checks intentional, or in old code that would like to make the transition. - // - // To make the transition from not_null, incrementally replace not_null - // by strict_not_null and fix compilation errors - // - // Expect to - // - remove all unneeded conversions from raw pointer to not_null and back - // - make API clear by specifying not_null in parameters where needed - // - remove unnecessary asserts - // - template - class strict_not_null : public not_null - { - public: - template ::value>> - constexpr explicit strict_not_null(U&& u) : not_null(std::forward(u)) - {} - - template ::value>> - constexpr explicit strict_not_null(T u) : not_null(u) - {} - - template ::value>> - constexpr strict_not_null(const not_null& other) : not_null(other) - {} - - template ::value>> - constexpr strict_not_null(const strict_not_null& other) : not_null(other) - {} - - // To avoid invalidating the "not null" invariant, the contained pointer is actually copied - // instead of moved. If it is a custom pointer, its constructor could in theory throw exceptions. - strict_not_null(strict_not_null&& other) noexcept(std::is_nothrow_copy_constructible::value) = default; - strict_not_null(const strict_not_null& other) = default; - strict_not_null& operator=(const strict_not_null& other) = default; - strict_not_null& operator=(const not_null& other) - { - not_null::operator=(other); - return *this; - } - - // prevents compilation when someone attempts to assign a null pointer constant - strict_not_null(std::nullptr_t) = delete; - strict_not_null& operator=(std::nullptr_t) = delete; - - // unwanted operators...pointers only point to single objects! - strict_not_null& operator++() = delete; - strict_not_null& operator--() = delete; - strict_not_null operator++(int) = delete; - strict_not_null operator--(int) = delete; - strict_not_null& operator+=(std::ptrdiff_t) = delete; - strict_not_null& operator-=(std::ptrdiff_t) = delete; - void operator[](std::ptrdiff_t) const = delete; - }; - - // more unwanted operators - template - std::ptrdiff_t operator-(const strict_not_null&, const strict_not_null&) = delete; - template - strict_not_null operator-(const strict_not_null&, std::ptrdiff_t) = delete; - template - strict_not_null operator+(const strict_not_null&, std::ptrdiff_t) = delete; - template - strict_not_null operator+(std::ptrdiff_t, const strict_not_null&) = delete; - - template - auto make_strict_not_null(T&& t) noexcept - { - return strict_not_null>>{std::forward(t)}; - } - -#if (defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201611L)) - - // deduction guides to prevent the ctad-maybe-unsupported warning - template - not_null(T) -> not_null; - template - strict_not_null(T) -> strict_not_null; - -#endif // ( defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201611L) ) - -} // namespace gsl - -namespace std -{ - template - struct hash> : ccm::helpers::not_null_hash> - { - }; - -} // namespace std - -#endif // CCM_POINTERS_H diff --git a/include/ccmath/internal/helpers/pow_integral.hpp b/include/ccmath/internal/helpers/pow_integral.hpp deleted file mode 100644 index 1ebcbd62..00000000 --- a/include/ccmath/internal/helpers/pow_integral.hpp +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2024-Present Ian Pike - * Copyright (c) 2024-Present ccmath contributors - * - * This library is provided under the MIT License. - * See LICENSE for more information. - */ - -#pragma once - -#include -#include - -namespace ccm::helpers -{ - template - constexpr T is_odd(const T val) noexcept - { - return val % static_cast(2) != static_cast(0); - } - - template - constexpr T compute_integral_pow(T base, U exponent) noexcept; - - // We calculate integral powers using exponentiation by squaring. - // https://en.wikipedia.org/wiki/Exponentiation_by_squaring - template - constexpr T compute_integral_pow_recursive(const T base, const T val, const U exponent) noexcept - { - if (exponent > static_cast(1)) - { - if (is_odd(exponent)) { return compute_integral_pow_recursive(base * base, val * base, exponent / static_cast(2)); } - else { return compute_integral_pow_recursive(base * base, val, exponent / static_cast(2)); } - } - else if (exponent == static_cast(1)) { return val * base; } - else { return val; } - } - - template >::value * = nullptr> - constexpr T check_sign_integral_pow(const T base, const U exponent) noexcept - { - if (exponent < static_cast(0)) { return static_cast(1) / compute_integral_pow(base, -exponent); } - else { return compute_integral_pow_recursive(base, static_cast(1), exponent); } - } - - template >::value * = nullptr> - constexpr T check_sign_integral_pow(const T base, const U exponent) noexcept - { - return compute_integral_pow_recursive(base, static_cast(1), exponent); - } - - template - constexpr T compute_integral_pow(T base, U exponent) noexcept - { - if (exponent == static_cast(0)) { return static_cast(1); } - - if (exponent == static_cast(1)) { return base; } - - if (exponent == static_cast(2)) { return base * base; } - - if (exponent == static_cast(3)) { return base * base * base; } - - if (exponent == std::numeric_limits::min()) { return static_cast(0); } - - if (exponent == std::numeric_limits::max()) { return std::numeric_limits::infinity(); } - - return check_sign_integral_pow(base, exponent); - } - - template >::value * = nullptr> - constexpr T type_check_integral_pow(const T base, const U exponent) noexcept - { - return compute_integral_pow(base, exponent); - } - - template >::value * = nullptr> - constexpr T type_check_integral_pow(const T base, const U exponent) noexcept - { - return compute_integral_pow(base, static_cast(exponent)); - } - - template - constexpr T pow_integral(const T base, const U exponent) noexcept - { - return ccm::helpers::type_check_integral_pow(base, exponent); - } - -} // namespace ccm::helpers diff --git a/include/ccmath/internal/helpers/promote.hpp b/include/ccmath/internal/helpers/promote.hpp deleted file mode 100644 index 0f74a482..00000000 --- a/include/ccmath/internal/helpers/promote.hpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2024-Present Ian Pike - * Copyright (c) 2024-Present ccmath contributors - * - * This library is provided under the MIT License. - * See LICENSE for more information. - */ - -#pragma once - -#include - -namespace ccm::helpers -{ - template > - struct promote - { - using type = double; - }; - - template - struct promote - { }; - - template<> - struct promote - { - using type = long double; - }; - - template<> - struct promote - { - using type = double; - }; - - template<> - struct promote - { - using type = float; - }; - - template - using promote_t = decltype((typename promote::type(0) + ...)); // Assume we have fold expression - - // Deducing the promoted type is done by promoted_t, - // then promote is used to provide the nested type member. - template - using promote_2 = promote>; - - template - using promote_3 = promote>; - - template - using promote_4 = promote>; - - template - using promote_2_t = typename promote_2::type; - - template - using promote_3_t = typename promote_3::type; - - template - using promote_4_t = typename promote_4::type; - - - -} diff --git a/include/ccmath/internal/setup/compiler_def.hpp b/include/ccmath/internal/setup/compiler_def.hpp index bc9dfdac..2dc41872 100644 --- a/include/ccmath/internal/setup/compiler_def.hpp +++ b/include/ccmath/internal/setup/compiler_def.hpp @@ -19,7 +19,7 @@ /// !!! ATTENTION !!! -#if defined(__GNUC__) && !defined(CCMATH_COMPILER_GCC) +#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) && !defined(CCMATH_COMPILER_GCC) #define CCMATH_COMPILER_GCC #define CCMATH_COMPILER_GCC_VER ((__GNUC__ * 10000) + (__GNUC_MINOR__ * 100) + __GNUC_PATCHLEVEL__) #define CCMATH_COMPILER_GCC_VER_MAJOR __GNUC__ @@ -89,7 +89,7 @@ #endif // Intel DPC++ Compiler -#if defined(SYCL_LANGUAGE_VERSION) && defined (__INTEL_LLVM_COMPILER) +#if defined(SYCL_LANGUAGE_VERSION) || defined(__INTEL_LLVM_COMPILER) #define CCMATH_COMPILER_INTEL #define CCMATH_COMPILER_INTEL_VER __INTEL_LLVM_COMPILER @@ -102,16 +102,13 @@ #endif // Nvidia HPC SDK -#if defined(__NVCOMPILER) || defined(__NVCOMPILER_LLVM__) && !defined(CCMATH_USING_CLANG_BASED_COMPILER) - #define CCMATH_COMPILER_NVIDIA - #define CCMATH_COMPILER_NVIDIA_VER (__NVCOMPILER_MAJOR__ * 10000 + __NVCOMPILER_MINOR__ * 100 + __NVCOMPILER_PATCHLEVEL__) - #define CCMATH_COMPILER_NVIDIA_VER_MAJOR __NVCOMPILER_MAJOR__ - #define CCMATH_COMPILER_NVIDIA_VER_MINOR __NVCOMPILER_MINOR__ - #define CCMATH_COMPILER_NVIDIA_VER_PATCH __NVCOMPILER_PATCHLEVEL__ +#if defined(__NVCOMPILER) || defined(__NVCOMPILER_LLVM__) + #define CCMATH_COMPILER_NVIDIA_HPC + #define CCMATH_COMPILER_NVIDIA_HPC_VER (__NVCOMPILER_MAJOR__ * 10000 + __NVCOMPILER_MINOR__ * 100 + __NVCOMPILER_PATCHLEVEL__) + #define CCMATH_COMPILER_NVIDIA_HPC_VER_MAJOR __NVCOMPILER_MAJOR__ + #define CCMATH_COMPILER_NVIDIA_HPC_VER_MINOR __NVCOMPILER_MINOR__ + #define CCMATH_COMPILER_NVIDIA_HPC_VER_PATCH __NVCOMPILER_PATCHLEVEL__ - #ifndef CCMATH_COMPILER_CLANG_BASED - #define CCMATH_COMPILER_CLANG_BASED - #endif #ifndef CCMATH_FOUND_COMPILER #define CCMATH_FOUND_COMPILER #endif diff --git a/test/basic/abs_test.cpp b/test/basic/abs_test.cpp index 4db3ca37..756668ec 100644 --- a/test/basic/abs_test.cpp +++ b/test/basic/abs_test.cpp @@ -8,7 +8,7 @@ #include -#include +#include #include #include diff --git a/test/basic/fdim_test.cpp b/test/basic/fdim_test.cpp index 2807a80a..1659ab86 100644 --- a/test/basic/fdim_test.cpp +++ b/test/basic/fdim_test.cpp @@ -8,7 +8,7 @@ #include -#include +#include #include #include diff --git a/test/basic/fma_test.cpp b/test/basic/fma_test.cpp index 8b86f730..339ae763 100644 --- a/test/basic/fma_test.cpp +++ b/test/basic/fma_test.cpp @@ -8,7 +8,7 @@ #include -#include +#include #include // TODO: Add more tests for fma diff --git a/test/basic/fmod_test.cpp b/test/basic/fmod_test.cpp index 857f6cfc..fac40d1f 100644 --- a/test/basic/fmod_test.cpp +++ b/test/basic/fmod_test.cpp @@ -8,7 +8,7 @@ #include -#include +#include #include #include #include diff --git a/test/basic/max_test.cpp b/test/basic/max_test.cpp index f6a56907..60837533 100644 --- a/test/basic/max_test.cpp +++ b/test/basic/max_test.cpp @@ -8,7 +8,7 @@ #include -#include +#include #include #include diff --git a/test/basic/min_test.cpp b/test/basic/min_test.cpp index 0d171e9c..7ee871bb 100644 --- a/test/basic/min_test.cpp +++ b/test/basic/min_test.cpp @@ -8,7 +8,7 @@ #include -#include +#include #include #include diff --git a/test/basic/remainder_test.cpp b/test/basic/remainder_test.cpp index faad044e..6f71168d 100644 --- a/test/basic/remainder_test.cpp +++ b/test/basic/remainder_test.cpp @@ -8,7 +8,7 @@ #include -#include +#include #include diff --git a/test/basic/remquo_test.cpp b/test/basic/remquo_test.cpp index 490e988b..3ce26cb7 100644 --- a/test/basic/remquo_test.cpp +++ b/test/basic/remquo_test.cpp @@ -8,7 +8,7 @@ #include -#include +#include #include #include diff --git a/test/ccmath_test_main.cpp b/test/ccmath_test_main.cpp index 15fd0ede..b6ac8cc5 100644 --- a/test/ccmath_test_main.cpp +++ b/test/ccmath_test_main.cpp @@ -7,6 +7,7 @@ */ #include +#include "ccmath/ccmath.hpp" int main(int argc, char** argv) diff --git a/test/compare/fpclassify_test.cpp b/test/compare/fpclassify_test.cpp index 5aad8b98..cb87d163 100644 --- a/test/compare/fpclassify_test.cpp +++ b/test/compare/fpclassify_test.cpp @@ -10,13 +10,14 @@ #include #include -#include "ccmath/detail/compare/fpclassify.hpp" +#include TEST(CcmathCompareTests, Fpclassify) { // Test that fpclassify is static_assert-able - // TODO: Having issues with static_assert for fpclassify on windows and macos - //static_assert(ccm::fpclassify(1.0) == std::fpclassify(1.0), "fpclassify has failed testing that it is static_assert-able!"); + // No implementation should be lower than -100. + // Since we are only testing that fpclassify is static_assert-able, we don't need to test the value it returns. + static_assert(ccm::fpclassify(1.0) > -100, "fpclassify has failed testing that it is static_assert-able!"); EXPECT_EQ(ccm::fpclassify(1.0), std::fpclassify(1.0)); EXPECT_EQ(ccm::fpclassify(0.0), std::fpclassify(0.0)); diff --git a/test/compare/isfinite_test.cpp b/test/compare/isfinite_test.cpp index 8230ece0..3d79b6fc 100644 --- a/test/compare/isfinite_test.cpp +++ b/test/compare/isfinite_test.cpp @@ -8,7 +8,7 @@ #include -#include +#include #include #include diff --git a/test/compare/isgreater_test.cpp b/test/compare/isgreater_test.cpp index 8a6534d2..5da42277 100644 --- a/test/compare/isgreater_test.cpp +++ b/test/compare/isgreater_test.cpp @@ -10,7 +10,7 @@ #include #include -#include "ccmath/detail/compare/isgreater.hpp" +#include TEST(CcmathCompareTests, IsGreater) { diff --git a/test/compare/isgreaterequal_test.cpp b/test/compare/isgreaterequal_test.cpp index fd3c400d..50b429a6 100644 --- a/test/compare/isgreaterequal_test.cpp +++ b/test/compare/isgreaterequal_test.cpp @@ -10,7 +10,7 @@ #include #include -#include "ccmath/detail/compare/isgreaterequal.hpp" +#include TEST(CcmathCompareTests, IsGreaterEqual) { diff --git a/test/compare/isinf_test.cpp b/test/compare/isinf_test.cpp index a2f18fc9..00f5b3d8 100644 --- a/test/compare/isinf_test.cpp +++ b/test/compare/isinf_test.cpp @@ -10,7 +10,7 @@ #include #include -#include "ccmath/detail/compare/isinf.hpp" +#include // TODO: add more tests for isinf diff --git a/test/compare/isless_test.cpp b/test/compare/isless_test.cpp index ffbfa242..810ddad0 100644 --- a/test/compare/isless_test.cpp +++ b/test/compare/isless_test.cpp @@ -10,7 +10,7 @@ #include #include -#include "ccmath/detail/compare/isless.hpp" +#include TEST(CcmathCompareTests, IsLess) { diff --git a/test/compare/islessequal_test.cpp b/test/compare/islessequal_test.cpp index 3019f041..e1ecc032 100644 --- a/test/compare/islessequal_test.cpp +++ b/test/compare/islessequal_test.cpp @@ -10,7 +10,7 @@ #include #include -#include "ccmath/detail/compare/islessequal.hpp" +#include TEST(CcmathCompareTests, IsLessEqual) { diff --git a/test/compare/islessgreater_test.cpp b/test/compare/islessgreater_test.cpp index e723de6f..1569afe3 100644 --- a/test/compare/islessgreater_test.cpp +++ b/test/compare/islessgreater_test.cpp @@ -10,7 +10,7 @@ #include #include -#include "ccmath/detail/compare/islessgreater.hpp" +#include TEST(CcmathCompareTests, IsLessGreater) { diff --git a/test/compare/isnan_test.cpp b/test/compare/isnan_test.cpp index c89fa9a4..b7b07c19 100644 --- a/test/compare/isnan_test.cpp +++ b/test/compare/isnan_test.cpp @@ -10,7 +10,7 @@ #include #include -#include "ccmath/detail/compare/isnan.hpp" +#include TEST(CcmathCompareTests, IsNan) { diff --git a/test/compare/isnormal_test.cpp b/test/compare/isnormal_test.cpp index 579dc765..cd69cdac 100644 --- a/test/compare/isnormal_test.cpp +++ b/test/compare/isnormal_test.cpp @@ -10,7 +10,7 @@ #include #include -#include "ccmath/detail/compare/isnormal.hpp" +#include TEST(CcmathCompareTests, IsNormal) { diff --git a/test/compare/isunordered_test.cpp b/test/compare/isunordered_test.cpp index d33c52b7..1beaeba3 100644 --- a/test/compare/isunordered_test.cpp +++ b/test/compare/isunordered_test.cpp @@ -10,7 +10,7 @@ #include #include -#include "ccmath/detail/compare/isunordered.hpp" +#include TEST(CcmathCompareTests, IsUnordered) { diff --git a/test/compare/signbit_test.cpp b/test/compare/signbit_test.cpp index b93ff371..1521f36a 100644 --- a/test/compare/signbit_test.cpp +++ b/test/compare/signbit_test.cpp @@ -8,7 +8,7 @@ #include #include -#include +#include #include diff --git a/test/exponential/exp2_test.cpp b/test/exponential/exp2_test.cpp index a056a2bd..b03aa601 100644 --- a/test/exponential/exp2_test.cpp +++ b/test/exponential/exp2_test.cpp @@ -10,7 +10,7 @@ #include #include -#include "ccmath/detail/exponential/exp2.hpp" +#include "ccmath/ccmath.hpp" TEST(CcmathExponentialTests, Exp2) { diff --git a/test/exponential/exp_test.cpp b/test/exponential/exp_test.cpp index f8904d48..7060435d 100644 --- a/test/exponential/exp_test.cpp +++ b/test/exponential/exp_test.cpp @@ -10,7 +10,7 @@ #include #include -#include "ccmath/detail/exponential/exp.hpp" +#include "ccmath/ccmath.hpp" TEST(CcmathExponentialTests, Exp) { diff --git a/test/exponential/expm1_test.cpp b/test/exponential/expm1_test.cpp index eef6a481..3b0f1f1b 100644 --- a/test/exponential/expm1_test.cpp +++ b/test/exponential/expm1_test.cpp @@ -10,7 +10,7 @@ #include #include -#include "ccmath/detail/exponential/expm1.hpp" +#include "ccmath/ccmath.hpp" TEST(CcmathExponentialTests, Expm1) { diff --git a/test/exponential/log10_test.cpp b/test/exponential/log10_test.cpp index 866423b7..50ec0b3e 100644 --- a/test/exponential/log10_test.cpp +++ b/test/exponential/log10_test.cpp @@ -10,7 +10,7 @@ #include #include -#include "ccmath/detail/exponential/log10.hpp" +#include "ccmath/ccmath.hpp" TEST(CcmathExponentialTests, Log10) { diff --git a/test/exponential/log1p_test.cpp b/test/exponential/log1p_test.cpp index 8dfe8032..b32d4eb8 100644 --- a/test/exponential/log1p_test.cpp +++ b/test/exponential/log1p_test.cpp @@ -10,7 +10,7 @@ #include #include -#include "ccmath/detail/exponential/log1p.hpp" +#include "ccmath/ccmath.hpp" TEST(CcmathExponentialTests, Log1p) { diff --git a/test/exponential/log2_test.cpp b/test/exponential/log2_test.cpp index 8a2727c4..da5f10d0 100644 --- a/test/exponential/log2_test.cpp +++ b/test/exponential/log2_test.cpp @@ -10,7 +10,7 @@ #include #include -#include "ccmath/detail/exponential/log2.hpp" +#include "ccmath/ccmath.hpp" TEST(CcmathExponentialTests, Log2) { diff --git a/test/exponential/log_test.cpp b/test/exponential/log_test.cpp index 002250a0..9567293c 100644 --- a/test/exponential/log_test.cpp +++ b/test/exponential/log_test.cpp @@ -10,7 +10,7 @@ #include #include -#include "ccmath/detail/exponential/log.hpp" +#include "ccmath/ccmath.hpp" TEST(CcmathExponentialTests, Log) diff --git a/test/fmanip/copysign_test.cpp b/test/fmanip/copysign_test.cpp index d0b7da87..ea62b7ca 100644 --- a/test/fmanip/copysign_test.cpp +++ b/test/fmanip/copysign_test.cpp @@ -10,7 +10,7 @@ #include #include -#include "ccmath/detail/fmanip/copysign.hpp" +#include "ccmath/ccmath.hpp" TEST(CcmathFmanipTests, Copysign) { diff --git a/test/fmanip/frexp_test.cpp b/test/fmanip/frexp_test.cpp index b3133714..45c3cc7c 100644 --- a/test/fmanip/frexp_test.cpp +++ b/test/fmanip/frexp_test.cpp @@ -10,7 +10,7 @@ #include #include -#include "ccmath/detail/fmanip/frexp.hpp" +#include "ccmath/ccmath.hpp" TEST(CcmathFmanipTests, Frexp) { diff --git a/test/fmanip/ilogb_test.cpp b/test/fmanip/ilogb_test.cpp index 80f95c41..96fb9100 100644 --- a/test/fmanip/ilogb_test.cpp +++ b/test/fmanip/ilogb_test.cpp @@ -10,7 +10,7 @@ #include #include -#include "ccmath/detail/fmanip/ilogb.hpp" +#include "ccmath/ccmath.hpp" TEST(CcmathFmanipTests, ILogb) { diff --git a/test/fmanip/ldexp_test.cpp b/test/fmanip/ldexp_test.cpp index 57b03d26..e41617dc 100644 --- a/test/fmanip/ldexp_test.cpp +++ b/test/fmanip/ldexp_test.cpp @@ -10,7 +10,7 @@ #include #include -#include "ccmath/detail/fmanip/ldexp.hpp" +#include "ccmath/ccmath.hpp" TEST(CcmathFmanipTests, Ldexp) { diff --git a/test/fmanip/logb_test.cpp b/test/fmanip/logb_test.cpp index 9c0210d3..5eff0c79 100644 --- a/test/fmanip/logb_test.cpp +++ b/test/fmanip/logb_test.cpp @@ -10,7 +10,7 @@ #include #include -#include "ccmath/detail/fmanip/logb.hpp" +#include "ccmath/ccmath.hpp" TEST(CcmathFmanipTests, Logb) { diff --git a/test/fmanip/modf_test.cpp b/test/fmanip/modf_test.cpp index 2ea8c699..9b57deff 100644 --- a/test/fmanip/modf_test.cpp +++ b/test/fmanip/modf_test.cpp @@ -10,7 +10,7 @@ #include #include -#include "ccmath/detail/fmanip/modf.hpp" +#include "ccmath/ccmath.hpp" TEST(CcmathFmanipTests, Modf) { diff --git a/test/fmanip/nextafter_test.cpp b/test/fmanip/nextafter_test.cpp index 7bb548ec..bff977c9 100644 --- a/test/fmanip/nextafter_test.cpp +++ b/test/fmanip/nextafter_test.cpp @@ -10,7 +10,7 @@ #include #include -#include "ccmath/detail/fmanip/nextafter.hpp" +#include "ccmath/ccmath.hpp" TEST(CcmathFmanipTests, Nextafter) { diff --git a/test/fmanip/scalbn_test.cpp b/test/fmanip/scalbn_test.cpp index c670ec46..4e9b401e 100644 --- a/test/fmanip/scalbn_test.cpp +++ b/test/fmanip/scalbn_test.cpp @@ -10,7 +10,7 @@ #include #include -#include "ccmath/detail/fmanip/scalbn.hpp" +#include "ccmath/ccmath.hpp" TEST(CcmathFmanipTests, Scalbn) { diff --git a/test/nearest/trunc_test.cpp b/test/nearest/trunc_test.cpp index a05cae8e..f467c4b7 100644 --- a/test/nearest/trunc_test.cpp +++ b/test/nearest/trunc_test.cpp @@ -10,8 +10,7 @@ #include #include -#include "ccmath/detail/nearest/trunc.hpp" -#include "ccmath/internal/helpers/not_null.hpp" +#include "ccmath/ccmath.hpp" // TODO: Find a way to test all of the different ways the function may behave internally to work based on the provided compiler. From 4793fe292bd20f19ab93500a9b846408e37def4c Mon Sep 17 00:00:00 2001 From: Ian Pike Date: Thu, 14 Mar 2024 01:50:20 -0400 Subject: [PATCH 3/3] Create CODE_OF_CONDUCT.md --- CODE_OF_CONDUCT.md | 128 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..3a731d5e --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +Ianpike98@gmail.com. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations.