diff --git a/.clang-format b/.clang-format index c61a8a3..0de2cb5 100644 --- a/.clang-format +++ b/.clang-format @@ -1,11 +1,12 @@ --- Language: Cpp -BasedOnStyle: Microsoft +BasedOnStyle: LLVM AccessModifierOffset: -4 AlignConsecutiveAssignments: Consecutive AlignConsecutiveMacros: Consecutive AlignEscapedNewlines: Right -AlignTrailingComments: Always +AlignTrailingComments: + Kind: Always AllowShortEnumsOnASingleLine: true AllowShortCaseLabelsOnASingleLine: true AllowShortFunctionsOnASingleLine: InlineOnly @@ -46,7 +47,7 @@ IncludeCategories: # Headers in <> with .hpp extension. - Regex: '<([A-Za-z0-9\/-_])+\.hpp>' Priority: 20 - # Headers in <> without extension. + # Headers in <> without an extension. - Regex: '<([A-Za-z0-9\/-_])+>' Priority: 30 NamespaceIndentation: All diff --git a/.clang-tidy b/.clang-tidy index 457e1ad..024233d 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,56 +1,22 @@ --- Checks: '-*, -bugprone-argument-comment, -bugprone-assert-side-effect, -bugprone-bad-signal-to-kill-thread, -bugprone-branch-clone, -bugprone-copy-constructor-init, -bugprone-dangling-handle, -bugprone-dynamic-static-initializers, -bugprone-fold-init-type, -bugprone-forward-declaration-namespace, -bugprone-forwarding-reference-overload, -bugprone-inaccurate-erase, -bugprone-incorrect-roundings, -bugprone-integer-division, -bugprone-lambda-function-name, -bugprone-macro-parentheses, -bugprone-macro-repeated-side-effects, -bugprone-misplaced-operator-in-strlen-in-alloc, -bugprone-misplaced-pointer-arithmetic-in-alloc, -bugprone-misplaced-widening-cast, -bugprone-move-forwarding-reference, -bugprone-multiple-statement-macro, -bugprone-no-escape, -bugprone-not-null-terminated-result, -bugprone-parent-virtual-call, -bugprone-posix-return, -bugprone-reserved-identifier, -bugprone-sizeof-container, -bugprone-sizeof-expression, -bugprone-spuriously-wake-up-functions, -bugprone-string-constructor, -bugprone-string-integer-assignment, -bugprone-string-literal-with-embedded-nul, -bugprone-suspicious-enum-usage, -bugprone-suspicious-include, -bugprone-suspicious-memset-usage, -bugprone-suspicious-missing-comma, -bugprone-suspicious-semicolon, -bugprone-suspicious-string-compare, -bugprone-suspicious-memory-comparison, -bugprone-suspicious-realloc-usage, -bugprone-swapped-arguments, -bugprone-terminating-continue, -bugprone-throw-keyword-missing, -bugprone-too-small-loop-variable, -bugprone-undefined-memory-manipulation, -bugprone-undelegated-constructor, -bugprone-unhandled-self-assignment, -bugprone-unused-raii, -bugprone-unused-return-value, -bugprone-use-after-move, -bugprone-virtual-near-miss, +bugprone-*, +-bugprone-easily-swappable-parameters, + +modernize-*, +-modernize-use-trailing-return-type, +-modernize-use-constraints, + +performance-*, +-performance-avoid-endl, + +readability-*, +-readability-identifier-length, +-readability-magic-numbers, + +cppcoreguidelines-*, +-cppcoreguidelines-avoid-magic-numbers, + cert-dcl21-cpp, cert-dcl58-cpp, cert-err34-c, @@ -60,88 +26,29 @@ cert-flp30-c, cert-msc50-cpp, cert-msc51-cpp, cert-str34-c, -cppcoreguidelines-interfaces-global-init, -cppcoreguidelines-narrowing-conversions, -cppcoreguidelines-pro-type-member-init, -cppcoreguidelines-pro-type-static-cast-downcast, -cppcoreguidelines-slicing, google-default-arguments, google-explicit-constructor, google-runtime-operator, +google-readability-casting, +google-upgrade-googletest-case, +llvm-include-order, +llvm-namespace-comment, hicpp-exception-baseclass, hicpp-multiway-paths-covered, +hicpp-no-assembler, +misc-const-correctness, +misc-definitions-in-headers, +misc-header-include-cycle, +misc-include-cleaner, misc-misplaced-const, misc-new-delete-overloads, misc-no-recursion, misc-non-copyable-objects, +misc-static-assert, misc-throw-by-value-catch-by-reference, misc-unconventional-assign-operator, misc-uniqueptr-reset-release, -modernize-avoid-bind, -modernize-concat-nested-namespaces, -modernize-deprecated-headers, -modernize-deprecated-ios-base-aliases, -modernize-loop-convert, -modernize-make-shared, -modernize-make-unique, -modernize-pass-by-value, -modernize-raw-string-literal, -modernize-redundant-void-arg, -modernize-replace-auto-ptr, -modernize-replace-disallow-copy-and-assign-macro, -modernize-replace-random-shuffle, -modernize-return-braced-init-list, -modernize-shrink-to-fit, -modernize-unary-static-assert, -modernize-use-auto, -modernize-use-bool-literals, -modernize-use-emplace, -modernize-use-equals-default, -modernize-use-equals-delete, -modernize-use-nodiscard, -modernize-use-noexcept, -modernize-use-nullptr, -modernize-use-override, -modernize-use-transparent-functors, -modernize-use-uncaught-exceptions, +misc-use-anonymous-namespace, mpi-buffer-deref, mpi-type-mismatch, -openmp-use-default-none, -performance-faster-string-find, -performance-for-range-copy, -performance-implicit-conversion-in-loop, -performance-inefficient-algorithm, -performance-inefficient-string-concatenation, -performance-inefficient-vector-operation, -performance-move-const-arg, -performance-move-constructor-init, -performance-no-automatic-move, -performance-noexcept-move-constructor, -performance-trivially-destructible, -performance-type-promotion-in-math-fn, -performance-unnecessary-copy-initialization, -performance-unnecessary-value-param, -portability-simd-intrinsics, -readability-avoid-const-params-in-decls, -readability-const-return-type, -readability-container-size-empty, -readability-convert-member-functions-to-static, -readability-delete-null-pointer, -readability-deleted-default, -readability-inconsistent-declaration-parameter-name, -readability-make-member-function-const, -readability-misleading-indentation, -readability-misplaced-array-index, -readability-non-const-parameter, -readability-redundant-control-flow, -readability-redundant-declaration, -readability-redundant-function-ptr-dereference, -readability-redundant-smartptr-get, -readability-redundant-string-cstr, -readability-redundant-string-init, -readability-simplify-subscript-expr, -readability-static-accessed-through-instance, -readability-static-definition-in-anonymous-namespace, -readability-string-compare, -readability-uniqueptr-delete-release, -readability-use-anyofallof' +openmp-use-default-none' diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..4bad4fd --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,5 @@ +# This file defines the owners of the code in this repository. +# See https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners for more information. + +# Assign @Rinzii as code owner for all files in the repository +* @Rinzii diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 8cebc58..0000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: "[BUG]" -labels: bug -assignees: Rinzii - ---- - -**Describe the bug** -A clear and concise description of what the bug is. - -**To Reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Screenshots** -If applicable, add screenshots to help explain your problem. - -**Environment (please complete the following information):** -- OS: [e.g. Windows 10] -- GPU: [e.g. Nvidia GTX 3070] -- Version [e.g. 0.0.1] - -**Additional context** -Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..116b710 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,76 @@ +name: Bug Report +description: Create a report to help us improve +title: '[BUG]: ' +labels: ['bug'] +body: + - type: checkboxes + attributes: + label: Due Diligence + description: Before you submit a bug report, please check the following + options: + - label: I have searched for similar issues before starting this bug report + required: true + - type: textarea + id: description + attributes: + label: Describe the bug + description: A clear and concise description of what the bug is. + placeholder: e.g. ccm::sqrt(4) is incorrectly outputting 1.9999999... instead of 2.0 on clang-12 x86-64 + validations: + required: true + - type: textarea + id: reproduction + attributes: + label: Steps to reproduce + description: Steps used to reproduce the bug. + placeholder: | + 1. Set the compiler to clang-12 on x86-64 + 2. Write the following code: + ```cpp + #include // std::cout and std::endl + #include + int main() { + std::cout << ccm::sqrt(4) << std::endl; + } + ``` + 3. Execute the program + 4. See the output + validations: + required: true + - type: textarea + id: expected-behavior + attributes: + label: Expected behavior + description: A clear and concise description of what you expected to happen. + placeholder: The output should be outputting 2.0 as this is the square root of 4, but it is outputting 1.9999999... instead. + validations: + required: true + - type: markdown + attributes: + value: '# System information' + - type: input + id: compiler-version-and-type + attributes: + label: Compiler version and type + description: The name of your compile and it's version + placeholder: GCC 13.2.1 (20230801) + validations: + required: true + - type: input + id: operating-system + attributes: + label: Operating System + description: + If applicable (i.e, an issue with how the code compiles for your OS) the OS version this bug is happening in + placeholder: Ubuntu Linux 22.04 + validations: + required: false + - type: textarea + id: screenshots + attributes: + label: Screenshots + description: If applicable, add screenshots to help explain your problem. + placeholder: As you can see here, the output is not showing up + value: Not applicable + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 524940e..0000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: "[FR]" -labels: enhancement -assignees: '' - ---- - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. - -**Additional context** -Add any other context or screenshots about the feature request here. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..70e35e2 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1 @@ + diff --git a/CMakeLists.txt b/CMakeLists.txt index f5f2655..e426b3f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.18) -set(CCMATH_BUILD_VERSION 0.1.2) +set(CCMATH_BUILD_VERSION 0.1.3) set(INTERNAL_PROJ_DEFAULT_NAME ccmath) project(${INTERNAL_PROJ_DEFAULT_NAME} VERSION ${CCMATH_BUILD_VERSION}) @@ -20,6 +20,7 @@ option(CCMATH_BUILD_EXAMPLES "Build ccmath examples" ${is_root_project}) option(CCMATH_BUILD_BENCHMARKS "Build ccmath benchmarks" ${is_root_project}) option(CCMATH_INSTALL "Setup install and package steps" ${is_root_project}) option(CCMATH_USE_SIMD "Use SIMD instructions" OFF) # SIMD is not yet implemented. +option(CCMATH_ENABLE_EXTENSIONS "Enable the extended ccmath library that adds helpful additional methods that are not defined by the standard" ON) # include the global configuration file include(cmake/GlobalConfig.cmake) @@ -31,6 +32,7 @@ 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. @@ -52,6 +54,11 @@ add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) target_sources(${PROJECT_NAME} INTERFACE "$") +if (CCMATH_ENABLE_EXTENSIONS) + include(ccmath_extensions_headers.cmake) + target_sources(${PROJECT_NAME} INTERFACE "$") +endif() + target_include_directories(${PROJECT_NAME} INTERFACE $) target_include_directories(${PROJECT_NAME} SYSTEM INTERFACE $/include>) @@ -65,7 +72,7 @@ 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) + add_subdirectory(thirdparty) endif() if (CCMATH_BUILD_EXAMPLES) diff --git a/CMakePresets.json b/CMakePresets.json index 2cdcab1..f53ff7c 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -92,6 +92,11 @@ "configurePreset": "default", "configuration": "Release" }, + { + "name": "Release-clang", + "configurePreset": "ninja-clang", + "configuration": "Release" + }, { "name": "RelWithDebInfo", "configurePreset": "default", @@ -111,6 +116,12 @@ "configuration": "Release", "inheritConfigureEnvironment": true }, + { + "name": "Release-clang", + "configurePreset": "ninja-clang", + "configuration": "Release", + "inheritConfigureEnvironment": true + }, { "name": "RelWithDebInfo", "configurePreset": "default", diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 3a731d5..7c92828 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -60,7 +60,7 @@ representative at an online or offline event. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at -Ianpike98@gmail.com. +Ianpike98(at)gmail(dot)com. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0d5b8e3..5c4884a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,3 +1,16 @@ # Contribution Guidelines +> [!IMPORTANT] +> Currently the contribution guidelines are a work in progress. Please check back later for more information. + +## Core Tenets of CCMath + +* All implementations have to be constexpr compatible. +* All implementations MUST have the same output as the standards implementation. +* All implementations must at least work towards working with static_assert +* All implementations must aim to have comparable speeds to the standard. +* All implementations must be well tested. +* All implementations must be well documented with doxygen. +* Aim to have a consistent API with the standard library. + WIP diff --git a/LICENSE b/LICENSE index aadfaf3..2b97844 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ MIT License Copyright (c) 2024-Present Ian Pike -Copyright (c) 2024-Present cmath contributors +Copyright (c) 2024-Present CCMath contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 9130f02..c530b60 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,23 @@ -# ccmath - A C++17 constexpr-Compatible CMath Library +# CCMath - A C++17 constexpr-Compatible CMath Library -ccmath is a C++17 library that provides a re-implementation of the standard `` library with all features made `constexpr`. This enables compile-time evaluation of mathematical functions, improving performance and allowing for more efficient code in scenarios where constant expressions are required. +CCMath is a C++17 library that provides a re-implementation of the standard `` library with all features made `constexpr`. +This enables compile-time evaluation of mathematical functions, +improving performance and allows for more efficient code in scenarios where constant expressions are required. ## Features -- **Full constexpr Compatibility**: All functions provided by ccmath are implemented as `constexpr` along with an active effort made to ensure all functions work within `static_assert`. The primary goal is to ensure every function can be evaluated at compile time. +- **Full constexpr Compatibility**: All functions provided by CCMath are implemented as `constexpr` along with an active effort made to ensure all functions work within `static_assert`. The primary goal is to ensure every function can be evaluated at compile time. -- **Drop In 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. +- **Drop-in Replacement for the 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 discernible 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**: Besides all 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 standard implementation. +- **Performance Optimization**: Besides all 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 standard implementation. -- **No External Dependencies**: ccmath has no external dependencies and only requires a C++17-compliant compiler. +- **No External Dependencies**: CCMath has no external dependencies and only requires a modern C++17-compliant compiler. ## Usage -To use ccmath in your projects, simply include the ccmath.hpp header file and start using the provided functions. Here's a basic example: +To use CCMath in your projects, include the `` header file and start using the provided functions. +Here's a basic example: ```cpp @@ -28,9 +31,9 @@ int main() { } ``` -## Adding ccmath to your project +## Adding CCMath to your project -ccmath has a comprehensive cmake setup and can be easily included in your project using fetchcontent like so: +CCMath has a comprehensive cmake setup and can be easily included in your project using fetchcontent like so: ```cmake cmake_minimum_required(VERSION 3.18) @@ -59,7 +62,7 @@ target_link_libraries(main PRIVATE ccmath::ccmath) ## Contributing -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. +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](CONTRIBUTING.md)! ## Implementation Progress (Modules) | Module | % done | In Progress? | Notes? | Planned Completion Version | @@ -67,105 +70,113 @@ CCmath is an open-source project, and it needs your help to go on growing and im | Basic | 100 | | | v0.1.0 (Released) | | Compare | 100 | | | v0.2.0 | | Exponential | 33 | ✓ | | v0.2.0 | -| Float Manipulation | 12 | | | +| Float Manipulation | 20 | | | | Hyperbolic | 0 | | | | Nearest | 33 | | | | Power | 5 | | | | Special Functions | 0 | | | | Trigonometric | 0 | | | -| Misc Functions | 10 | | | +| Misc Functions | 30 | | | -> Last Updated: Mar 14, 2024 +> Last Updated: Apr 2, 2024 ## Implementation Progress (Functions) -| Feature | % done | TODO | -|----------------|--------|-------------------------------------------------------------------------------------| -| abs | 100 | | -| fdim | 100 | | -| fma | 100 | Functional but more fallbacks are desired. | -| (f)max | 100 | | -| (f)min | 100 | | -| remainder | 100 | | -| remquo | 100 | | -| fpclassify | 100 | | -| isfinite | 100 | | -| isgreater | 100 | | -| isgreaterequal | 100 | | -| isinf | 100 | | -| isless | 100 | | -| islessequal | 100 | | -| islessgreater | 100 | | -| isnan | 100 | | -| 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 | 100 | Functional, but fallbacks without requiring recent compiler versions is desired. | -| log1p | 0 | Implement function | -| log2 | 100 | Functional, but fallbacks without requiring recent compiler versions is desired. | -| log10 | 0 | Implement function | -| copysign | 100 | | -| frexp | 0 | Implement function | -| ilogb | 0 | Implement function | -| ldexp | 0 | Implement function | -| logb | 0 | Implement function | -| modf | 0 | Implement function | -| nextafter | 0 | Implement function | -| scalbn | 0 | Implement function | -| acosh | 0 | Implement function | -| asinh | 0 | Implement function | -| atanh | 0 | Implement function | -| cosh | 0 | Implement function | -| sinh | 0 | Implement function | -| tanh | 0 | Implement function | -| ceil | 0 | Implement function | -| floor | 100 | | -| nearbyint | 0 | Implement function | -| rint | 0 | Implement function | -| round | 0 | Implement function | -| trunc | 100 | | -| cbrt | 0 | Implement function | -| hypot | 0 | Implement function | -| pow | 20 | Continue implementation process and add documentation and tests | -| sqrt | 0 | Implement function | -| assoc_laguerre | 0 | Implement function | -| assoc_legendre | 0 | Implement function | -| beta | 0 | Implement function | -| comp_ellint_1 | 0 | Implement function | -| comp_ellint_2 | 0 | Implement function | -| comp_ellint_3 | 0 | Implement function | -| cyl_bessel_i | 0 | Implement function | -| cyl_bessel_j | 0 | Implement function | -| cyl_bessel_k | 0 | Implement function | -| cyl_neumann | 0 | Implement function | -| ellint_1 | 0 | Implement function | -| ellint_2 | 0 | Implement function | -| ellint_3 | 0 | Implement function | -| expint | 0 | Implement function | -| hermite | 0 | Implement function | -| laguerre | 0 | Implement function | -| legendre | 0 | Implement function | -| riemann_zeta | 0 | Implement function | -| sph_bessel | 0 | Implement function | -| sph_legendre | 0 | Implement function | -| sph_neumann | 0 | Implement function | -| acos | 0 | Implement function | -| asin | 0 | Implement function | -| atan | 0 | Implement function | -| atan2 | 0 | Implement function | -| cos | 0 | Implement function | -| sin | 0 | Implement function | -| tan | 0 | Implement function | -| gamma | 0 | Implement function | -| lerp | 30 | Need to finish implementation process along with handling edge cases and promotion. | -| lgamma | 0 | Implement function | - -> Last Updated: Mar 14, 2024 +| Feature | % done | TODO | +|----------------|--------|---------------------------------------------------------------------------------------| +| abs | 100 | | +| fdim | 100 | | +| fma | 100 | Functional. Faster implementations desired. | +| (f)max | 100 | | +| (f)min | 100 | | +| remainder | 100 | | +| remquo | 100 | | +| nan | 100 | The nan function is still quite brittle, but it has reached a point of functionality. | +| fpclassify | 100 | | +| isfinite | 100 | | +| isgreater | 100 | | +| isgreaterequal | 100 | | +| isinf | 100 | | +| isless | 100 | | +| islessequal | 100 | | +| islessgreater | 100 | | +| isnan | 100 | | +| isnormal | 100 | | +| isunordered | 100 | | +| signbit | 100 | Desire to find a manner of implementing signbit on lower versions of MSVC. | +| exp | 90 | Nearly finished. Just need some minor adjustments and better test coverage. | +| exp2 | 0 | Implement function | +| expm1 | 0 | Implement function | +| log | 100 | Functional, but fallbacks without requiring recent compiler versions is desired. | +| log1p | 0 | Implement function | +| log2 | 100 | Functional, but fallbacks without requiring recent compiler versions is desired. | +| log10 | 0 | Implement function | +| copysign | 100 | | +| frexp | 0 | Implement function | +| ilogb | 0 | Implement function | +| ldexp | 0 | Implement function | +| logb | 0 | Implement function | +| modf | 0 | Implement function | +| nextafter | 0 | Implement function | +| scalbn | 80 | Still need to implement the long double version of scalbn. | +| acosh | 0 | Implement function | +| asinh | 0 | Implement function | +| atanh | 0 | Implement function | +| cosh | 0 | Implement function | +| sinh | 0 | Implement function | +| tanh | 0 | Implement function | +| ceil | 0 | Implement function | +| floor | 100 | | +| nearbyint | 0 | Implement function | +| rint | 0 | Implement function | +| round | 0 | Implement function | +| trunc | 100 | | +| cbrt | 0 | Implement function | +| hypot | 0 | Implement function | +| pow | 20 | Continue implementation process and add documentation and tests | +| sqrt | 0 | Implement function | +| assoc_laguerre | 0 | Implement function | +| assoc_legendre | 0 | Implement function | +| beta | 0 | Implement function | +| comp_ellint_1 | 0 | Implement function | +| comp_ellint_2 | 0 | Implement function | +| comp_ellint_3 | 0 | Implement function | +| cyl_bessel_i | 0 | Implement function | +| cyl_bessel_j | 0 | Implement function | +| cyl_bessel_k | 0 | Implement function | +| cyl_neumann | 0 | Implement function | +| ellint_1 | 0 | Implement function | +| ellint_2 | 0 | Implement function | +| ellint_3 | 0 | Implement function | +| expint | 0 | Implement function | +| hermite | 0 | Implement function | +| laguerre | 0 | Implement function | +| legendre | 0 | Implement function | +| riemann_zeta | 0 | Implement function | +| sph_bessel | 0 | Implement function | +| sph_legendre | 0 | Implement function | +| sph_neumann | 0 | Implement function | +| acos | 0 | Implement function | +| asin | 0 | Implement function | +| atan | 0 | Implement function | +| atan2 | 0 | Implement function | +| cos | 0 | Implement function | +| sin | 0 | Implement function | +| tan | 0 | Implement function | +| gamma | 0 | Implement function | +| lerp | 90 | Nearly finished, just need to test the function further and validate full compliance. | +| lgamma | 0 | Implement function | + +> Last Updated: Apr 2, 2024 ## License -ccmath is distributed under the MIT License. See the LICENSE file for more information. +CCMath is distributed under the MIT License. See the LICENSE file for more information. + + +## Special Thanks + +Thanks a lot to LLVM, GCC, +and LibM for a lot of insights and inspiration for many different tough issues +encountered through out the development of ccmath! diff --git a/benchmark/CMakeLists.txt b/benchmark/CMakeLists.txt index afbb31e..2d7b56e 100644 --- a/benchmark/CMakeLists.txt +++ b/benchmark/CMakeLists.txt @@ -18,6 +18,7 @@ target_link_libraries(${PROJECT_NAME} PUBLIC ) target_include_directories(${PROJECT_NAME} PUBLIC .) target_sources(${PROJECT_NAME} PRIVATE + helpers/randomizers.h ccmath_benchmark_main.cpp ) diff --git a/benchmark/ccmath_benchmark_main.cpp b/benchmark/ccmath_benchmark_main.cpp index 57e2954..beb1767 100644 --- a/benchmark/ccmath_benchmark_main.cpp +++ b/benchmark/ccmath_benchmark_main.cpp @@ -7,11 +7,13 @@ */ #include -#include #include +#include -#include #include "ccmath/ccmath.hpp" +#include + +// NOLINTBEGIN namespace bm = benchmark; @@ -40,6 +42,27 @@ std::vector generateRandomDoubles(size_t count, unsigned int seed) { return randomDouble; } +/* + +static void BM_std_fma(bm::State& state) { + for ([[maybe_unused]] auto _ : state) { + bm::DoNotOptimize(std::fma(state.range(0), state.range(1), state.range(2))); + } + state.SetComplexityN(state.range(0)); +} +BENCHMARK(BM_std_fma)->Args({16, 16, 16})->Args({256, 256, 256})->Args({4096, 4096, 4096})->Args({65536, 65536, 65536})->Complexity(); + +static void BM_ccm_fma(bm::State& state) { + for ([[maybe_unused]] auto _ : state) { + bm::DoNotOptimize(ccm::fma(state.range(0), state.range(1), state.range(2))); + } + state.SetComplexityN(state.range(0)); +} +BENCHMARK(BM_ccm_fma)->Args({16, 16, 16})->Args({256, 256, 256})->Args({4096, 4096, 4096})->Args({65536, 65536, 65536})->Complexity(); + +*/ + +/* // 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); @@ -63,6 +86,8 @@ static void BM_ccm_abs_rand_int(benchmark::State& state) { 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) { @@ -106,6 +131,28 @@ static void BM_ccm_abs(benchmark::State& state) { BENCHMARK(BM_ccm_abs)->Arg(16)->Arg(256)->Arg(4096)->Arg(65536)->Complexity(); +static void BM_std_log_rand_double(bm::State& state) { + auto randomDoubles = generateRandomDoubles(static_cast(state.range(0)), DefaultSeed); + while (state.KeepRunning()) { + for (auto x : randomDoubles) { + bm::DoNotOptimize(std::log(x)); + } + } + state.SetComplexityN(state.range(0)); +} +BENCHMARK(BM_std_log_rand_double)->Range(8, 8<<10)->Complexity(); + +static void BM_ccm_log_rand_double(bm::State& state) { + auto randomDoubles = generateRandomDoubles(static_cast(state.range(0)), DefaultSeed); + while (state.KeepRunning()) { + for (auto x : randomDoubles) { + bm::DoNotOptimize(ccm::log(x)); + } + } + state.SetComplexityN(state.range(0)); +} +BENCHMARK(BM_ccm_log_rand_double)->Range(8, 8<<10)->Complexity(); + static void BM_ccm_log(bm::State& state) { for (auto _ : state) { bm::DoNotOptimize(ccm::log(state.range(0))); @@ -122,6 +169,28 @@ static void BM_std_log(bm::State& state) { } BENCHMARK(BM_std_log)->Arg(16)->Arg(256)->Arg(4096)->Arg(65536)->Complexity(); +static void BM_std_log2_rand_double(bm::State& state) { + auto randomDoubles = generateRandomDoubles(static_cast(state.range(0)), DefaultSeed); + while (state.KeepRunning()) { + for (auto x : randomDoubles) { + bm::DoNotOptimize(std::log2(x)); + } + } + state.SetComplexityN(state.range(0)); +} +BENCHMARK(BM_std_log2_rand_double)->Range(8, 8<<10)->Complexity(); + +static void BM_ccm_log2_rand_double(bm::State& state) { + auto randomDoubles = generateRandomDoubles(static_cast(state.range(0)), DefaultSeed); + while (state.KeepRunning()) { + for (auto x : randomDoubles) { + bm::DoNotOptimize(ccm::log2(x)); + } + } + state.SetComplexityN(state.range(0)); +} +BENCHMARK(BM_ccm_log2_rand_double)->Range(8, 8<<10)->Complexity(); + static void BM_ccm_log2(bm::State& state) { for (auto _ : state) { bm::DoNotOptimize(ccm::log2(state.range(0))); @@ -138,4 +207,75 @@ static void BM_std_log2(bm::State& state) { } BENCHMARK(BM_std_log2)->Arg(16)->Arg(256)->Arg(4096)->Arg(65536)->Complexity(); +static void BM_ccm_lerp(bm::State& state) { + for (auto _ : state) { + bm::DoNotOptimize(ccm::lerp(state.range(0), state.range(1), state.range(2))); + } + state.SetComplexityN(state.range(0)); +} +BENCHMARK(BM_ccm_lerp)->Args({16, 16, 16})->Args({256, 256, 256})->Args({4096, 4096, 4096})->Args({65536, 65536, 65536})->Complexity(); + +static void BM_ccm_lerp2(bm::State& state) { + for (auto _ : state) { + bm::DoNotOptimize(ccm::lerp2(state.range(0), state.range(1), state.range(2))); + } + state.SetComplexityN(state.range(0)); +} +BENCHMARK(BM_ccm_lerp2)->Args({16, 16, 16})->Args({256, 256, 256})->Args({4096, 4096, 4096})->Args({65536, 65536, 65536})->Complexity(); + + +static void BM_std_lerp(bm::State& state) { + for (auto _ : state) { + bm::DoNotOptimize(ccm::lerp(state.range(0), state.range(1), state.range(2))); + } + state.SetComplexityN(state.range(0)); +} +BENCHMARK(BM_std_lerp)->Args({16, 16, 16})->Args({256, 256, 256})->Args({4096, 4096, 4096})->Args({65536, 65536, 65536})->Complexity(); + +*/ + +// fma +static void BM_std_fma(bm::State& state) { + for ([[maybe_unused]] auto _ : state) { + bm::DoNotOptimize(std::fma(state.range(0), state.range(1), state.range(2))); + } + state.SetComplexityN(state.range(0)); +} +BENCHMARK(BM_std_fma)->Args({16, 16, 16})->Args({256, 256, 256})->Args({4096, 4096, 4096})->Args({65536, 65536, 65536})->Complexity(); + +static void BM_ccm_fma(bm::State& state) { + for ([[maybe_unused]] auto _ : state) { + bm::DoNotOptimize(ccm::fma(state.range(0), state.range(1), state.range(2))); + } + state.SetComplexityN(state.range(0)); +} +BENCHMARK(BM_ccm_fma)->Args({16, 16, 16})->Args({256, 256, 256})->Args({4096, 4096, 4096})->Args({65536, 65536, 65536})->Complexity(); + +static void BM_std_fma_rand_double(bm::State& state) { + auto randomDoubles = generateRandomDoubles(static_cast(state.range(0)), DefaultSeed); + while (state.KeepRunning()) { + for (auto x : randomDoubles) { + bm::DoNotOptimize(std::fma(x, x, x)); + } + } + state.SetComplexityN(state.range(0)); +} +BENCHMARK(BM_std_fma_rand_double)->Range(8, 8<<10)->Complexity(); + +static void BM_ccm_fma_rand_double(bm::State& state) { + auto randomDoubles = generateRandomDoubles(static_cast(state.range(0)), DefaultSeed); + while (state.KeepRunning()) { + for (auto x : randomDoubles) { + bm::DoNotOptimize(ccm::fma(x, x, x)); + } + } + state.SetComplexityN(state.range(0)); +} +BENCHMARK(BM_ccm_fma_rand_double)->Range(8, 8<<10)->Complexity(); + + + BENCHMARK_MAIN(); + + +// NOLINTEND diff --git a/benchmark/helpers/randomizers.h b/benchmark/helpers/randomizers.h new file mode 100644 index 0000000..302dfdc --- /dev/null +++ b/benchmark/helpers/randomizers.h @@ -0,0 +1,37 @@ +/* + * 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 + +// 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; +} diff --git a/benchmark/tools/plot.py b/benchmark/tools/plot.py new file mode 100644 index 0000000..815ca82 --- /dev/null +++ b/benchmark/tools/plot.py @@ -0,0 +1,165 @@ +#!/usr/bin/env python +"""Script to visualize google-benchmark output""" +import argparse +import sys +import logging +import json +import pandas as pd +import matplotlib.pyplot as plt +import pathlib + +# Borrowed from: https://github.com/lakshayg/google_benchmark_plot/blob/master/plot.py + +logging.basicConfig(format="[%(levelname)s] %(message)s") + +METRICS = [ + "real_time", + "cpu_time", + "bytes_per_second", + "items_per_second", + "iterations", +] +TRANSFORMS = {"": lambda x: x, "inverse": lambda x: 1.0 / x} + + +def get_default_ylabel(args): + """Compute default ylabel for commandline args""" + label = "" + if args.transform == "": + label = args.metric + else: + label = args.transform + "(" + args.metric + ")" + if args.relative_to is not None: + label += " relative to %s" % args.relative_to + return label + + +def parse_args(): + """Parse commandline arguments""" + parser = argparse.ArgumentParser(description="Visualize google-benchmark output") + parser.add_argument( + "-f", + metavar="FILE", + type=argparse.FileType("r"), + default=sys.stdin, + dest="file", + help="path to file containing the csv or json benchmark data", + ) + parser.add_argument( + "-m", + metavar="METRIC", + choices=METRICS, + default=METRICS[0], + dest="metric", + help="metric to plot on the y-axis, valid choices are: %s" % ", ".join(METRICS), + ) + parser.add_argument( + "-t", + metavar="TRANSFORM", + choices=TRANSFORMS.keys(), + default="", + help="transform to apply to the chosen metric, valid choices are: %s" + % ", ".join(list(TRANSFORMS)), + dest="transform", + ) + parser.add_argument( + "-r", + metavar="RELATIVE_TO", + type=str, + default=None, + dest="relative_to", + help="plot metrics relative to this label", + ) + parser.add_argument( + "--xlabel", type=str, default="input size", help="label of the x-axis" + ) + parser.add_argument("--ylabel", type=str, help="label of the y-axis") + parser.add_argument("--title", type=str, default="", help="title of the plot") + parser.add_argument( + "--logx", action="store_true", help="plot x-axis on a logarithmic scale" + ) + parser.add_argument( + "--logy", action="store_true", help="plot y-axis on a logarithmic scale" + ) + parser.add_argument( + "--output", type=str, default="", help="File in which to save the graph" + ) + + args = parser.parse_args() + if args.ylabel is None: + args.ylabel = get_default_ylabel(args) + return args + + +def parse_input_size(name): + splits = name.split("/") + if len(splits) == 1: + return 1 + return int(splits[1]) + + +def read_data(args): + """Read and process dataframe using commandline args""" + extension = pathlib.Path(args.file.name).suffix + try: + if extension == ".csv": + data = pd.read_csv(args.file, usecols=["name", args.metric]) + elif extension == ".json": + json_data = json.load(args.file) + data = pd.DataFrame(json_data["benchmarks"]) + else: + logging.error("Unsupported file extension '{}'".format(extension)) + exit(1) + except ValueError: + logging.error( + 'Could not parse the benchmark data. Did you forget "--benchmark_format=[csv|json] when running the benchmark"?' + ) + exit(1) + data["label"] = data["name"].apply(lambda x: x.split("/")[0]) + data["input"] = data["name"].apply(parse_input_size) + data[args.metric] = data[args.metric].apply(TRANSFORMS[args.transform]) + return data + + +def plot_groups(label_groups, args): + """Display the processed data""" + for label, group in label_groups.items(): + plt.plot(group["input"], group[args.metric], label=label, marker=".") + if args.logx: + plt.xscale("log") + if args.logy: + plt.yscale("log") + plt.xlabel(args.xlabel) + plt.ylabel(args.ylabel) + plt.title(args.title) + plt.legend() + if args.output: + logging.info("Saving to %s" % args.output) + plt.savefig(args.output) + else: + plt.show() + + +def main(): + """Entry point of the program""" + args = parse_args() + data = read_data(args) + label_groups = {} + for label, group in data.groupby("label"): + label_groups[label] = group.set_index("input", drop=False) + if args.relative_to is not None: + try: + baseline = label_groups[args.relative_to][args.metric].copy() + except KeyError as key: + msg = "Key %s is not present in the benchmark output" + logging.error(msg, str(key)) + exit(1) + + if args.relative_to is not None: + for label in label_groups: + label_groups[label][args.metric] /= baseline + plot_groups(label_groups, args) + + +if __name__ == "__main__": + main() diff --git a/ccmath_extensions_headers.cmake b/ccmath_extensions_headers.cmake new file mode 100644 index 0000000..4eab56d --- /dev/null +++ b/ccmath_extensions_headers.cmake @@ -0,0 +1,6 @@ + + +set(ccmath_extensions_headers + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/extensions/lerp_smooth.hpp + +) diff --git a/ccmath_headers.cmake b/ccmath_headers.cmake index 87c7d79..1bbc61a 100644 --- a/ccmath_headers.cmake +++ b/ccmath_headers.cmake @@ -2,42 +2,55 @@ # Internal headers ########################################## +set(ccmath_internal_config_headers + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/config/compiler.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/config/builtin_support.hpp +) + 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/find_number.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/helpers/floating_point_type.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/helpers/digit_to_int.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/helpers/fpclassify_helper.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/helpers/exp_helpers.hpp ) set(ccmath_internal_predef_headers + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/predef/compiler_warnings_and_errors.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/predef/has_attribute.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/predef/likely.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/predef/unlikely.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/predef/no_debug.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/predef/suppress.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/predef/has_builtin.hpp ) -set(ccmath_internal_setup_headers - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/setup/compiler_def.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/setup/compiler_undef.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/setup/builtin_support_def.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/setup/builtin_support_undef.hpp - -) - -set(ccmath_internal_typetraits_headers - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/type_traits/is_constant_evaluated.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/type_traits/floating_point_traits.hpp +set(ccmath_internal_support_headers + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/support/bits.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/support/endian.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/support/floating_point_traits.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/support/is_constant_evaluated.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/support/meta_compare.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/support/unreachable.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/support/ctz.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/support/type_identity.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/support/always_false.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/support/limits.hpp ) -set(ccmath_internal_utility_headers - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/utility/unreachable.hpp +set(ccmath_internal_types_headers + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/types/fp_types.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/types/number_pair.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/types/sign.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/types/float128.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/types/big_int.hpp ) set(ccmath_internal_headers ${ccmath_internal_helpers_headers} ${ccmath_internal_predef_headers} - ${ccmath_internal_setup_headers} - ${ccmath_internal_typetraits_headers} - ${ccmath_internal_utility_headers} + ${ccmath_internal_config_headers} + ${ccmath_internal_support_headers} + ${ccmath_internal_types_headers} ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/setup.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/version.hpp @@ -45,149 +58,162 @@ set(ccmath_internal_headers ########################################## -# Detail headers +# Math headers ########################################## -set(ccmath_detail_basic_impl_headers - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/basic/impl/remquo_float_impl.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/basic/impl/remquo_double_impl.hpp -) - -set(ccmath_detail_basic_headers - ${ccmath_detail_basic_impl_headers} - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/basic/abs.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/basic/fdim.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/basic/fma.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/basic/fmod.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/basic/max.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/basic/min.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/basic/remainder.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/basic/remquo.hpp -) - -set(ccmath_detail_compare_headers - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/compare/fpclassify.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/compare/isfinite.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/compare/isgreater.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/compare/isgreaterequal.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/compare/isinf.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/compare/isless.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/compare/islessequal.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/compare/islessgreater.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/compare/isnan.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/compare/isnormal.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/compare/isunordered.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/compare/signbit.hpp -) - -set(ccmath_detail_exponential_impl_headers - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/exponential/impl/log_float_impl.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/exponential/impl/log_double_impl.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/exponential/impl/log_data.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/exponential/impl/log2_float_impl.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/exponential/impl/log2_double_impl.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/exponential/impl/log2_data.hpp -) - -set(ccmath_detail_exponential_headers - ${ccmath_detail_exponential_impl_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 - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/exponential/log.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/exponential/log1p.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/exponential/log2.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/exponential/log10.hpp -) - -set(ccmath_detail_fmanip_headers - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/fmanip/copysign.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/fmanip/frexp.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/fmanip/ilogb.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/fmanip/ldexp.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/fmanip/logb.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/fmanip/modf.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/fmanip/nextafter.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/fmanip/scalbn.hpp -) - -set(ccmath_detail_hyperbolic_headers - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/hyperbolic/acosh.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/hyperbolic/asinh.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/hyperbolic/atanh.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/hyperbolic/cosh.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/hyperbolic/sinh.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/hyperbolic/tanh.hpp +set(ccmath_math_basic_impl_headers + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/basic/impl/nan_float_impl.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/basic/impl/nan_double_impl.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/basic/impl/remquo_float_impl.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/basic/impl/remquo_double_impl.hpp +) + +set(ccmath_math_basic_headers + ${ccmath_math_basic_impl_headers} + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/basic/abs.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/basic/fdim.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/basic/fma.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/basic/fmod.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/basic/max.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/basic/min.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/basic/remainder.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/basic/remquo.hpp +) + +set(ccmath_math_compare_headers + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/compare/fpclassify.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/compare/isfinite.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/compare/isgreater.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/compare/isgreaterequal.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/compare/isinf.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/compare/isless.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/compare/islessequal.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/compare/islessgreater.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/compare/isnan.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/compare/isnormal.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/compare/isunordered.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/compare/signbit.hpp +) + +set(ccmath_math_exponential_impl_headers + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/exponential/impl/exp_float_impl.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/exponential/impl/exp_double_impl.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/exponential/impl/exp_data.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/exponential/impl/exp2_data.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/exponential/impl/log_float_impl.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/exponential/impl/log_double_impl.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/exponential/impl/log_data.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/exponential/impl/log2_float_impl.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/exponential/impl/log2_double_impl.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/exponential/impl/log2_data.hpp +) + +set(ccmath_math_exponential_headers + ${ccmath_math_exponential_impl_headers} + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/exponential/exp.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/exponential/exp2.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/exponential/expm1.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/exponential/log.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/exponential/log1p.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/exponential/log2.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/exponential/log10.hpp +) + +set(ccmath_math_fmanip_impl_headers + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/fmanip/impl/scalbn_float_impl.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/fmanip/impl/scalbn_double_impl.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/fmanip/impl/scalbn_ldouble_impl.hpp +) + +set(ccmath_math_fmanip_headers + ${ccmath_math_fmanip_impl_headers} + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/fmanip/copysign.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/fmanip/frexp.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/fmanip/ilogb.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/fmanip/ldexp.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/fmanip/logb.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/fmanip/modf.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/fmanip/nextafter.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/fmanip/scalbn.hpp +) + +set(ccmath_math_hyperbolic_headers + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/hyperbolic/acosh.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/hyperbolic/asinh.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/hyperbolic/atanh.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/hyperbolic/cosh.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/hyperbolic/sinh.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/hyperbolic/tanh.hpp ) set(ccmath_detail_nearest_headers - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/nearest/ceil.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/nearest/floor.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/nearest/nearbyint.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/nearest/rint.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/nearest/round.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/nearest/trunc.hpp -) - -set(ccmath_detail_power_headers - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/power/cbrt.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/power/hypot.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/power/pow.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/power/sqrt.hpp -) - -set(ccmath_detail_special_headers - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/special/assoc_laguerre.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/special/assoc_legendre.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/special/beta.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/special/comp_ellint_1.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/special/comp_ellint_2.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/special/comp_ellint_3.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/special/cyl_bessel_i.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/special/cyl_bessel_j.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/special/cyl_bessel_k.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/special/cyl_neumann.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/special/ellint_1.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/special/ellint_2.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/special/ellint_3.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/special/expint.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/special/hermite.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/special/laguerre.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/special/legendre.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/special/riemann_zeta.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/special/sph_bessel.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/special/sph_legendre.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/special/sph_neumann.hpp -) - -set(ccmath_detail_trig_headers - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/trig/acos.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/trig/asin.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/trig/atan.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/trig/atan2.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/trig/cos.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/trig/sin.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/trig/tan.hpp -) - - -set(ccmath_detail_root_headers + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/nearest/ceil.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/nearest/floor.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/nearest/nearbyint.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/nearest/rint.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/nearest/round.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/nearest/trunc.hpp +) + +set(ccmath_math_power_headers + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/power/cbrt.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/power/hypot.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/power/pow.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/power/sqrt.hpp +) + +set(ccmath_math_special_headers + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/special/assoc_laguerre.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/special/assoc_legendre.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/special/beta.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/special/comp_ellint_1.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/special/comp_ellint_2.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/special/comp_ellint_3.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/special/cyl_bessel_i.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/special/cyl_bessel_j.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/special/cyl_bessel_k.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/special/cyl_neumann.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/special/ellint_1.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/special/ellint_2.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/special/ellint_3.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/special/expint.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/special/hermite.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/special/laguerre.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/special/legendre.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/special/riemann_zeta.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/special/sph_bessel.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/special/sph_legendre.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/special/sph_neumann.hpp +) + +set(ccmath_math_trig_headers + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/trig/acos.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/trig/asin.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/trig/atan.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/trig/atan2.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/trig/cos.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/trig/sin.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/trig/tan.hpp +) + + +set(ccmath_math_root_headers # Functions without a specified category - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/gamma.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/lerp.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/detail/lgamma.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/gamma.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/lerp.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/lgamma.hpp ) -set(ccmath_detail_headers - ${ccmath_detail_basic_headers} - ${ccmath_detail_compare_headers} - ${ccmath_detail_exponential_headers} - ${ccmath_detail_fmanip_headers} - ${ccmath_detail_hyperbolic_headers} - ${ccmath_detail_nearest_headers} - ${ccmath_detail_power_headers} - ${ccmath_detail_trig_headers} - ${ccmath_detail_root_headers} +set(ccmath_math_headers + ${ccmath_math_basic_headers} + ${ccmath_math_compare_headers} + ${ccmath_math_exponential_headers} + ${ccmath_math_fmanip_headers} + ${ccmath_math_hyperbolic_headers} + ${ccmath_math_nearest_headers} + ${ccmath_math_power_headers} + ${ccmath_math_trig_headers} + ${ccmath_math_root_headers} ) @@ -210,7 +236,7 @@ set(ccmath_root_headers ) set(ccmath_headers - ${ccmath_detail_headers} + ${ccmath_math_headers} ${ccmath_internal_headers} ${ccmath_root_headers} ) diff --git a/example/calculate_half_life_of_carbon_14.cpp b/example/calculate_half_life_of_carbon_14.cpp deleted file mode 100644 index 730e47f..0000000 --- a/example/calculate_half_life_of_carbon_14.cpp +++ /dev/null @@ -1,12 +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. - */ - -int main() -{ - -} diff --git a/example/main.cpp b/example/main.cpp index 1342448..134ad59 100644 --- a/example/main.cpp +++ b/example/main.cpp @@ -7,17 +7,27 @@ */ #include "ccmath/ccmath.hpp" +#include "cmath" #include "iostream" int main() { auto test = ccm::log(1.0); - std::cout << test; + //std::cout << test; // TODO: Implement actual examples showcasing the library + unsigned int two = 2; + unsigned int num = 5; + unsigned int exp = 4; + + auto ccm_result = num * ccm::pow(two, exp); + auto std_result = std::ldexp(num, static_cast(exp)); + + std::cout << "ccm::ldexp: " << ccm_result << std::endl; + std::cout << "std::ldexp: " << std_result << std::endl; return 0; } diff --git a/include/ccmath/basic.hpp b/include/ccmath/basic.hpp index 6bd451d..81ae7a4 100644 --- a/include/ccmath/basic.hpp +++ b/include/ccmath/basic.hpp @@ -8,11 +8,12 @@ #pragma once -#include "ccmath/detail/basic/abs.hpp" -#include "ccmath/detail/basic/fdim.hpp" -#include "ccmath/detail/basic/fma.hpp" -#include "ccmath/detail/basic/fmod.hpp" -#include "ccmath/detail/basic/max.hpp" -#include "ccmath/detail/basic/min.hpp" -#include "ccmath/detail/basic/remainder.hpp" -#include "ccmath/detail/basic/remquo.hpp" +#include "ccmath/math/basic/abs.hpp" +#include "ccmath/math/basic/fdim.hpp" +#include "ccmath/math/basic/fma.hpp" +#include "ccmath/math/basic/fmod.hpp" +#include "ccmath/math/basic/max.hpp" +#include "ccmath/math/basic/min.hpp" +#include "ccmath/math/basic/nan.hpp" +#include "ccmath/math/basic/remainder.hpp" +#include "ccmath/math/basic/remquo.hpp" diff --git a/include/ccmath/ccmath.hpp b/include/ccmath/ccmath.hpp index 32a7d28..23b9d00 100644 --- a/include/ccmath/ccmath.hpp +++ b/include/ccmath/ccmath.hpp @@ -20,7 +20,7 @@ CCMATH REQUIREMENTS: */ // Includes ccm::float_t and ccm::double_t -#include "ccmath/internal/helpers/floating_point_type.hpp" +#include "ccmath/internal/types/fp_types.hpp" /// Basic math functions #include "ccmath/basic.hpp" @@ -50,6 +50,6 @@ CCMATH REQUIREMENTS: #include "ccmath/trig.hpp" /// Uncategorized functions -#include "ccmath/detail/gamma.hpp" -#include "ccmath/detail/lerp.hpp" -#include "ccmath/detail/lgamma.hpp" +#include "ccmath/math/gamma.hpp" +#include "ccmath/math/lerp.hpp" +#include "ccmath/math/lgamma.hpp" diff --git a/include/ccmath/compare.hpp b/include/ccmath/compare.hpp index 881dbea..48c915e 100644 --- a/include/ccmath/compare.hpp +++ b/include/ccmath/compare.hpp @@ -8,15 +8,15 @@ #pragma once -#include "ccmath/detail/compare/fpclassify.hpp" -#include "ccmath/detail/compare/isfinite.hpp" -#include "ccmath/detail/compare/isgreater.hpp" -#include "ccmath/detail/compare/isgreaterequal.hpp" -#include "ccmath/detail/compare/isinf.hpp" -#include "ccmath/detail/compare/isless.hpp" -#include "ccmath/detail/compare/islessequal.hpp" -#include "ccmath/detail/compare/islessgreater.hpp" -#include "ccmath/detail/compare/isnan.hpp" -#include "ccmath/detail/compare/isnormal.hpp" -#include "ccmath/detail/compare/isunordered.hpp" -#include "ccmath/detail/compare/signbit.hpp" +#include "ccmath/math/compare/fpclassify.hpp" +#include "ccmath/math/compare/isfinite.hpp" +#include "ccmath/math/compare/isgreater.hpp" +#include "ccmath/math/compare/isgreaterequal.hpp" +#include "ccmath/math/compare/isinf.hpp" +#include "ccmath/math/compare/isless.hpp" +#include "ccmath/math/compare/islessequal.hpp" +#include "ccmath/math/compare/islessgreater.hpp" +#include "ccmath/math/compare/isnan.hpp" +#include "ccmath/math/compare/isnormal.hpp" +#include "ccmath/math/compare/isunordered.hpp" +#include "ccmath/math/compare/signbit.hpp" diff --git a/include/ccmath/detail/basic/abs.hpp b/include/ccmath/detail/basic/abs.hpp deleted file mode 100644 index be56402..0000000 --- a/include/ccmath/detail/basic/abs.hpp +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2024-Present Ian Pike - * Copyright (c) 2024-Present cmath contributors - * - * This library is provided under the MIT License. - * See LICENSE for more information. - */ - -#pragma once - -#include "ccmath/detail/compare/isnan.hpp" -#include - -namespace ccm -{ - /** - * @brief Computes the absolute value of a number. - * @tparam T A numeric type. - * @param x A floating-point or integer value. - * @return If successful, returns the absolute value of arg (|arg|). The value returned is exact and does not depend on any rounding modes. - */ - template - constexpr T abs(T x) noexcept - { - // If we are NOT dealing with an integral type check for NaN. - if constexpr (!std::is_integral_v) - { - // If x is NaN, return a quiet NaN. - if (ccm::isnan(x)) { return std::numeric_limits::quiet_NaN(); } - } - - // If x is equal to ±zero, return +zero. - // Otherwise, if x is less than zero, return -x, otherwise return x. - return x >= T{0} ? x : -x; - } - - /** - * @brief Computes the absolute value of a number. - * @tparam T An unsigned numeric type. - * @param x A floating-point or integer value. - * @return If successful, returns the absolute value of arg (|arg|). The value returned is exact and does not depend on any rounding modes. - */ - template , bool> = true> - constexpr T abs(T x) noexcept - { - // If abs is called with an argument of type X for which is_unsigned_v is true and - // if X cannot be converted to int by integral promotion, the program is ill-formed. - // See: http://eel.is/c++draft/c.math.abs#3 - // See: ISO/IEC 9899:2018, 7.12.7.2, 7.22.6.1 - if constexpr (std::is_same_v) { return abs(static_cast(x)); } - 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."); - } - } - - /** - * @brief Computes the absolute value of a number. - * @tparam T A floating-point type. - * @param x A floating-point value. - * @return If successful, returns the absolute value of arg (|arg|). The value returned is exact and does not depend on any rounding modes. - */ - template , bool> = true> - constexpr T fabs(T x) noexcept - { - return abs(x); - } - - /** - * @brief Computes the absolute value of a number. - * @param x A floating-point value. - * @return If successful, returns the absolute value of arg (|arg|). The value returned is exact and does not depend on any rounding modes. - */ - inline constexpr float fabsf(float x) noexcept - { - return abs(x); - } - - /** - * @brief Computes the absolute value of a number. - * @param x A floating-point value. - * @return If successful, returns the absolute value of arg (|arg|). The value returned is exact and does not depend on any rounding modes. - */ - inline constexpr long double fabsl(long double x) noexcept - { - return abs(x); - } -} // namespace ccm - -/// @ingroup basic diff --git a/include/ccmath/detail/basic/fma.hpp b/include/ccmath/detail/basic/fma.hpp deleted file mode 100644 index 95859e3..0000000 --- a/include/ccmath/detail/basic/fma.hpp +++ /dev/null @@ -1,158 +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/detail/compare/isinf.hpp" -#include "ccmath/detail/compare/isnan.hpp" - -namespace ccm -{ - /// @cond MATH_DETAIL - namespace - { - namespace impl - { - template - inline constexpr T fma_impl_internal(T x, T y, T z) noexcept - { - // If the compiler has a builtin, use it -#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) && !defined(__INTEL_LLVM_COMPILER) - // TODO: Add a wrapper for if constexpr - if constexpr (std::is_same_v) { return __builtin_fmaf(x, y, z); } - else if constexpr (std::is_same_v) { return __builtin_fma(x, y, z); } - else if constexpr (std::is_same_v) { return __builtin_fmal(x, y, z); } -#endif - // If the compiler doesn't have a builtin, use the following and hope that the compiler is smart enough to optimize it - return (x * y) + z; - } - - // Special case for floating point types - template , bool> = true> - inline constexpr Real fma_impl_switch(Real x, Real y, Real z) noexcept - { - // Handle infinity - if ((x == Real{0} && ccm::isinf(y)) || (y == Real{0} && ccm::isinf(x))) { return std::numeric_limits::quiet_NaN(); } - if (x * y == std::numeric_limits::infinity() && z == -std::numeric_limits::infinity()) - { - return std::numeric_limits::infinity(); - } - - // Handle NaN - if (ccm::isnan(x) || ccm::isnan(y)) { return std::numeric_limits::quiet_NaN(); } - if (ccm::isnan(z) && (x * y != 0 * std::numeric_limits::infinity() || x * y != std::numeric_limits::infinity() * 0)) - { - return std::numeric_limits::quiet_NaN(); - } - - return fma_impl_internal(x, y, z); - } - - template , bool> = true> - inline constexpr Integer fma_impl_switch(Integer x, Integer y, Integer z) noexcept - { - return fma_impl_internal(x, y, z); - } - } // namespace impl - } // namespace - /// @endcond - - template - /** - * @brief Fused multiply-add operation. - * @tparam T A numeric type. - * @param x A floating-point or integer value. - * @param y A floating-point or integer value. - * @param z A floating-point or integer value. - * @return If successful, returns the value of x * y + z as if calculated to infinite precision and rounded once to fit the result type (or, alternatively, - * calculated as a single ternary floating-point operation). - */ - inline constexpr T fma(T x, T y, T z) noexcept - { - // the switch decides which implementation to use based on the type - return impl::fma_impl_switch(x, y, z); - } - - /** - * @brief Fused multiply-add operation. - * @tparam T A floating-point or integer type converted to a common type. - * @tparam U A floating-point or integer type converted to a common type. - * @tparam V A floating-point or integer type converted to a common type. - * @param x A floating-point or integer value converted to a common type. - * @param y A floating-point or integer value converted to a common type. - * @param z A floating-point or integer value converted to a common type. - * @return If successful, returns the value of x * y + z as if calculated to infinite precision and rounded once to fit the result type (or, alternatively, - * calculated as a single ternary floating-point operation). - */ - template - inline constexpr auto fma(T x, U y, V z) noexcept - { - // If our type is an integer epsilon will be set to 0 by default. - // Instead, set epsilon to 1 so that our type is always at least the widest floating point type. - constexpr auto TCommon = std::numeric_limits::epsilon() > 0 ? std::numeric_limits::epsilon() : 1; - constexpr auto UCommon = std::numeric_limits::epsilon() > 0 ? std::numeric_limits::epsilon() : 1; - constexpr auto VCommon = std::numeric_limits::epsilon() > 0 ? std::numeric_limits::epsilon() : 1; - - using epsilon_type = std::common_type_t; - - using shared_type = std::conditional_t< - TCommon <= std::numeric_limits::epsilon() && TCommon <= UCommon, T, - std::conditional_t::epsilon() && UCommon <= TCommon, U, - std::conditional_t::epsilon() && VCommon <= UCommon, V, epsilon_type>>>; - - return ccm::fma(static_cast(x), static_cast(y), static_cast(z)); - } - - /** - * @brief Fused multiply-add operation. - * @tparam T A floating-point or integer type converted to a common type. - * @tparam U A floating-point or integer type converted to a common type. - * @tparam V A floating-point or integer type converted to a common type. - * @param x A floating-point or integer value converted to a common type. - * @param y A floating-point or integer value converted to a common type. - * @param z A floating-point or integer value converted to a common type. - * @return If successful, returns the value of x * y + z as if calculated to infinite precision and rounded once to fit the result type (or, alternatively, - * calculated as a single ternary floating-point operation). - */ - template && std::is_integral_v && std::is_integral_v, bool> = true> - inline constexpr auto fma(T x, U y, V z) noexcept // Special case for if all types are integers. - { - using shared_type = std::common_type_t; - return ccm::fma(static_cast(x), static_cast(y), static_cast(z)); - } - - /** - * @brief Fused multiply-add operation. - * @param x A floating-point value. - * @param y A floating-point value. - * @param z A floating-point value. - * @return If successful, returns the value of x * y + z as if calculated to infinite precision and rounded once to fit the result type (or, alternatively, - * calculated as a single ternary floating-point operation). - */ - inline constexpr float fmaf(float x, float y, float z) noexcept - { - return ccm::fma(x, y, z); - } - - /** - * @brief Fused multiply-add operation. - * @param x A floating-point value. - * @param y A floating-point value. - * @param z A floating-point value. - * @return If successful, returns the value of x * y + z as if calculated to infinite precision and rounded once to fit the result type (or, alternatively, - * calculated as a single ternary floating-point operation). - */ - inline constexpr long double fmal(long double x, long double y, long double z) noexcept - { - return ccm::fma(x, y, z); - } -} // namespace ccm - -/// @ingroup basic diff --git a/include/ccmath/detail/basic/fmod.hpp b/include/ccmath/detail/basic/fmod.hpp deleted file mode 100644 index dea2584..0000000 --- a/include/ccmath/detail/basic/fmod.hpp +++ /dev/null @@ -1,140 +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/detail/compare/isfinite.hpp" -#include "ccmath/detail/compare/isinf.hpp" -#include "ccmath/detail/compare/isnan.hpp" -#include "ccmath/detail/compare/signbit.hpp" -#include "ccmath/detail/nearest/trunc.hpp" - -namespace ccm -{ - /// @cond MATH_DETAIL - namespace - { - namespace impl - { - // NOTE: We do not raise FE_INVALID even is situations where the standard demands it. - template - inline constexpr T fmod_impl_check(T x, T y) noexcept - { - // Special edge cases for floating-point types. - if constexpr (std::numeric_limits::is_iec559) - { - // If x is ±0 and y is not zero, ±0 is returned. - if (x == static_cast(0.0) && (y != static_cast(0.0))) - { - // The standard specifies that plus or minus 0 is returned depending on the sign of x. - if (ccm::signbit(x)) { return -static_cast(0.0); } - else { return static_cast(0.0); } - } - - // If x is ±∞ and y is not NaN OR if y is ±0 and x is not NaN, -NaN is returned - if ((ccm::isinf(x) && !ccm::isnan(y)) || (y == static_cast(0.0) && !ccm::isnan(x))) - { - // For some reason all the major compilers return a negative NaN even though I can't find anywhere - // in the standard that specifies this. I'm going to follow suit and return a negative NaN for now. - // Overall, this has little effect on checking for NaN. We only really care for conformance with the standard. - return -std::numeric_limits::quiet_NaN(); - } - - // If y is ±∞ and x is finite, x is returned. - if (ccm::isinf(y) && ccm::isfinite(x)) { return x; } - - // If either argument is NaN, NaN is returned. - if (ccm::isnan(x) || ccm::isnan(y)) - { - // Same problem as before but this time all major compilers return a positive NaN. - return std::numeric_limits::quiet_NaN(); - } - } - - // Calculate the remainder of the division of x by y. - // static_cast is required to prevent the compiler from complaining about narrowing with integer types. - return static_cast(x - (ccm::trunc(x / y) * y)); - } - - template > - inline constexpr TC fmod_impl_type_check(T x, U y) noexcept - { - return fmod_impl_check(static_cast(x), static_cast(y)); - } - - } // namespace impl - } // namespace - /// @endcond - - /** - * @brief Returns the floating-point remainder of the division operation x/y. - * @note Some edge cases where NaN is returned are different from std::fmod due to the standard allowing implementation based returns. - * @tparam T A floating-point type. - * @param x A floating-point value. - * @param y A floating-point value. - * @return The floating-point remainder of the division operation x/y. - */ - template , int> = 0> - inline constexpr Real fmod(Real x, Real y) - { - return impl::fmod_impl_check(x, y); - } - - /** - * @brief Returns the floating-point remainder of the division operation x/y. - * @tparam Integer An integral type. - * @param x An integral value. - * @param y An integral value. - * @return The floating-point remainder of the division operation x/y. - */ - template , int> = 0> - inline constexpr double fmod(Integer x, Integer y) - { - return impl::fmod_impl_type_check(x, y); - } - - /** - * @brief Returns the floating-point remainder of the division operation x/y. - * @tparam T A floating-point or integral type. - * @tparam U A floating-point or integral type. - * @param x A floating-point or integral value. - * @param y A floating-point or integral value. - * @return The floating-point remainder of the division operation x/y. - */ - template - inline constexpr auto fmod(T x, T y) - { - return impl::fmod_impl_type_check(x, y); - } - - /** - * @brief Returns the floating-point remainder of the division operation x/y. - * @param x A floating-point value. - * @param y A floating-point value. - * @return The floating-point remainder of the division operation x/y. - */ - inline constexpr float fmodf(float x, float y) - { - return fmod(x, y); - } - - /** - * @brief Returns the floating-point remainder of the division operation x/y. - * @param x A floating-point value. - * @param y A floating-point value. - * @return The floating-point remainder of the division operation x/y. - */ - inline constexpr long double fmodl(long double x, long double y) - { - return fmod(x, y); - } -} // namespace ccm - -/// @ingroup basic diff --git a/include/ccmath/detail/compare/fpclassify.hpp b/include/ccmath/detail/compare/fpclassify.hpp deleted file mode 100644 index 9eb3d94..0000000 --- a/include/ccmath/detail/compare/fpclassify.hpp +++ /dev/null @@ -1,37 +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 "ccmath/detail/basic/abs.hpp" -#include "ccmath/detail/compare/isinf.hpp" -#include "ccmath/detail/compare/isnan.hpp" -#include "ccmath/internal/helpers/fpclassify_helper.hpp" - -namespace ccm -{ - /** - * @brief Classify the floating point number - * @attention This function is heavily implementation defined and may not work as expected on unknown compilers - * @tparam T The type of the floating point number - * @param num The number to classify - * @return The classification of the number as an integer - */ - template , bool> = true> - inline constexpr int fpclassify(T num) - { - if (ccm::isnan(num)) { return static_cast(ccm::helpers::floating_point_defines::eFP_NAN); } - else if (ccm::isinf(num)) { return static_cast(ccm::helpers::floating_point_defines::eFP_INFINITE); } - else if (num == static_cast(0)) { return static_cast(ccm::helpers::floating_point_defines::eFP_ZERO); } - else if (ccm::abs(num) < std::numeric_limits::min() && ccm::abs(num) > 0) - { - return static_cast(ccm::helpers::floating_point_defines::eFP_SUBNORMAL); - } - else { return static_cast(ccm::helpers::floating_point_defines::eFP_NORMAL); } - } -} // namespace ccm diff --git a/include/ccmath/detail/compare/isgreaterequal.hpp b/include/ccmath/detail/compare/isgreaterequal.hpp deleted file mode 100644 index d04a0be..0000000 --- a/include/ccmath/detail/compare/isgreaterequal.hpp +++ /dev/null @@ -1,47 +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 -{ - /** - * @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 deleted file mode 100644 index efe5291..0000000 --- a/include/ccmath/detail/compare/isless.hpp +++ /dev/null @@ -1,47 +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 -{ - /** - * @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 deleted file mode 100644 index 68c0775..0000000 --- a/include/ccmath/detail/compare/islessequal.hpp +++ /dev/null @@ -1,47 +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 -{ - /** - * @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 deleted file mode 100644 index 0957b13..0000000 --- a/include/ccmath/detail/compare/islessgreater.hpp +++ /dev/null @@ -1,44 +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 -{ - /** - * @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 deleted file mode 100644 index 8352f68..0000000 --- a/include/ccmath/detail/compare/isnormal.hpp +++ /dev/null @@ -1,41 +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 "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 deleted file mode 100644 index ddd7e20..0000000 --- a/include/ccmath/detail/compare/isunordered.hpp +++ /dev/null @@ -1,58 +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/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/exponential/exp.hpp b/include/ccmath/detail/exponential/exp.hpp deleted file mode 100644 index 1f7eda9..0000000 --- a/include/ccmath/detail/exponential/exp.hpp +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2024-Present Ian Pike - * Copyright (c) 2024-Present cmath contributors - * - * This library is provided under the MIT License. - * See LICENSE for more information. - */ - -#pragma once - -#include "ccmath/detail/basic/abs.hpp" - -#include "ccmath/numbers.hpp" - -#include - -namespace ccm -{ - namespace - { - namespace impl - { - - } // namespace impl - } // namespace - - template - inline constexpr T exp(T x) - { - return 0; - } - -} // namespace ccm diff --git a/include/ccmath/detail/exponential/impl/log2_data.hpp b/include/ccmath/detail/exponential/impl/log2_data.hpp deleted file mode 100644 index fd24cb1..0000000 --- a/include/ccmath/detail/exponential/impl/log2_data.hpp +++ /dev/null @@ -1,139 +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::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/impl/log2_double_impl.hpp b/include/ccmath/detail/exponential/impl/log2_double_impl.hpp deleted file mode 100644 index e75267e..0000000 --- a/include/ccmath/detail/exponential/impl/log2_double_impl.hpp +++ /dev/null @@ -1,158 +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 "ccmath/detail/compare/isnan.hpp" - -#include -#include -#include -#include - -#include "ccmath/detail/exponential/impl/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 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) - { - // 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 intNorm{}; - std::uint64_t tmp{}; - - std::int64_t expo{}; - std::int64_t i{}; - - 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); - - // 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::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 * (log2_poly1_values_dbl[0] + rem * log2_poly1_values_dbl[1]); - result = highPart + polynomialTerm; - lowPart += highPart - result + polynomialTerm; - lowPart += 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; - } - - 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_log2TableOff_dbl; - // NOLINTBEGIN - i = (tmp >> (52 - ccm::internal::k_log2TableBitsDbl)) % k_log2TableN_dbl; - expo = static_cast(tmp) >> 52; // Arithmetic shift. - // NOLINTEND - intNorm = intX - (tmp & 0xfffULL << 52); - 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); - - ccm::double_t remHi{}; - ccm::double_t remLo{}; - - // rounding error: 0x1p-55/N + 0x1p-65. - 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 * 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; - 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 = 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; - } - } // 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/impl/log2_float_impl.hpp b/include/ccmath/detail/exponential/impl/log2_float_impl.hpp deleted file mode 100644 index f5fd2fa..0000000 --- a/include/ccmath/detail/exponential/impl/log2_float_impl.hpp +++ /dev/null @@ -1,110 +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 "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 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) - { - 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_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 = 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 - rem = normVal * inverseCoeff - 1.0; - result0 = logarithmCoeff + static_cast(expo); - - // Pipelined polynomial evaluation to approximate log1p(r)/ln2. - remSqr = rem * rem; - 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; - } - } // namespace impl - } // namespace - - template - [[nodiscard]] inline constexpr T log2_float(T num) noexcept - { - return static_cast(impl::log2_float_impl(static_cast(num))); - } -} // namespace ccm::internal diff --git a/include/ccmath/detail/exponential/impl/log_data.hpp b/include/ccmath/detail/exponential/impl/log_data.hpp deleted file mode 100644 index 7ada8fc..0000000 --- a/include/ccmath/detail/exponential/impl/log_data.hpp +++ /dev/null @@ -1,192 +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::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/impl/log_double_impl.hpp b/include/ccmath/detail/exponential/impl/log_double_impl.hpp deleted file mode 100644 index bafefe9..0000000 --- a/include/ccmath/detail/exponential/impl/log_double_impl.hpp +++ /dev/null @@ -1,155 +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 "ccmath/detail/compare/isnan.hpp" - -#include -#include -#include -#include - -#include "ccmath/detail/exponential/impl/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 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; - - 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 intNorm{}; - std::uint64_t tmp{}; - - // Declare variables for exponent and loop iteration - std::int64_t expo{}; - std::int64_t i{}; - - // Convert input double to uint64_t and extract top 16 bits - 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); - 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 * (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 * log_poly1_values_dbl[0]; // poly1_values[0] == -0.5. - highPart = rem + workspace; - lowPart = rem - highPart + workspace; - lowPart += log_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 = 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 - 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 * log_ln2hi_value_dbl + logarithmCoeff; - highPart = workspace + rem; - 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 * 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 - } // 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/impl/log_float_impl.hpp b/include/ccmath/detail/exponential/impl/log_float_impl.hpp deleted file mode 100644 index 514553a..0000000 --- a/include/ccmath/detail/exponential/impl/log_float_impl.hpp +++ /dev/null @@ -1,97 +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 "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 log_tab_values_flt = internalLogDataFlt.tab; - constexpr auto log_poly_values_flt = internalLogDataFlt.poly; - constexpr auto log_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 = log_tab_values_flt[i].invc; - logarithmCoeff = log_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) * log_ln2_value_flt; - - // Polynomial approximation for log1p(rem) - remSqr = rem * rem; - result = log_poly_values_flt[1] * rem + log_poly_values_flt[2]; - result = log_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/lerp.hpp b/include/ccmath/detail/lerp.hpp deleted file mode 100644 index 6458563..0000000 --- a/include/ccmath/detail/lerp.hpp +++ /dev/null @@ -1,49 +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 - -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 deleted file mode 100644 index ae423dd..0000000 --- a/include/ccmath/detail/nearest/floor.hpp +++ /dev/null @@ -1,79 +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 "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 deleted file mode 100644 index 469c11a..0000000 --- a/include/ccmath/detail/power/pow.hpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2024-Present Ian Pike - * Copyright (c) 2024-Present cmath contributors - * - * This library is provided under the MIT License. - * See LICENSE for more information. - */ - -#pragma once - -#include -#include - - -#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 - [[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 - -} // namespace ccm diff --git a/include/ccmath/exponential.hpp b/include/ccmath/exponential.hpp index 82e9dad..42e798f 100644 --- a/include/ccmath/exponential.hpp +++ b/include/ccmath/exponential.hpp @@ -8,10 +8,10 @@ #pragma once -#include "ccmath/detail/exponential/exp.hpp" -#include "ccmath/detail/exponential/exp2.hpp" -#include "ccmath/detail/exponential/expm1.hpp" -#include "ccmath/detail/exponential/log.hpp" -#include "ccmath/detail/exponential/log10.hpp" -#include "ccmath/detail/exponential/log1p.hpp" -#include "ccmath/detail/exponential/log2.hpp" +#include "ccmath/math/exponential/exp.hpp" +#include "ccmath/math/exponential/exp2.hpp" +#include "ccmath/math/exponential/expm1.hpp" +#include "ccmath/math/exponential/log.hpp" +#include "ccmath/math/exponential/log10.hpp" +#include "ccmath/math/exponential/log1p.hpp" +#include "ccmath/math/exponential/log2.hpp" diff --git a/include/ccmath/extensions/clamp.hpp b/include/ccmath/extensions/clamp.hpp new file mode 100644 index 0000000..8c95fbb --- /dev/null +++ b/include/ccmath/extensions/clamp.hpp @@ -0,0 +1,46 @@ +/* + * 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/math/basic/min.hpp" +#include "ccmath/math/basic/max.hpp" +#include + +namespace ccm::ext +{ + /** + * @brief Clamps a value between a minimum and maximum value. + * @tparam T Type of the input and output. + * @param x Value to clamp. + * @param lo Minimum value. + * @param hi Maximum value. + * @return The clamped value. + */ + template + inline constexpr T clamp(T v, T lo = T{0}, T hi = T{1}) + { + return ccm::min(ccm::max(v, lo), hi); + } + + /** + * @brief Clamps a value between a minimum and maximum value. + * @tparam TVal Type of the input value. + * @tparam TLow Type of the lower bound. + * @tparam THigh Type of the upper bound. + * @param v Value to clamp. + * @param lo Minimum value. + * @param hi Maximum value. + * @return The clamped value. + */ + template + inline constexpr std::common_type_t clamp(TVal v, TLow lo = TLow{0}, THigh hi = THigh{1}) + { + return ccm::min(ccm::max(v, lo), hi); + } +} diff --git a/include/ccmath/extensions/degrees.hpp b/include/ccmath/extensions/degrees.hpp new file mode 100644 index 0000000..dce4c62 --- /dev/null +++ b/include/ccmath/extensions/degrees.hpp @@ -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. + */ + +#pragma once + +#include "ccmath/numbers.hpp" +#include + +namespace ccm::ext +{ + template , bool> = true> + constexpr T degrees(T radians) noexcept + { + return (static_cast(180) * radians) / ccm::numbers::pi_v; + } +} diff --git a/include/ccmath/extensions/fract.hpp b/include/ccmath/extensions/fract.hpp new file mode 100644 index 0000000..b4a1629 --- /dev/null +++ b/include/ccmath/extensions/fract.hpp @@ -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. + */ + +#pragma once + +#include "ccmath/math/nearest/floor.hpp" +#include + +namespace ccm::ext +{ + template , bool> = true> + constexpr T fract(T x) noexcept + { + return x - ccm::floor(x); + } +} // namespace ccm diff --git a/include/ccmath/extensions/lerp_smooth.hpp b/include/ccmath/extensions/lerp_smooth.hpp new file mode 100644 index 0000000..f710bc2 --- /dev/null +++ b/include/ccmath/extensions/lerp_smooth.hpp @@ -0,0 +1,31 @@ +/* + * 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/math/exponential/exp2.hpp" + +namespace ccm::ext +{ + /** + * @brief Frame rate independent linear interpolation smoothing. + * @tparam T Type of the input and output. + * @param a Current value. + * @param b Target value. + * @param t Delta time, in seconds. + * @param h Half-life, time until halfway, in seconds. + * @return The smoothed value. + * + * @warning Currently waiting on ccm::exp2 to be implemented. Till then this will NOT work. + */ + template + constexpr T lerp_smooth(T a, T b, T t, T h) + { + return b + (a - b) * ccm::exp2(-t / h); + } +} // namespace ccm::ext diff --git a/include/ccmath/extensions/mix.hpp b/include/ccmath/extensions/mix.hpp new file mode 100644 index 0000000..6e29003 --- /dev/null +++ b/include/ccmath/extensions/mix.hpp @@ -0,0 +1,26 @@ +/* + * 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 +{ + template + constexpr T mix(T x, T y, T a) noexcept + { + return x * (1 - a) + y * a; + } + + template + constexpr std::common_type_t mix(TStart x, TEnd y, TAplha a) noexcept + { + return x * (1 - a) + y * a; + } +} // namespace ccm diff --git a/include/ccmath/extensions/radians.hpp b/include/ccmath/extensions/radians.hpp new file mode 100644 index 0000000..7ab3c3c --- /dev/null +++ b/include/ccmath/extensions/radians.hpp @@ -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. + */ + +#pragma once + +#include "ccmath/numbers.hpp" +#include + +namespace ccm::ext +{ + template , bool> = true> + constexpr T radians(T degrees) noexcept + { + return (ccm::numbers::pi_v * degrees) / static_cast(180); + } +} diff --git a/include/ccmath/extensions/smoothstep.hpp b/include/ccmath/extensions/smoothstep.hpp new file mode 100644 index 0000000..0fba503 --- /dev/null +++ b/include/ccmath/extensions/smoothstep.hpp @@ -0,0 +1,32 @@ +/* + * 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/extensions/clamp.hpp" +#include + +namespace ccm::ext +{ + /** + * @brief Smooth hermite interpolation. + * @tparam T Type of the input and output. + * @param edge0 Lower edge. + * @param edge1 Upper edge. + * @param x Value to interpolate. + * @return The interpolated value. + */ + template, bool> = true> + inline constexpr T smoothstep(T edge0, T edge1, T x) + { + // Scale, bias and saturate x to 0..1 range + x = ccm::ext::clamp((x - edge0) / (edge1 - edge0)); + // Evaluate polynomial + return x * x * (static_cast(3) - static_cast(2) * x); + } +} diff --git a/include/ccmath/fmanip.hpp b/include/ccmath/fmanip.hpp index a6912e4..d690cd3 100644 --- a/include/ccmath/fmanip.hpp +++ b/include/ccmath/fmanip.hpp @@ -8,11 +8,11 @@ #pragma once -#include "ccmath/detail/fmanip/copysign.hpp" -#include "ccmath/detail/fmanip/frexp.hpp" -#include "ccmath/detail/fmanip/ilogb.hpp" -#include "ccmath/detail/fmanip/ldexp.hpp" -#include "ccmath/detail/fmanip/logb.hpp" -#include "ccmath/detail/fmanip/modf.hpp" -#include "ccmath/detail/fmanip/nextafter.hpp" -#include "ccmath/detail/fmanip/scalbn.hpp" +#include "ccmath/math/fmanip/copysign.hpp" +#include "ccmath/math/fmanip/frexp.hpp" +#include "ccmath/math/fmanip/ilogb.hpp" +#include "ccmath/math/fmanip/ldexp.hpp" +#include "ccmath/math/fmanip/logb.hpp" +#include "ccmath/math/fmanip/modf.hpp" +#include "ccmath/math/fmanip/nextafter.hpp" +#include "ccmath/math/fmanip/scalbn.hpp" diff --git a/include/ccmath/hyperbolic.hpp b/include/ccmath/hyperbolic.hpp index f623ecc..e5d1bde 100644 --- a/include/ccmath/hyperbolic.hpp +++ b/include/ccmath/hyperbolic.hpp @@ -8,9 +8,9 @@ #pragma once -#include "ccmath/detail/hyperbolic/acosh.hpp" -#include "ccmath/detail/hyperbolic/asinh.hpp" -#include "ccmath/detail/hyperbolic/atanh.hpp" -#include "ccmath/detail/hyperbolic/cosh.hpp" -#include "ccmath/detail/hyperbolic/sinh.hpp" -#include "ccmath/detail/hyperbolic/tanh.hpp" +#include "ccmath/math/hyperbolic/acosh.hpp" +#include "ccmath/math/hyperbolic/asinh.hpp" +#include "ccmath/math/hyperbolic/atanh.hpp" +#include "ccmath/math/hyperbolic/cosh.hpp" +#include "ccmath/math/hyperbolic/sinh.hpp" +#include "ccmath/math/hyperbolic/tanh.hpp" diff --git a/include/ccmath/internal/README.md b/include/ccmath/internal/README.md new file mode 100644 index 0000000..3e3ba44 --- /dev/null +++ b/include/ccmath/internal/README.md @@ -0,0 +1,10 @@ +# CCMath Internal Headers Info +> [!CAUTION] +> This directory contains internal headers for the CCMath library. These headers are not intended for public use and are subject to change at any time. They are used for internal implementation details and are not guaranteed to be stable or to have a stable API. Assume anything included here directly could change its signature at any moment. + +## Folders +- `config`: Predefined macros and configuration settings for the library. +- `helpers`: Helper functions that are generally specific to a particular function. +- `predef`: Predefined macro wrappers for compiler and platform-specific features. +- `support`: Support classes and functions for internal implementation details. +- `types`: Custom type definitions and wrappers for ccmath types. diff --git a/include/ccmath/internal/setup/builtin_support_def.hpp b/include/ccmath/internal/config/builtin_support.hpp similarity index 95% rename from include/ccmath/internal/setup/builtin_support_def.hpp rename to include/ccmath/internal/config/builtin_support.hpp index 598dc89..213268f 100644 --- a/include/ccmath/internal/setup/builtin_support_def.hpp +++ b/include/ccmath/internal/config/builtin_support.hpp @@ -8,13 +8,7 @@ #pragma once -#include "ccmath/internal/setup/compiler_def.hpp" - -/// !!! ATTENTION !!! -/// If you add a new builtin here, remember you HAVE to also undefine -/// any new macro created inside of builtin_support_undef.hpp -/// !!! ATTENTION !!! - +#include "ccmath/internal/config/compiler.hpp" // This file is used to detect if the compiler has support for certain builtins that are static_assert-able constexpr @@ -161,4 +155,3 @@ // TODO: Add more builtins as more are required for the library -#include "ccmath/internal/setup/compiler_undef.hpp" diff --git a/include/ccmath/internal/config/compiler.hpp b/include/ccmath/internal/config/compiler.hpp new file mode 100644 index 0000000..16de93b --- /dev/null +++ b/include/ccmath/internal/config/compiler.hpp @@ -0,0 +1,123 @@ +/* + * 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. + */ + +/** + * Currently supported compilers: + * - MSVC + * - Clang-CL + * - Intel DPC++ + * - Nvidia HPC SDK + * - Nvidia CUDA + * - AMD HIP + * - Apple Clang + * - Clang + * - GCC + */ + + +/// MSVC +#if defined(_MSC_VER) && !defined(__clang__) && !defined(CCMATH_COMPILER_MSVC) + #define CCMATH_COMPILER_MSVC + #define CCMATH_COMPILER_MSVC_VER _MSC_VER + + +#elif defined(_MSC_VER) && defined(__clang__) && !defined(CCMATH_COMPILER_CLANG_CL) + #define CCMATH_COMPILER_CLANG_CL + #define CCMATH_COMPILER_CLANG_CL_VER ((__clang_major__ * 10000) + (__clang_minor__ * 100) + __clang_patchlevel__) + #define CCMATH_COMPILER_CLANG_CL_VER_MAJOR __clang_major__ + #define CCMATH_COMPILER_CLANG_CL_VER_MINOR __clang_minor__ + #define CCMATH_COMPILER_CLANG_CL_VER_PATCH __clang_patchlevel__ + + #ifndef CCMATH_COMPILER_CLANG_BASED + #define CCMATH_COMPILER_CLANG_BASED + #endif + +// TODO: Add precise detection for specific compiler versions along with a warning if using unsupported compiler + +/// Intel DPC++ Compiler +#elif defined(SYCL_LANGUAGE_VERSION) || defined(__INTEL_LLVM_COMPILER) && !defined(CCMATH_COMPILER_INTEL) + #define CCMATH_COMPILER_INTEL + #define CCMATH_COMPILER_INTEL_VER __INTEL_LLVM_COMPILER + + #ifndef CCMATH_COMPILER_CLANG_BASED + #define CCMATH_COMPILER_CLANG_BASED + #endif + +// TODO: Add precise detection for specific compiler versions along with a warning if using unsupported compiler + + +/// Nvidia HPC SDK +#elif defined(__NVCOMPILER) || defined(__NVCOMPILER_LLVM__) && !defined(CCMATH_COMPILER_NVIDIA_HPC) + #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__ + +// TODO: Add precise detection for specific compiler versions along with a warning if using unsupported compiler + + +/// Nvidia CUDA +#elif defined(__CUDACC__) && !defined(CCMATH_COMPILER_NVIDIA_CUDA) + #if !defined(CUDA_VERSION) + #include // We need to make sure the version is defined since nvcc doesn't define it + #endif + + #define CCMATH_COMPILER_NVIDIA_CUDA + #define CCMATH_COMPILER_NVIDIA_CUDA_VER (CUDA_VERSION / 1000) + +// TODO: Add precise detection for specific compiler versions along with a warning if using unsupported compiler + + +/// AMD HIP +#elif defined(__HIP__) && !defined(CCMATH_COMPILER_AMD_HIP) + #define CCMATH_COMPILER_AMD_HIP + +// TODO: Add precise detection for specific compiler versions along with a warning if using unsupported compiler + + +/// Apple Clang +#elif defined(__apple_build_version__) && defined(__clang__) && !defined(CCMATH_COMPILER_APPLE_CLANG) + #define CCMATH_COMPILER_APPLE_CLANG + #define CCMATH_COMPILER_APPLE_CLANG_VER ((__clang_major__ * 10000) + (__clang_minor__ * 100) + __clang_patchlevel__) + #define CCMATH_COMPILER_APPLE_CLANG_VER_MAJOR __clang_major__ + #define CCMATH_COMPILER_APPLE_CLANG_VER_MINOR __clang_minor__ + #define CCMATH_COMPILER_APPLE_CLANG_VER_PATCH __clang_patchlevel__ + + #ifndef CCMATH_COMPILER_CLANG_BASED + #define CCMATH_COMPILER_CLANG_BASED + #endif + +// TODO: Add precise detection for specific compiler versions along with a warning if using unsupported compiler + +/// Clang +#elif defined(__clang__) && !defined(CCMATH_COMPILER_CLANG) + #define CCMATH_COMPILER_CLANG + #define CCMATH_COMPILER_CLANG_VER ((__clang_major__ * 100) + (__clang_minor__ * 10) + __clang_patchlevel__) + #define CCMATH_COMPILER_CLANG_VER_MAJOR __clang_major__ + #define CCMATH_COMPILER_CLANG_VER_MINOR __clang_minor__ + #define CCMATH_COMPILER_CLANG_VER_PATCH __clang_patchlevel__ + + #ifndef CCMATH_COMPILER_CLANG_BASED + #define CCMATH_COMPILER_CLANG_BASED + #endif + +// TODO: Add precise detection for specific compiler versions along with a warning if using unsupported compiler + +/// GCC +#elif defined(__GNUC__) && !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__ + #define CCMATH_COMPILER_GCC_VER_MINOR __GNUC_MINOR__ + #define CCMATH_COMPILER_GCC_VER_PATCH __GNUC_PATCHLEVEL__ + + +#else + #define CCMATH_COMPILER_UNKNOWN +#endif diff --git a/include/ccmath/internal/helpers/bits.hpp b/include/ccmath/internal/helpers/bits.hpp deleted file mode 100644 index 3faeff6..0000000 --- a/include/ccmath/internal/helpers/bits.hpp +++ /dev/null @@ -1,126 +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::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 -{ - - /** - * @brief - * @tparam To - * @tparam From - * @param src - * @return - */ - 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 - { - // 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 static_cast(bit_cast(x) >> 48); - } - - inline constexpr std::uint32_t top12_bits_of_double(double x) noexcept - { - return static_cast(bit_cast(x) >> 52); - } - - inline constexpr std::uint64_t double_to_uint64(double x) noexcept - { - return bit_cast(x); - } - - inline constexpr std::int64_t double_to_int64(double x) noexcept - { - return bit_cast(x); - } - - inline constexpr double uint64_to_double(std::uint64_t x) noexcept - { - return bit_cast(x); - } - - inline constexpr double int64_to_double(std::int64_t x) noexcept - { - return bit_cast(x); - } - - inline constexpr std::uint32_t float_to_uint32(float x) noexcept - { - return bit_cast(x); - } - - inline constexpr std::int32_t float_to_int32(float x) noexcept - { - return bit_cast(x); - } - - inline constexpr float uint32_to_float(std::uint32_t x) noexcept - { - return bit_cast(x); - } - - inline constexpr float int32_to_float(std::int32_t x) noexcept - { - return bit_cast(x); - } - - -} // namespace ccm::helpers diff --git a/include/ccmath/internal/helpers/digit_to_int.hpp b/include/ccmath/internal/helpers/digit_to_int.hpp new file mode 100644 index 0000000..88cb8c6 --- /dev/null +++ b/include/ccmath/internal/helpers/digit_to_int.hpp @@ -0,0 +1,29 @@ +/* + * 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 +{ + constexpr int digit_to_int(int c) + { + if ('0' <= c && c <= '9') + { + return (c - '0'); + } + if ('A' <= c && c <= 'F') + { + return (c - 'A' + 10); + } + if ('a' <= c && c <= 'f') + { + return (c - 'a' + 10); + } + return 0; + } +} diff --git a/include/ccmath/internal/helpers/exp_helpers.hpp b/include/ccmath/internal/helpers/exp_helpers.hpp new file mode 100644 index 0000000..370e9c7 --- /dev/null +++ b/include/ccmath/internal/helpers/exp_helpers.hpp @@ -0,0 +1,43 @@ +/* + * 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/internal/support/bits.hpp" +#include "ccmath/internal/support/meta_compare.hpp" +#include +#include + +namespace ccm::helpers +{ +#if FLT_EVAL_METHOD == 1 + // check if the type is the same as float + template + inline constexpr bool check_excess_precision = std::is_same_v; +#else + // check if the type is the same as float or double + template + inline constexpr bool check_excess_precision = ccm::support::or_v, std::is_same>; +#endif + + template + constexpr T narrow_eval(T x) + { +#if FLT_EVAL_METHOD == 0 + return x; +#else + T math_narrow_eval_tmp = x; + if constexpr (check_excess_precision) + { + volatile T volatile_tmp = ccm::support::bit_cast(math_narrow_eval_tmp); + math_narrow_eval_tmp = volatile_tmp; + } + return math_narrow_eval_tmp; +#endif + } +} // namespace ccm::helpers diff --git a/include/ccmath/internal/helpers/find_number.hpp b/include/ccmath/internal/helpers/find_number.hpp deleted file mode 100644 index 8d23d64..0000000 --- a/include/ccmath/internal/helpers/find_number.hpp +++ /dev/null @@ -1,36 +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 "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/fpclassify_helper.hpp b/include/ccmath/internal/helpers/fpclassify_helper.hpp index a71d173..e2d5fb2 100644 --- a/include/ccmath/internal/helpers/fpclassify_helper.hpp +++ b/include/ccmath/internal/helpers/fpclassify_helper.hpp @@ -10,8 +10,6 @@ // Implementation of cmath floating point macros based on supported compilers -// TODO: Add support for other compilers - // Currently supported compilers: // - GCC // - Clang @@ -20,43 +18,38 @@ // - Intel DPC++ // - Nvidia HPC SDK -// Identify the compiler -#include "ccmath/internal/setup/compiler_def.hpp" +#include "ccmath/internal/config/compiler.hpp" // Identify the compiler namespace ccm::helpers { struct floating_point_defines { #if defined(CCMATH_COMPILER_MSVC) // Mirrors the corecrt definitions - 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; + static constexpr int eFP_NAN = 2; + static constexpr int eFP_INFINITE = 1; + static constexpr int eFP_ZERO = 0; + static constexpr int eFP_SUBNORMAL = -2; + static constexpr int eFP_NORMAL = -1; #elif defined(CCMATH_COMPILER_APPLE_CLANG) // Apple Clang has a different set of defines than Clang - 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; + static constexpr int eFP_NAN = 1; + static constexpr int eFP_INFINITE = 2; + static constexpr int eFP_ZERO = 3; + static constexpr int eFP_SUBNORMAL = 5; + static constexpr int eFP_NORMAL = 4; +#elif defined(CCMATH_COMPILER_NVIDIA_HPC) // Nvidia HPC SDK has a different set of defines than GCC + static constexpr int eFP_NAN = 0; + static constexpr int eFP_INFINITE = 1; + static constexpr int eFP_ZERO = 2; + static constexpr int eFP_SUBNORMAL = 2; + static constexpr 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 - 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; + static constexpr int eFP_NAN = 0; + static constexpr int eFP_INFINITE = 1; + static constexpr int eFP_ZERO = 2; + static constexpr int eFP_SUBNORMAL = 3; + static constexpr 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" - +} // namespace ccm::helpers diff --git a/include/ccmath/internal/predef/compiler_warnings_and_errors.hpp b/include/ccmath/internal/predef/compiler_warnings_and_errors.hpp new file mode 100644 index 0000000..644c969 --- /dev/null +++ b/include/ccmath/internal/predef/compiler_warnings_and_errors.hpp @@ -0,0 +1,433 @@ +/* + * 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 + +// clang-format off + + +/// +/// \def CCM_DISABLE_GCC_WARNING(w) +/// \brief Disables a warning for GCC. +/// \param w The warning to disable as an error. This should be written as -Wwarning-name and not as a string. +/// \note Only one warning can be disabled per statement, due to how gcc works. +/// \note This macro must be called before CCM_RESTORE_GCC_WARNING(). +/// +/// Example usage: +/// CCM_DISABLE_GCC_WARNING(-Wuninitialized) +/// CCM_DISABLE_GCC_WARNING(-Wunused) +/// +/// CCM_RESTORE_GCC_WARNING() +/// CCM_RESTORE_GCC_WARNING() +/// +#ifndef CCM_DISABLE_GCC_WARNING + #if defined(__GNUC__) && !defined(__clang__) + // Helper macros - do not use directly + #define CCM_GCC_WHELP0(x) #x + #define CCM_GCC_WHELP1(x) CCM_GCC_WHELP0(GCC diagnostic ignored x) + #define CCM_GCC_WHELP2(x) CCM_GCC_WHELP1(#x) + + // Disables a warning for GCC. This should be written as -Wwarning-name and not as a string. + // Must be called before CCM_RESTORE_GCC_WARNING(). + #define CCM_DISABLE_GCC_WARNING(w) _Pragma("GCC diagnostic push") _Pragma(CCM_GCC_WHELP2(w)) + #else + // If not on GCC this macro does nothing. + // Must be called before CCM_RESTORE_GCC_WARNING(). + #define CCM_DISABLE_GCC_WARNING(w) + #endif +#endif + +/// +/// \def CCM_RESTORE_GCC_WARNING() +/// \brief Restore a warning for GCC. Warnings are restored in a stack based manner. +/// \note Only one warning can be disabled per statement, due to how gcc works. +/// \note This macro must be called after CCM_DISABLE_GCC_WARNING(w). +/// +/// Example usage: +/// CCM_DISABLE_GCC_WARNING(-Wuninitialized) +/// CCM_DISABLE_GCC_WARNING(-Wunused) +/// +/// CCM_RESTORE_GCC_WARNING() +/// CCM_RESTORE_GCC_WARNING() +/// +#ifndef CCM_RESTORE_GCC_WARNING + #if defined(__GNUC__) && !defined(__clang__) + // Restore a warning for GCC in a stack based manner. + // Must be called after CCM_DISABLE_GCC_WARNING(w). + #define CCM_RESTORE_GCC_WARNING() _Pragma("GCC diagnostic pop") + #else + // If not on GCC this macro does nothing. + // Must be called after CCM_DISABLE_GCC_WARNING(w). + #define CCM_RESTORE_GCC_WARNING() + #endif +#endif + +/// +/// \def CCM_ENABLE_GCC_WARNING_AS_ERROR(w) +/// \brief This will enable a warning as an error for GCC. +/// \param w The warning to enable as an error. This should be written as -Wwarning-name and not as a string. +/// \note Only one warning can be treated as an error per statement, due to how GCC works +/// \note This macro must be called before CCM_DISABLE_GCC_WARNING_AS_ERROR(). +/// +/// Example usage: +/// CCM_ENABLE_GCC_WARNING_AS_ERROR(-Wuninitialized) +/// CCM_ENABLE_GCC_WARNING_AS_ERROR(-Wunused) +/// +/// CCM_DISABLE_GCC_WARNING_AS_ERROR() +/// CCM_DISABLE_GCC_WARNING_AS_ERROR() +/// +#ifndef CCM_ENABLE_GCC_WARNING_AS_ERROR + #if defined(__GNUC__) && !defined(__clang__) + #define CCM_GCC_WERROR_HELP0(x) #x // Helper macros - do not use directly + #define CCM_GCC_WERROR_HELP1(x) CCM_GCC_WERROR_HELP0(GCC diagnostic error x) // Helper macros - do not use directly + #define CCM_GCC_WERROR_HELP2(x) CCM_GCC_WERROR_HELP1(#x) // Helper macros - do not use directly + + // This will enable a warning as an error for GCC. This should be written as -Wwarning-name and not as a string. + // Must be called before CCM_DISABLE_GCC_WARNING_AS_ERROR(). + #define CCM_ENABLE_GCC_WARNING_AS_ERROR(w) _Pragma("GCC diagnostic push") _Pragma(CCM_GCC_WERROR_HELP2(w)) + #else + // If not on GCC this macro does nothing. + // Must be called before CCM_DISABLE_GCC_WARNING_AS_ERROR(). + #define CCM_ENABLE_GCC_WARNING_AS_ERROR(w) + #endif +#endif + +/// +/// \def CCM_DISABLE_GCC_WARNING_AS_ERROR() +/// \brief This will disable a warning as an error for GCC. +/// \note Only one warning can be treated as an error per statement, due to how GCC works +/// +/// Example usage: +/// CCM_ENABLE_GCC_WARNING_AS_ERROR(-Wuninitialized) +/// CCM_ENABLE_GCC_WARNING_AS_ERROR(-Wunused) +/// +/// CCM_DISABLE_GCC_WARNING_AS_ERROR() +/// CCM_DISABLE_GCC_WARNING_AS_ERROR() +/// +#ifndef CCM_DISABLE_GCC_WARNING_AS_ERROR + #if defined(__GNUC__) && !defined(__clang__) + // This will disable a warning as an error for GCC in a stack based manner. + // Must be called after CCM_ENABLE_GCC_WARNING_AS_ERROR(w). + #define CCM_DISABLE_GCC_WARNING_AS_ERROR() _Pragma("GCC diagnostic pop") + #else + // If not on GCC this macro does nothing. + // Must be called after CCM_ENABLE_GCC_WARNING_AS_ERROR(w). + #define CCM_DISABLE_GCC_WARNING_AS_ERROR() + #endif +#endif + +/// +/// \def CCM_ENABLE_IGNORE_GCC_WARNING(w) +/// \brief This will enable the suppression of a warning on GCC. +/// \param w The warning to enable as an error. This should be written as -Wwarning-name and not as a string. +/// \note Only one warning can be treated as an error per statement, due to how GCC works +/// +/// Example usage: +/// CCM_ENABLE_IGNORE_GCC_WARNING(-Wuninitialized) +/// CCM_ENABLE_IGNORE_GCC_WARNING(-Wunused) +/// +/// CCM_DISABLE_IGNORE_GCC_WARNING(-Wuninitialized) +/// CCM_DISABLE_IGNORE_GCC_WARNING(-Wunused) +/// +#ifndef CCM_ENABLE_IGNORE_GCC_WARNING + #if defined(__GNUC__) && !defined(__clang__) + #define CCM_GCC_IGNORE_HELP0(x) #x // Helper macros - do not use directly + #define CCM_GCC_IGNORE_HELP1(x) CCM_GCC_IGNORE_HELP0(GCC diagnostic ignored x) // Helper macros - do not use directly + #define CCM_GCC_IGNORE_HELP2(x) CCM_GCC_IGNORE_HELP1(#x) // Helper macros - do not use directly + + // This will enable the suppression of a warning on GCC. This should be written as -Wwarning-name and not as a string. + // Must be called before CCM_DISABLE_IGNORE_GCC_WARNING(w). + #define CCM_ENABLE_IGNORE_GCC_WARNING(w) _Pragma("GCC diagnostic push") _Pragma(CCM_GCC_IGNORE_HELP2(w)) + #else + // If not on GCC this macro does nothing. + // Must be called before CCM_DISABLE_IGNORE_GCC_WARNING(w). + #define CCM_ENABLE_IGNORE_GCC_WARNING(w) + #endif +#endif + +/// +/// \def CCM_DISABLE_IGNORE_GCC_WARNING(w) +/// \brief This will re-enable a ignored warning for GCC. +/// \param w The warning to re-enable as an warning. This should be written as -Wwarning-name and not as a string. +/// \note Only one warning can be treated as an error per statement, due to how GCC works +/// +/// Example usage: +/// CCM_ENABLE_IGNORE_GCC_WARNING(-Wuninitialized) +/// CCM_ENABLE_IGNORE_GCC_WARNING(-Wunused) +/// +/// CCM_DISABLE_IGNORE_GCC_WARNING(-Wuninitialized) +/// CCM_DISABLE_IGNORE_GCC_WARNING(-Wunused) +/// +#ifndef CCM_DISABLE_IGNORE_GCC_WARNING + #if defined(__GNUC__) && !defined(__clang__) + #ifndef CCM_GCC_IGNORE_DISABLE_HELP0 + #define CCM_GCC_IGNORE_DISABLE_HELP0(x) #x // Helper macros - do not use directly + #endif + #ifndef CCM_GCC_IGNORE_DISABLE_HELP1 + #define CCM_GCC_IGNORE_DISABLE_HELP1(x) CCM_GCC_IGNORE_DISABLE_HELP0(GCC diagnostic warning x) // Helper macros - do not use directly + #endif + #ifndef CCM_GCC_IGNORE_DISABLE_HELP2 + #define CCM_GCC_IGNORE_DISABLE_HELP2(x) CCM_GCC_IGNORE_DISABLE_HELP1(#x) // Helper macros - do not use directly + #endif + // This will disable suppression of an ignored warning for GCC + // Must be called after CCM_ENABLE_IGNORE_GCC_WARNING(w). + #define CCM_DISABLE_IGNORE_GCC_WARNING(w) _Pragma(CCM_GCC_IGNORE_DISABLE_HELP2(w)) _Pragma("GCC diagnostic pop") + #else + // If not on GCC this macro does nothing. + // Must be called after CCM_ENABLE_IGNORE_GCC_WARNING(w). + #define CCM_DISABLE_IGNORE_GCC_WARNING(w) + #endif +#endif + +/// +/// \def CCM_DISABLE_CLANG_WARNING(w) +/// \brief Disables a warning for Clang. +/// \param w The warning to disable as an error. This should be written as -Wwarning-name and not as a string. +/// \note Only one warning can be disabled per statement, due to how clang works. +/// +/// Example usage: +/// CCM_DISABLE_CLANG_WARNING(-Wuninitialized) +/// CCM_DISABLE_CLANG_WARNING(-Wunused) +/// +/// CCM_RESTORE_CLANG_WARNING() +/// CCM_RESTORE_CLANG_WARNING() +/// +#ifndef CCM_DISABLE_CLANG_WARNING + #if defined(__clang__) + #define CCM_CLANG_WHELP0(x) #x // Helper macros - do not use directly + #define CCM_CLANG_WHELP1(x) CCM_CLANG_WHELP0(clang diagnostic ignored x) // Helper macros - do not use directly + #define CCM_CLANG_WHELP2(x) CCM_CLANG_WHELP1(#x) // Helper macros - do not use directly + + // Disables a warning for Clang. This should be written as -Wwarning-name and not as a string. + // Must be called before CCM_RESTORE_CLANG_WARNING(). + #define CCM_DISABLE_CLANG_WARNING(w) \ + _Pragma("clang diagnostic push") _Pragma(CCM_CLANG_WHELP2(-Wunknown-warning-option)) _Pragma(CCM_CLANG_WHELP2(w)) + #else + // If not on Clang this macro does nothing. + // Must be called before CCM_RESTORE_CLANG_WARNING(). + #define CCM_DISABLE_CLANG_WARNING(w) + #endif +#endif + +/// +/// \def CCM_RESTORE_CLANG_WARNING() +/// \brief Restore a warning for Clang. +/// \param w The warning to disable as an error. This should be written as -Wwarning-name and not as a string. +/// \note Only one warning can be disabled per statement, due to how clang works. +/// +/// Example usage: +/// CCM_DISABLE_CLANG_WARNING(-Wuninitialized) +/// CCM_DISABLE_CLANG_WARNING(-Wunused) +/// +/// CCM_RESTORE_CLANG_WARNING() +/// CCM_RESTORE_CLANG_WARNING() +/// +#ifndef CCM_RESTORE_CLANG_WARNING + #if defined(__clang__) + // Restore a warning for Clang in a stack based manner. + // Must be called after CCM_DISABLE_CLANG_WARNING(w). + #define CCM_RESTORE_CLANG_WARNING() _Pragma("clang diagnostic pop") + #else + // If not on Clang this macro does nothing. + // Must be called after CCM_DISABLE_CLANG_WARNING(w). + #define CCM_RESTORE_CLANG_WARNING() + #endif +#endif + +/// +/// \def CCM_ENABLE_CLANG_WARNING_AS_ERROR(w) +/// \brief This will enable a warning as an error for clang. +/// \param w The warning to enable as an error. This should be written as -Wwarning-name and not as a string. +/// \note Only one warning can be treated as an error per statement, due to how clang works. +/// +/// Example usage: +/// CCM_ENABLE_CLANG_WARNING_AS_ERROR(-Wuninitialized) +/// CCM_ENABLE_CLANG_WARNING_AS_ERROR(-Wunused) +/// +/// CCM_DISABLE_CLANG_WARNING_AS_ERROR() +/// CCM_DISABLE_CLANG_WARNING_AS_ERROR() +/// +#ifndef CCM_ENABLE_CLANG_WARNING_AS_ERROR + #if defined(__clang__) + // Helper macros + #define CCM_CLANG_WERROR_HELP0(x) #x // Helper macros - do not use directly + #define CCM_CLANG_WERROR_HELP1(x) CCM_CLANG_WERROR_HELP0(clang diagnostic error x) // Helper macros - do not use directly + #define CCM_CLANG_WERROR_HELP2(x) CCM_CLANG_WERROR_HELP1(#x) // Helper macros - do not use directly + + // This will enable a warning as an error for clang. This should be written as -Wwarning-name and not as a string. + // Must be called before CCM_DISABLE_CLANG_WARNING_AS_ERROR(). + #define CCM_ENABLE_CLANG_WARNING_AS_ERROR(w) _Pragma("clang diagnostic push") _Pragma(CCM_CLANG_WERROR_HELP2(w)) + #else + // If not on clang this macro does nothing. + // Must be called before CCM_DISABLE_CLANG_WARNING_AS_ERROR(). + #define CCM_ENABLE_CLANG_WARNING_AS_ERROR(w) + #endif +#endif + +/// +/// \def CCM_DISABLE_CLANG_WARNING_AS_ERROR() +/// \brief This will disable a warning as an error for clang. +/// \note Only one warning can be treated as an error per statement, due to how clang works. +/// +/// Example usage: +/// CCM_ENABLE_CLANG_WARNING_AS_ERROR(-Wuninitialized) +/// CCM_ENABLE_CLANG_WARNING_AS_ERROR(-Wunused) +/// +/// CCM_DISABLE_CLANG_WARNING_AS_ERROR() +/// CCM_DISABLE_CLANG_WARNING_AS_ERROR() +/// +#ifndef CCM_DISABLE_CLANG_WARNING_AS_ERROR + #if defined(__clang__) + // This will disable a warning as an error for clang in a stack based manner. + // Must be called after CCM_ENABLE_CLANG_WARNING_AS_ERROR(w). + #define CCM_DISABLE_CLANG_WARNING_AS_ERROR() _Pragma("clang diagnostic pop") + #else + // If not on clang this macro does nothing. + // Must be called after CCM_ENABLE_CLANG_WARNING_AS_ERROR(w). + #define CCM_DISABLE_CLANG_WARNING_AS_ERROR() + #endif +#endif + +/// +/// \def CCM_DISABLE_MSVC_WARNING(w) +/// \brief Disables a warning for MSVC. +/// \param w The warning to disable as an error. Writen with a integer value to specify the warning. +/// \note This macro allows for multiple warnings to be disabled per statement by using a space between warnings. +/// \note This macro is effectively a wrapper for the VC++ #pragma warning(disable: warning-list) directive. +/// \note This macro must be called before CCM_RESTORE_VC_WARNING(). +/// +/// Example usage: +/// CCM_DISABLE_MSVC_WARNING(4100 4101) +/// +/// CCM_RESTORE_MSVC_WARNING() +/// +#ifndef CCM_DISABLE_MSVC_WARNING + #if defined(_MSC_VER) && !defined(__clang__) + // This will disable a list of warning for MSVC. + // This macro allows for multiple warnings to be disabled per statement by using a space between warnings like so: CCM_DISABLE_MSVC_WARNING(4100 4101). + // Must be called before CCM_RESTORE_MSVC_WARNING(). + #define CCM_DISABLE_VC_WARNING(w) __pragma(warning(push)) __pragma(warning(disable : w)) + #else + // If not on MSVC this macro does nothing. + #define CCM_DISABLE_VC_WARNING(w) + #endif +#endif + +/// +/// \def CCM_RESTORE_MSVC_WARNING() +/// \brief Restore all disabled warnings for MSVC. +/// \note This macro must be called after CCM_DISABLE_VC_WARNING(w). +/// +/// Example usage: +/// CCM_DISABLE_MSVC_WARNING(4100 4101) +/// +/// CCM_RESTORE_MSVC_WARNING() +/// +#ifndef CCM_RESTORE_MSVC_WARNING + #if defined(_MSC_VER) && !defined(__clang__) + // This will restore all disabled warnings for MSVC in a single statement. + // Must be called after CCM_DISABLE_MSVC_WARNING(w). + #define CCM_RESTORE_VC_WARNING() __pragma(warning(pop)) + #else + // If not on MSVC this macro does nothing. + #define CCM_RESTORE_VC_WARNING() + #endif +#endif + +/// +/// \def CCM_ENABLE_MSVC_WARNING_AS_ERROR(w) +/// \brief This will enable a warning as an error for MSVC. +/// \param w The warning to enable as an error. Writen with a integer value to specify the warning. +/// \note This macro allows for multiple warnings to be disabled per statement by using a space between warnings. +/// \note This macro is effectively a wrapper for the VC++ #pragma warning(error: warning-list) directive. +/// \note This macro must be called before CCM_DISABLE_MSVC_WARNING_AS_ERROR(). +/// +/// Example usage: +/// CCM_ENABLE_MSVC_WARNING_AS_ERROR(4100 4101) +/// +/// CCM_DISABLE_MSVC_WARNING_AS_ERROR() +/// +#ifndef CCM_ENABLE_MSVC_WARNING_AS_ERROR + #if defined(_MSC_VER) && !defined(__clang__) + // This will enable a warning as an error for MSVC. + // You can specify multiple warnings by using a space between warnings like so: CCM_ENABLE_MSVC_WARNING_AS_ERROR(4100 4101). + // Must be called before CCM_DISABLE_MSVC_WARNING_AS_ERROR(). + #define CCM_ENABLE_MSVC_WARNING_AS_ERROR(w) __pragma(warning(push)) __pragma(warning(error : w)) + #else + // If not on MSVC this macro does nothing. + #define CCM_ENABLE_MSVC_WARNING_AS_ERROR(w) + #endif +#endif + +/// +/// \def CCM_DISABLE_MSVC_WARNING_AS_ERROR() +/// \brief This will disable a warning as an error for MSVC. +/// \note This macro allows for multiple warnings to be disabled per statement by using a space between warnings. +/// \note This macro is effectively a wrapper for the VC++ #pragma warning(error: warning-list) directive. +/// \note This macro must be called after CCM_ENABLE_MSVC_WARNING_AS_ERROR(w). +/// +/// Example usage: +/// CCM_ENABLE_MSVC_WARNING_AS_ERROR(4100 4101) +/// +/// CCM_DISABLE_MSVC_WARNING_AS_ERROR() +/// +#ifndef CCM_DISABLE_MSVC_WARNING_AS_ERROR + #if defined(_MSC_VER) && !defined(__clang__) + // This will restore all disabled warnings for MSVC in a single statement. + // Must be called after CCM_ENABLE_MSVC_WARNING_AS_ERROR(w). + #define CCM_DISABLE_MSVC_WARNING_AS_ERROR(w) __pragma(warning(pop)) + #else + // If not on MSVC this macro does nothing. + // Must be called after CCM_ENABLE_MSVC_WARNING_AS_ERROR(w). + #define CCM_DISABLE_MSVC_WARNING_AS_ERROR(w) + #endif +#endif + +/// +/// \def CCM_DISABLE_ALL_MSVC_WARNINGS() +/// \brief This will disable all warnings for MSVC. +/// \note This macro must be called before CCM_RESTORE_ALL_MSVC_WARNINGS(). +/// +/// Example usage: +/// CCM_DISABLE_ALL_MSVC_WARNINGS() +/// +/// CCM_RESTORE_ALL_MSVC_WARNINGS() +/// +#ifndef CCM_DISABLE_ALL_MSVC_WARNINGS + #if defined(_MSC_VER) && !defined(__clang__) + // This will disable all warnings for MSVC. + #define CCM_DISABLE_ALL_MSVC_WARNINGS() \ + __pragma(warning(push, 0)) __pragma(warning( \ + disable : 4244 4265 4267 4350 4472 4509 4548 4623 4710 4985 6320 4755 4625 4626 4702)) // Some warnings have to be explicitly specified. + #else + // If not on MSVC this macro does nothing. + #define CCM_DISABLE_ALL_MSVC_WARNINGS() + #endif +#endif + +/// +/// \def CCM_RESTORE_ALL_MSVC_WARNINGS() +/// \brief This will restore all disabled warnings for MSVC. +/// \note This macro must be called after CCM_DISABLE_ALL_MSVC_WARNINGS(). +/// +/// Example usage: +/// CCM_DISABLE_ALL_MSVC_WARNINGS() +/// +/// CCM_RESTORE_ALL_MSVC_WARNINGS() +/// +#ifndef CCM_RESTORE_ALL_MSVC_WARNINGS + #if defined(_MSC_VER) && !defined(__clang__) + // This will restore all disabled warnings for MSVC in a single statement. + #define CCM_RESTORE_ALL_MSVC_WARNINGS() __pragma(warning(pop)) + #else + // If not on MSVC this macro does nothing. + #define CCM_RESTORE_ALL_MSVC_WARNINGS() + #endif +#endif + +// clang-format on diff --git a/include/ccmath/internal/predef/has_attribute.hpp b/include/ccmath/internal/predef/has_attribute.hpp new file mode 100644 index 0000000..917bd47 --- /dev/null +++ b/include/ccmath/internal/predef/has_attribute.hpp @@ -0,0 +1,38 @@ +/* + * 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 + +/// +/// \def CCM_HAS_ATTR(x) +/// \brief This will check if a compiler has a specific attribute for GCC and Clang based compilers. +/// \note Currently only supports GCC and Clang based compilers. +/// \note This macro handles specifically checking for cpp attributes. +/// \param x The attribute to check for. +/// \return True if the compiler has the attribute, false otherwise. +/// +#ifndef CCM_HAS_ATTR + #if defined(__GNUC__) || defined(__clang__) + #if defined(__has_attribute) // Check if the compiler has __has_attribute() support. + // This will check if the compiler has a specific attribute. Only works for GCC and Clang based compilers. + #define CCM_HAS_ATTR(x) __has_attribute(x) + #elif defined(__has_cpp_attribute) // Attempt to fall back to __has_cpp_attribute if __has_attribute is not defined. + // This will check if the compiler has a specific attribute. Only works for GCC and Clang based compilers. + #define CCM_HAS_ATTR(x) __has_cpp_attribute(x) + #elif defined(__has_c_attribute) // Attempt to fall back to __has_c_attribute if __has_cpp_attribute is not defined. + // This will check if the compiler has a specific attribute. Only works for GCC and Clang based compilers. + #define CCM_HAS_ATTR(x) __has_c_attribute(x) + #else + // If the compiler doesn't have __has_cpp_attribute() then we can't check for attributes in C++. + #define CCM_HAS_ATTR(x) 0 + #endif + #else + // If not on a GCC or Clang based compiler then this macro does nothing. + #define CCM_HAS_ATTR(x) 0 + #endif +#endif diff --git a/include/ccmath/internal/predef/has_builtin.hpp b/include/ccmath/internal/predef/has_builtin.hpp new file mode 100644 index 0000000..f1dae76 --- /dev/null +++ b/include/ccmath/internal/predef/has_builtin.hpp @@ -0,0 +1,17 @@ +/* +* 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_HAS_BUILTIN +# if defined(__has_builtin) +# define CCM_HAS_BUILTIN(x) __has_builtin(x) +# else +# define CCM_HAS_BUILTIN(x) 0 +# endif +#endif // CCM_HAS_BUILTIN diff --git a/include/ccmath/internal/predef/likely.hpp b/include/ccmath/internal/predef/likely.hpp new file mode 100644 index 0000000..9f71714 --- /dev/null +++ b/include/ccmath/internal/predef/likely.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_LIKELY + #if defined(__cplusplus) && __cplusplus >= 202003L + #define CCM_LIKELY(x) [[likely]] (x) + #elif defined(__GNUC__) || defined(__clang__) + #define CCM_LIKELY(x) __builtin_expect(!!(x), 1) + #else + #define CCM_LIKELY(x) (!!(x)) + #endif // defined(__cplusplus) && __cplusplus >= 202003L +#endif // CCM_LIKELY diff --git a/include/ccmath/internal/predef/no_debug.hpp b/include/ccmath/internal/predef/no_debug.hpp new file mode 100644 index 0000000..ab9d87f --- /dev/null +++ b/include/ccmath/internal/predef/no_debug.hpp @@ -0,0 +1,49 @@ +/* + * 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/internal/predef/has_attribute.hpp" + +#ifndef CCM_NODEBUG + #if defined(__GNUC__) || defined(__clang__) + #if CCM_HAS_ATTR(__nodebug__) + #define CCM_NODEBUG __attribute__((__nodebug__)) + #else + #define CCM_NODEBUG + #endif + #else + #define CCM_NODEBUG + #endif +#endif + +/// +/// \def CCM_HAS_DECLSPEC_ATTR(x) +/// \brief This function-like macro takes a single identifier argument that is the name of an attribute implemented as a Microsoft-style __declspec attribute. +/// \note MSVC will always return \a true for this macro. +/// \note Currently this macro is supported only by clang based compilers and will return \a false on non-clang based compilers. +/// \param x The attribute to check for. +/// \return True if the compiler has the attribute, false otherwise. +/// +#ifndef CCM_HAS_DECLSPEC_ATTR + #if defined(_MSC_VER) + // MSVC will always return true for this macro. + #define CCM_HAS_DECLSPEC_ATTR(x) 1 + #elif defined(__clang__) + #if defined(__has_declspec_attribute) + // This will check if the compiler has access to a specific declspec attribute. Only works for Clang based compilers. + #define CCM_HAS_DECLSPEC_ATTR(x) __has_declspec_attribute(x) + #else + // If the compiler doesn't have __has_declspec_attribute() then we can't check for declspec attributes in C++. + #define CCM_HAS_DECLSPEC_ATTR(x) 0 + #endif + #else + // If not on a Clang based compiler then this macro does nothing. + #define CCM_HAS_DECLSPEC_ATTR(x) 0 + #endif +#endif diff --git a/include/ccmath/internal/predef/suppress.hpp b/include/ccmath/internal/predef/suppress.hpp new file mode 100644 index 0000000..2db43a7 --- /dev/null +++ b/include/ccmath/internal/predef/suppress.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 + +#if defined(__clang__) + #define CCM_SUPPRESS(x) [[gsl::suppress("x")]] +#else + #if defined(_MSC_VER) && !defined(__INTEL_COMPILER) + #define CCM_SUPPRESS(x) [[gsl::suppress(x)]] + #else + #define CCM_SUPPRESS(x) + #endif // _MSC_VER +#endif // __clang__ diff --git a/include/ccmath/internal/predef/unlikely.hpp b/include/ccmath/internal/predef/unlikely.hpp index 2734a0e..7db8b55 100644 --- a/include/ccmath/internal/predef/unlikely.hpp +++ b/include/ccmath/internal/predef/unlikely.hpp @@ -12,8 +12,8 @@ #if defined(__cplusplus) && __cplusplus >= 202003L #define CCM_UNLIKELY(x) [[unlikely]] (x) #elif defined(__GNUC__) || defined(__clang__) - #define CCM_UNLIKELY(x) __builtin_expect((x), 0) + #define CCM_UNLIKELY(x) __builtin_expect(!!(x), 0) #else - #define CCM_UNLIKELY(x) (x) - #endif -#endif + #define CCM_UNLIKELY(x) (!!(x)) + #endif // defined(__cplusplus) && __cplusplus >= 202003L +#endif // CCM_UNLIKELY diff --git a/include/ccmath/internal/setup.hpp b/include/ccmath/internal/setup.hpp index ccacfcd..b22f8d8 100644 --- a/include/ccmath/internal/setup.hpp +++ b/include/ccmath/internal/setup.hpp @@ -9,217 +9,11 @@ #pragma once // Note: This is used to generate the CCM_COMPILER_STRING macros -#ifndef INTERNAL_STRINGIFY - #define INTERNAL_STRINGIFY(x) INTERNAL_PRIMITIVE_STRINGIFY(x) -#endif #ifndef INTERNAL_PRIMITIVE_STRINGIFY #define INTERNAL_PRIMITIVE_STRINGIFY(x) #x #endif - -/// CCM_COMPILER_CPP20_ENABLED -// This check could have some issues with some compilers as a few have decided not to -// define __cplusplus correctly until they are fully supporting C++20, but this should -// not matter to use for our purposes. -#if !defined(CCM_COMPILER_CPP20_ENABLED) && defined(__cplusplus) - #if (__cplusplus >= 202002L) - #define CCM_COMPILER_CPP20_ENABLED 1 - #elif defined(_MSVC_LANG) && (_MSVC_LANG >= 202002L) // C++20+ - #define CCM_COMPILER_CPP20_ENABLED 1 - #endif -#endif - -// Clang's GCC-compatible driver. -#if defined(__clang__) && !defined(_MSC_VER) - #define CCM_COMPILER_CLANG 1 - #define CCM_COMPILER_CLANG_BASED 1 - #define CCM_COMPILER_VERSION (__clang_major__ * 100 + __clang_minor__ * 10 + __clang_patchlevel__) - - #if defined(__apple_build_version__) - #define CCM_COMPILER_APPLE_CLANG 1 - #define CCM_COMPILER_CLANG_BASED 1 - #endif - -// GCC (a.k.a. GNUC) -#elif defined(__GNUC__) - #define CCM_COMPILER_GCC 1 - #define CCM_COMPILER_VERSION (__GNUC__ * 100 + __GNUC_MINOR__ * 10 + __GNUC_PATCHLEVEL__) - -#elif defined(_MSC_VER) - #define CCM_COMPILER_MSVC 1 - #define CCM_COMPILER_VERSION _MSC_VER - - #if defined(__clang__) - // Clang's MSVC-compatible driver. - #define CCM_COMPILER_CLANG_CL 1 - #endif -#endif - -// ------------------------------------------------------------------------ -// CCM_DISABLE_GCC_WARNING / CCM_RESTORE_GCC_WARNING -// -// Example usage: -// // Only one warning can be ignored per statement, due to how GCC works. -// CCM_DISABLE_GCC_WARNING(-Wuninitialized) -// CCM_DISABLE_GCC_WARNING(-Wunused) -// -// CCM_RESTORE_GCC_WARNING() -// CCM_RESTORE_GCC_WARNING() -// -#ifndef CCM_DISABLE_GCC_WARNING - #if defined(CCM_COMPILER_GCC) - #define CCMGCCWHELP0(x) #x - #define CCMGCCWHELP1(x) CCMGCCWHELP0(GCC diagnostic ignored x) - #define CCMGCCWHELP2(x) CCMGCCWHELP1(#x) - #endif - - #if defined(CCM_COMPILER_GCC) && (CCM_COMPILER_VERSION >= 4006) // Can't test directly for __GNUC__ because some compilers lie. - #define CCM_DISABLE_GCC_WARNING(w) _Pragma("GCC diagnostic push") _Pragma(CCMGCCWHELP2(w)) - #else - #define CCM_DISABLE_GCC_WARNING(w) - #endif -#endif - -#ifndef CCM_RESTORE_GCC_WARNING - #if defined(CCM_COMPILER_GCC) && (CCM_COMPILER_VERSION >= 4006) - #define CCM_RESTORE_GCC_WARNING() _Pragma("GCC diagnostic pop") - #else - #define CCM_RESTORE_GCC_WARNING() - #endif -#endif - -// ------------------------------------------------------------------------ -// CCM_ENABLE_GCC_WARNING_AS_ERROR / CCM_DISABLE_GCC_WARNING_AS_ERROR -// -// Example usage: -// // Only one warning can be treated as an error per statement, due to how GCC works. -// CCM_ENABLE_GCC_WARNING_AS_ERROR(-Wuninitialized) -// CCM_ENABLE_GCC_WARNING_AS_ERROR(-Wunused) -// -// CCM_DISABLE_GCC_WARNING_AS_ERROR() -// CCM_DISABLE_GCC_WARNING_AS_ERROR() -// -#ifndef CCM_ENABLE_GCC_WARNING_AS_ERROR - #if defined(CCM_COMPILER_GCC) - #define CCMGCCWERRORHELP0(x) #x - #define CCMGCCWERRORHELP1(x) CCMGCCWERRORHELP0(GCC diagnostic error x) - #define CCMGCCWERRORHELP2(x) CCMGCCWERRORHELP1(#x) - #endif -#endif - -#if defined(CCM_COMPILER_GCC) && (CCM_COMPILER_VERSION >= 4006) // Can't test directly for __GNUC__ because some compilers lie. - #define CCM_ENABLE_GCC_WARNING_AS_ERROR(w) _Pragma("GCC diagnostic push") _Pragma(CCMGCCWERRORHELP2(w)) -#else - #define CCM_ENABLE_GCC_WARNING_AS_ERROR(w) -#endif - -#ifndef CCM_DISABLE_GCC_WARNING_AS_ERROR - #if defined(CCM_COMPILER_GCC) && (CCM_COMPILER_VERSION >= 4006) - #define CCM_DISABLE_GCC_WARNING_AS_ERROR() _Pragma("GCC diagnostic pop") - #else - #define CCM_DISABLE_GCC_WARNING_AS_ERROR() - #endif -#endif - -#ifndef CCM_ENABLE_IGNORE_GCC_WARNING - #if defined(CCM_COMPILER_GCC) - #define CCMGCCIGNOREHELP0(x) #x - #define CCMGCCIGNOREHELP1(x) CCMGCCIGNOREHELP0(GCC diagnostic ignored x) - #define CCMGCCIGNOREHELP2(x) CCMGCCIGNOREHELP1(#x) - #endif -#endif - -#ifndef CCM_ENABLE_IGNORE_GCC_WARNING - #if defined(CCM_COMPILER_GNUC) && (CCM_COMPILER_VERSION >= 4006) // Can't test directly for __GNUC__ because some compilers lie. - #define CCM_ENABLE_IGNORE_GCC_WARNING(w) _Pragma("GCC diagnostic push") _Pragma(CCMGCCIGNOREHELP2(w)) - #else - #define CCM_ENABLE_IGNORE_GCC_WARNING(w) - #endif -#endif - -#ifndef CCM_DISABLE_IGNORE_GCC_WARNING - #if defined(CCM_COMPILER_GCC) - #define CCMGCCIGNOREDISABLEHELP0(x) #x - #define CCMGCCIGNOREDISABLEHELP1(x) CCMGCCIGNOREDISABLEHELP0(GCC diagnostic warning x) - #define CCMGCCIGNOREDISABLEHELP2(x) CCMGCCIGNOREDISABLEHELP1(#x) - #endif -#endif - -#ifndef CCM_DISABLE_IGNORE_GCC_WARNING - #if defined(CCM_COMPILER_GCC) && (CCM_COMPILER_VERSION >= 4004) - #define CCM_DISABLE_IGNORE_GCC_WARNING(w) _Pragma(CCMGCCIGNOREDISABLEHELP2(w)) _Pragma("GCC diagnostic pop") - #else - #define CCM_DISABLE_IGNORE_GCC_WARNING(w) - #endif -#endif - -// ------------------------------------------------------------------------ -// CCM_DISABLE_CLANG_WARNING / CCM_RESTORE_CLANG_WARNING -// -// Example usage: -// // Only one warning can be ignored per statement, due to how clang works. -// CCM_DISABLE_CLANG_WARNING(-Wuninitialized) -// CCM_DISABLE_CLANG_WARNING(-Wunused) -// -// CCM_RESTORE_CLANG_WARNING() -// CCM_RESTORE_CLANG_WARNING() -// -#ifndef CCM_DISABLE_CLANG_WARNING - #if defined(CCM_COMPILER_CLANG_BASED) || defined(CCM_COMPILER_CLANG_CL) - #define CCMCLANGWHELP0(x) #x - #define CCMCLANGWHELP1(x) CCMCLANGWHELP0(clang diagnostic ignored x) - #define CCMCLANGWHELP2(x) CCMCLANGWHELP1(#x) - - #define CCM_DISABLE_CLANG_WARNING(w) _Pragma("clang diagnostic push") _Pragma(CCMCLANGWHELP2(-Wunknown - warning - option)) _Pragma(CCMCLANGWHELP2(w)) - #else - #define CCM_DISABLE_CLANG_WARNING(w) - #endif -#endif - -#ifndef CCM_RESTORE_CLANG_WARNING - #if defined(CCM_COMPILER_CLANG_BASED) || defined(CCM_COMPILER_CLANG_CL) - #define CCM_RESTORE_CLANG_WARNING() _Pragma("clang diagnostic pop") - #else - #define CCM_RESTORE_CLANG_WARNING() - #endif -#endif - -// ------------------------------------------------------------------------ -// CCM_ENABLE_CLANG_WARNING_AS_ERROR / CCM_DISABLE_CLANG_WARNING_AS_ERROR -// -// Example usage: -// // Only one warning can be treated as an error per statement, due to how clang works. -// CCM_ENABLE_CLANG_WARNING_AS_ERROR(-Wuninitialized) -// CCM_ENABLE_CLANG_WARNING_AS_ERROR(-Wunused) -// -// CCM_DISABLE_CLANG_WARNING_AS_ERROR() -// CCM_DISABLE_CLANG_WARNING_AS_ERROR() -// -#ifndef CCM_ENABLE_CLANG_WARNING_AS_ERROR - #if defined(CCM_COMPILER_CLANG_BASED) || defined(CCM_COMPILER_CLANG_CL) - #define CCMCLANGWERRORHELP0(x) #x - #define CCMCLANGWERRORHELP1(x) CCMCLANGWERRORHELP0(clang diagnostic error x) - #define CCMCLANGWERRORHELP2(x) CCMCLANGWERRORHELP1(#x) - - #define CCM_ENABLE_CLANG_WARNING_AS_ERROR(w) _Pragma("clang diagnostic push") _Pragma(CCMCLANGWERRORHELP2(w)) - #else - #define CCM_DISABLE_CLANG_WARNING(w) - #endif -#endif - -#ifndef CCM_DISABLE_CLANG_WARNING_AS_ERROR - #if defined(CCM_COMPILER_CLANG_BASED) || defined(CCM_COMPILER_CLANG_CL) - #define CCM_DISABLE_CLANG_WARNING_AS_ERROR() _Pragma("clang diagnostic pop") - #else - #define CCM_DISABLE_CLANG_WARNING_AS_ERROR() - #endif +#ifndef INTERNAL_STRINGIFY + #define INTERNAL_STRINGIFY(x) INTERNAL_PRIMITIVE_STRINGIFY(x) #endif -// MSVC -#if defined(CCM_COMPILER_MSVC) - #define CCM_DISABLE_MSVC_WARNING(w) __pragma(warning(push)) __pragma(warning(disable : w)) - #define CCM_RESTORE_MSVC_WARNING() __pragma(warning(pop)) - #define CCM_ENABLE_MSVC_WARNING_AS_ERROR(w) __pragma(warning(push)) __pragma(warning(error : w)) - #define CCM_DISABLE_MSVC_WARNING_AS_ERROR() __pragma(warning(pop)) -#endif - diff --git a/include/ccmath/internal/setup/builtin_support_undef.hpp b/include/ccmath/internal/setup/builtin_support_undef.hpp deleted file mode 100644 index e3d7bdc..0000000 --- a/include/ccmath/internal/setup/builtin_support_undef.hpp +++ /dev/null @@ -1,23 +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 - - -#ifdef CCMATH_HAS_BUILTIN_BIT_CAST - #undef CCMATH_HAS_BUILTIN_BIT_CAST -#endif - -#ifdef CCMATH_HAS_CONSTEXPR_BUILTIN_SIGNBIT - #undef CCMATH_HAS_CONSTEXPR_BUILTIN_SIGNBIT -#endif - -#ifdef CCMATH_HAS_CONSTEXPR_BUILTIN_COPYSIGN - #undef CCMATH_HAS_CONSTEXPR_BUILTIN_COPYSIGN -#endif - diff --git a/include/ccmath/internal/setup/compiler_def.hpp b/include/ccmath/internal/setup/compiler_def.hpp deleted file mode 100644 index 2dc4187..0000000 --- a/include/ccmath/internal/setup/compiler_def.hpp +++ /dev/null @@ -1,122 +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. - */ - -// Currently supported compilers: -// - GCC -// - Clang -// - Apple Clang -// - Intel DPC++ -// - Nvidia HPC SDK -// - MSVC - -/// !!! ATTENTION !!! -/// If you add a new compiler macro remember to undefine it inside of compiler_undef.hpp also! -/// !!! ATTENTION !!! - - -#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__ - #define CCMATH_COMPILER_GCC_VER_MINOR __GNUC_MINOR__ - #define CCMATH_COMPILER_GCC_VER_PATCH __GNUC_PATCHLEVEL__ - - #ifndef CCMATH_FOUND_COMPILER - #define CCMATH_FOUND_COMPILER - #endif -#endif - -// MSVC -#if defined(_MSC_VER) && !defined(__clang__) - #define CCMATH_COMPILER_MSVC - #define CCMATH_COMPILER_MSVC_VER _MSC_VER - - #ifndef CCMATH_FOUND_COMPILER - #define CCMATH_FOUND_COMPILER - #endif -#endif - -#if defined(_MSC_VER) && defined(__clang__) - #define CCMATH_COMPILER_CLANG_CL - #define CCMATH_COMPILER_CLANG_CL_VER ((__clang_major__ * 10000) + (__clang_minor__ * 100) + __clang_patchlevel__) - #define CCMATH_COMPILER_CLANG_CL_VER_MAJOR __clang_major__ - #define CCMATH_COMPILER_CLANG_CL_VER_MINOR __clang_minor__ - #define CCMATH_COMPILER_CLANG_CL_VER_PATCH __clang_patchlevel__ - - #ifndef CCMATH_COMPILER_CLANG_BASED - #define CCMATH_COMPILER_CLANG_BASED - #endif - #ifndef CCMATH_FOUND_COMPILER - #define CCMATH_FOUND_COMPILER - #endif -#endif - -// Clang -#if defined(__clang__) - #define CCMATH_COMPILER_CLANG - #define CCMATH_COMPILER_CLANG_VER ((__clang_major__ * 100) + (__clang_minor__ * 10) + __clang_patchlevel__) - #define CCMATH_COMPILER_CLANG_VER_MAJOR __clang_major__ - #define CCMATH_COMPILER_CLANG_VER_MINOR __clang_minor__ - #define CCMATH_COMPILER_CLANG_VER_PATCH __clang_patchlevel__ - - #ifndef CCMATH_COMPILER_CLANG_BASED - #define CCMATH_COMPILER_CLANG_BASED - #endif - #ifndef CCMATH_FOUND_COMPILER - #define CCMATH_FOUND_COMPILER - #endif -#endif - -// Apple Clang -#if defined(__apple_build_version__) && defined(__clang__) - #define CCMATH_COMPILER_APPLE_CLANG - #define CCMATH_COMPILER_APPLE_CLANG_VER ((__clang_major__ * 10000) + (__clang_minor__ * 100) + __clang_patchlevel__) - #define CCMATH_COMPILER_APPLE_CLANG_VER_MAJOR __clang_major__ - #define CCMATH_COMPILER_APPLE_CLANG_VER_MINOR __clang_minor__ - #define CCMATH_COMPILER_APPLE_CLANG_VER_PATCH __clang_patchlevel__ - - #ifndef CCMATH_COMPILER_CLANG_BASED - #define CCMATH_COMPILER_CLANG_BASED - #endif - #ifndef CCMATH_FOUND_COMPILER - #define CCMATH_FOUND_COMPILER - #endif -#endif - -// Intel DPC++ Compiler -#if defined(SYCL_LANGUAGE_VERSION) || defined(__INTEL_LLVM_COMPILER) - #define CCMATH_COMPILER_INTEL - #define CCMATH_COMPILER_INTEL_VER __INTEL_LLVM_COMPILER - - #ifndef CCMATH_COMPILER_CLANG_BASED - #define CCMATH_COMPILER_CLANG_BASED - #endif - #ifndef CCMATH_FOUND_COMPILER - #define CCMATH_FOUND_COMPILER - #endif -#endif - -// Nvidia HPC SDK -#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_FOUND_COMPILER - #define CCMATH_FOUND_COMPILER - #endif -#endif - - - -#if !defined(CCMATH_FOUND_COMPILER) - #define CCMATH_UNKNOWN_COMPILER -#endif - diff --git a/include/ccmath/internal/setup/compiler_undef.hpp b/include/ccmath/internal/setup/compiler_undef.hpp deleted file mode 100644 index c19c7df..0000000 --- a/include/ccmath/internal/setup/compiler_undef.hpp +++ /dev/null @@ -1,144 +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. - */ - -// Undefine all the compiler macros to clean up the global namespace - -// GCC -#ifdef CCMATH_COMPILER_GCC - #undef CCMATH_COMPILER_GCC -#endif - -#ifdef CCMATH_COMPILER_GCC_VER - #undef CCMATH_COMPILER_GCC_VER -#endif - -#ifdef CCMATH_COMPILER_GCC_VER_MAJOR - #undef CCMATH_COMPILER_GCC_VER_MAJOR -#endif - -#ifdef CCMATH_COMPILER_GCC_VER_MINOR - #undef CCMATH_COMPILER_GCC_VER_MINOR -#endif - -#ifdef CCMATH_COMPILER_GCC_VER_PATCH - #undef CCMATH_COMPILER_GCC_VER_PATCH -#endif - -// MSVC -#ifdef CCMATH_COMPILER_MSVC - #undef CCMATH_COMPILER_MSVC -#endif - -#ifdef CCMATH_COMPILER_MSVC_VER - #undef CCMATH_COMPILER_MSVC_VER -#endif - -// Clang - -#ifdef CCMATH_COMPILER_CLANG - #undef CCMATH_COMPILER_CLANG -#endif - -#ifdef CCMATH_COMPILER_CLANG_VER - #undef CCMATH_COMPILER_CLANG_VER -#endif - -#ifdef CCMATH_COMPILER_CLANG_VER_MAJOR - #undef CCMATH_COMPILER_CLANG_VER_MAJOR -#endif - -#ifdef CCMATH_COMPILER_CLANG_VER_MINOR - #undef CCMATH_COMPILER_CLANG_VER_MINOR -#endif - -#ifdef CCMATH_COMPILER_CLANG_VER_PATCH - #undef CCMATH_COMPILER_CLANG_VER_PATCH -#endif - -// Apple Clang -#ifdef CCMATH_COMPILER_APPLE_CLANG - #undef CCMATH_COMPILER_APPLE_CLANG -#endif - -#ifdef CCMATH_COMPILER_APPLE_CLANG_VER - #undef CCMATH_COMPILER_APPLE_CLANG_VER -#endif - -#ifdef CCMATH_COMPILER_APPLE_CLANG_VER_MAJOR - #undef CCMATH_COMPILER_APPLE_CLANG_VER_MAJOR -#endif - -#ifdef CCMATH_COMPILER_APPLE_CLANG_VER_MINOR - #undef CCMATH_COMPILER_APPLE_CLANG_VER_MINOR -#endif - -#ifdef CCMATH_COMPILER_APPLE_CLANG_VER_PATCH - #undef CCMATH_COMPILER_APPLE_CLANG_VER_PATCH -#endif - -// Clang CL -#ifdef CCMATH_COMPILER_CLANG_CL - #undef CCMATH_COMPILER_CLANG_CL -#endif - -#ifdef CCMATH_COMPILER_CLANG_CL_VER - #undef CCMATH_COMPILER_CLANG_CL_VER -#endif - -#ifdef CCMATH_COMPILER_CLANG_CL_VER_MAJOR - #undef CCMATH_COMPILER_CLANG_CL_VER_MAJOR -#endif - -#ifdef CCMATH_COMPILER_CLANG_CL_VER_MINOR - #undef CCMATH_COMPILER_CLANG_CL_VER_MINOR -#endif - -#ifdef CCMATH_COMPILER_CLANG_CL_VER_PATCH - #undef CCMATH_COMPILER_CLANG_CL_VER_PATCH -#endif - -// Clang Based -#ifdef CCMATH_COMPILER_CLANG_BASED - #undef CCMATH_COMPILER_CLANG_BASED -#endif - -// Intel -#ifdef CCMATH_COMPILER_INTEL - #undef CCMATH_COMPILER_INTEL -#endif - -#ifdef CCMATH_COMPILER_INTEL_VER - #undef CCMATH_COMPILER_INTEL_VER -#endif - - -// Nvidia -#ifdef CCMATH_COMPILER_NVIDIA - #undef CCMATH_COMPILER_NVIDIA -#endif - -#ifdef CCMATH_COMPILER_NVIDIA_VER - #undef CCMATH_COMPILER_NVIDIA_VER -#endif - -#ifdef CCMATH_COMPILER_NVIDIA_VER_MAJOR - #undef CCMATH_COMPILER_NVIDIA_VER_MAJOR -#endif - -#ifdef CCMATH_COMPILER_NVIDIA_VER_MINOR - #undef CCMATH_COMPILER_NVIDIA_VER_MINOR -#endif - -#ifdef CCMATH_COMPILER_NVIDIA_VER_PATCH - #undef CCMATH_COMPILER_NVIDIA_VER_PATCH -#endif - -// Unknown -#ifdef CCMATH_UNKNOWN_COMPILER - #undef CCMATH_UNKNOWN_COMPILER -#endif diff --git a/include/ccmath/detail/fmanip/scalbn.hpp b/include/ccmath/internal/support/always_false.hpp similarity index 62% rename from include/ccmath/detail/fmanip/scalbn.hpp rename to include/ccmath/internal/support/always_false.hpp index b950a84..45d5ad1 100644 --- a/include/ccmath/detail/fmanip/scalbn.hpp +++ b/include/ccmath/internal/support/always_false.hpp @@ -8,7 +8,8 @@ #pragma once -namespace ccm +namespace ccm::support { - -} // namespace ccm + template + inline constexpr bool always_false = false; +} // namespace ccm::support diff --git a/include/ccmath/internal/support/bits.hpp b/include/ccmath/internal/support/bits.hpp new file mode 100644 index 0000000..fc93c08 --- /dev/null +++ b/include/ccmath/internal/support/bits.hpp @@ -0,0 +1,279 @@ +/* + * 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. + */ + +// Support header that brings C++20's header to C++17. + +#pragma once + +#include "ccmath/internal/support/ctz.hpp" +#include "ccmath/internal/predef/has_builtin.hpp" + +#include +#include + +namespace ccm::support::traits +{ + // TODO: Later add this into its own header. + // clang-format off + template struct is_char : std::false_type {}; + template <> struct is_char : std::true_type {}; + template <> struct is_char : std::true_type {}; +#if (__cplusplus >= 202002L) || defined(__cpp_char8_t) || defined(__cpp_lib_char8_t) // C++20 defines char8_t + template <> struct is_char : std::true_type {}; +#endif + template <> struct is_char : std::true_type {}; + template <> struct is_char : std::true_type {}; + template <> struct is_char : std::true_type {}; + template <> struct is_char : std::true_type {}; + template constexpr bool is_char_v = is_char::value; + + template struct is_unsigned_integer : std::false_type {}; + template <> struct is_unsigned_integer : std::true_type {}; + template <> struct is_unsigned_integer : std::true_type {}; + template <> struct is_unsigned_integer : std::true_type {}; + template <> struct is_unsigned_integer : std::true_type {}; + template <> struct is_unsigned_integer : std::true_type {}; +#if defined(__SIZEOF_INT128__) + template <> struct is_unsigned_integer<__uint128_t> : std::true_type {}; +#endif + template constexpr bool is_unsigned_integer_v = is_unsigned_integer::value; + // clang-format on + +} // namespace ccm::support::traits + +namespace ccm::support +{ + + /** + * @brief + * @tparam To + * @tparam From + * @param src + * @return + */ + template + std::enable_if_t && std::is_trivially_copyable_v, To> 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::support::traits::is_char_v && !std::is_same_v, bool> = true> + constexpr bool has_single_bit(T x) noexcept + { + return x && !(x & (x - 1)); + } + + /** + * @brief Helper function to get the top 16-bits of a double. + * @param x Double to get the bits from. + * @return + */ + constexpr std::uint32_t top16_bits_of_double(double x) noexcept + { + return static_cast(bit_cast(x) >> 48); + } + + constexpr std::uint32_t top12_bits_of_double(double x) noexcept + { + return static_cast(bit_cast(x) >> 52); + } + + constexpr std::uint32_t top12_bits_of_float(float x) noexcept + { + return bit_cast(x) >> 20; + } + + constexpr std::uint64_t double_to_uint64(double x) noexcept + { + return bit_cast(x); + } + + constexpr std::int64_t double_to_int64(double x) noexcept + { + return bit_cast(x); + } + + constexpr double uint64_to_double(std::uint64_t x) noexcept + { + return bit_cast(x); + } + + constexpr double int64_to_double(std::int64_t x) noexcept + { + return bit_cast(x); + } + + constexpr std::uint32_t float_to_uint32(float x) noexcept + { + return bit_cast(x); + } + + constexpr std::int32_t float_to_int32(float x) noexcept + { + return bit_cast(x); + } + + constexpr float uint32_to_float(std::uint32_t x) noexcept + { + return bit_cast(x); + } + + constexpr float int32_to_float(std::int32_t x) noexcept + { + return bit_cast(x); + } + + /** + * @brief Rotates unsigned integer bits to the right. + * https://en.cppreference.com/w/cpp/numeric/rotr + */ + template + constexpr T rotr(T t, int cnt) noexcept + { + static_assert(ccm::support::traits::is_unsigned_integer_v, "rotr requires an unsigned integer type"); + const unsigned int dig = std::numeric_limits::digits; + if ((static_cast(cnt) % dig) == 0) { return t; } + + if (cnt < 0) + { + cnt *= -1; + return (t << (static_cast(cnt) % dig)) | + (t >> (dig - (static_cast(cnt) % dig))); // rotr with negative cnt is similar to rotl + } + + return (t >> (static_cast(cnt) % dig)) | (t << (dig - (static_cast(cnt) % dig))); + } + + /** + * @brief Rotates unsigned integer bits to the left. + * https://en.cppreference.com/w/cpp/numeric/rotl + */ + template + constexpr T rotl(T t, int cnt) noexcept + { + return rotr(t, -cnt); + } + + // https://en.cppreference.com/w/cpp/numeric/countr_zero + template + [[nodiscard]] constexpr std::enable_if_t, int> countr_zero(T value) + { + if (value == 0) { return std::numeric_limits::digits; } + + if constexpr (ccm::support::traits::is_unsigned_integer_v) { return ccm::support::ctz(value); } + + int ret = 0; + const unsigned int ulldigits = std::numeric_limits::digits; + while (static_cast(value) == 0ULL) + { + ret += ulldigits; + value >>= ulldigits; + } + return ret + ccm::support::ctz(static_cast(value)); + } + + template + [[nodiscard]] constexpr std::enable_if_t, int> countr_one(T value) + { + return value != std::numeric_limits::max() ? countr_zero(static_cast(~value)) : std::numeric_limits::digits; + } + + template , bool> = true> + [[nodiscard]] constexpr std::enable_if_t, int> countl_zero(T value) + { + if (value == 0) { return std::numeric_limits::digits; } + + if constexpr (ccm::support::traits::is_unsigned_integer_v) { return std::numeric_limits::digits - ccm::support::ctz(value); } + + int ret = 0; + int iter = 0; + const unsigned int ulldigits = std::numeric_limits::digits; + while (true) + { + value = rotl(value, ulldigits); + if ((iter = countl_zero(static_cast(value))) != ulldigits) // NOLINT + break; + ret += iter; + } + return ret + iter; + } + + template , bool> = true> + [[nodiscard]] constexpr std::enable_if_t, int> countl_one(T value) + { + return value != std::numeric_limits::max() ? countl_zero(static_cast(~value)) : std::numeric_limits::digits; + } + + template + [[nodiscard]] constexpr std::enable_if_t, int> bit_width(T value) + { + return std::numeric_limits::digits - countl_zero(value); + } + +#if CCM_HAS_BUILTIN(__builtin_popcountg) + template + [[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t, int> popcount(T value) + { + return __builtin_popcountg(value); + } +#else // !CCM_HAS_BUILTIN(__builtin_popcountg) + template + [[nodiscard]] constexpr std::enable_if_t, int> popcount(T value) + { + int count = 0; + for (int i = 0; i != std::numeric_limits::digits; ++i) + { + if ((value >> i) & 0x1) { ++count; } + } + return count; + } +#endif // CCM_HAS_BUILTIN(__builtin_popcountg) + +// If the compiler has builtin's for popcount, the create specializations that use the builtin. +#if CCM_HAS_BUILTIN(__builtin_popcount) + template <> + [[nodiscard]] constexpr int popcount(unsigned char value) + { + return __builtin_popcount(value); + } + + template <> + [[nodiscard]] constexpr int popcount(unsigned short value) + { + return __builtin_popcount(value); + } + + template <> + [[nodiscard]] constexpr int popcount(unsigned value) + { + return __builtin_popcount(value); + } +#endif // CCM_HAS_BUILTIN(__builtin_popcount) + +#if CCM_HAS_BUILTIN(__builtin_popcountl) + template <> + [[nodiscard]] constexpr int popcount(unsigned long value) + { + return __builtin_popcountl(value); + } +#endif // CCM_HAS_BUILTIN(__builtin_popcountl) + +#if CCM_HAS_BUILTIN(__builtin_popcountll) + template <> + [[nodiscard]] constexpr int popcount(unsigned long long value) + { + return __builtin_popcountll(value); + } +#endif // CCM_HAS_BUILTIN(__builtin_popcountll) + +} // namespace ccm::support diff --git a/include/ccmath/internal/support/ctz.hpp b/include/ccmath/internal/support/ctz.hpp new file mode 100644 index 0000000..7941cbb --- /dev/null +++ b/include/ccmath/internal/support/ctz.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 "ccmath/internal/predef/has_builtin.hpp" + +#include +#include + +namespace ccm::support +{ + namespace internal + { + // Software implementation of ctz for unsigned integral types in the event that the compiler does not provide a builtin + // Mostly added for msvc support, as gcc and clang have builtins for this. + template && std::is_unsigned_v && !std::is_same_v, bool> = true> + constexpr int generic_ctz(T x) noexcept + { + // If x is 0, the result is undefined. + if (x == 0) + { + // We return the size of the type in bits to indicate undefined behavior. + // This mimics the behavior of __builtin_ctz. + return sizeof(T) * std::numeric_limits::digits; + } + + int count = 0; + + // Loop until the least significant bit is 1 + while ((x & 1) == 0) + { + ++count; + x >>= 1; // Right shift x by 1 bit + } + + return count; + } + } // namespace internal + + template + constexpr int ctz(T /* x */) noexcept; + + template <> + constexpr int ctz(unsigned short x) noexcept + { +#if CCM_HAS_BUILTIN(__builtin_ctzs) + return __builtin_ctzs(x); +#else + return internal::generic_ctz(x); +#endif + } + + template <> + constexpr int ctz(unsigned int x) noexcept + { +#if CCM_HAS_BUILTIN(__builtin_ctz) + return __builtin_ctz(x); +#else + return internal::generic_ctz(x); +#endif + } + + template <> + constexpr int ctz(unsigned long x) noexcept + { +#if CCM_HAS_BUILTIN(__builtin_ctzl) + return __builtin_ctzl(x); +#else + return internal::generic_ctz(x); +#endif + } + + template <> + constexpr int ctz(unsigned long long x) noexcept + { +#if CCM_HAS_BUILTIN(__builtin_ctzll) + return __builtin_ctzll(x); +#else + return internal::generic_ctz(x); +#endif + } +} // namespace ccm::support diff --git a/include/ccmath/internal/helpers/endian.hpp b/include/ccmath/internal/support/endian.hpp similarity index 86% rename from include/ccmath/internal/helpers/endian.hpp rename to include/ccmath/internal/support/endian.hpp index c35f12e..20d8d61 100644 --- a/include/ccmath/internal/helpers/endian.hpp +++ b/include/ccmath/internal/support/endian.hpp @@ -8,9 +8,11 @@ #pragma once +#include + namespace ccm::helpers { - enum class endian + enum class endian : std::uint16_t { // TODO: Add support for other compilers besides just MSVC, GCC, and Clang. @@ -24,6 +26,6 @@ namespace ccm::helpers native = __BYTE_ORDER__ #endif }; -} // namespace ccm +} // namespace ccm::helpers diff --git a/include/ccmath/internal/type_traits/floating_point_traits.hpp b/include/ccmath/internal/support/floating_point_traits.hpp similarity index 81% rename from include/ccmath/internal/type_traits/floating_point_traits.hpp rename to include/ccmath/internal/support/floating_point_traits.hpp index 7b84061..2928e3d 100644 --- a/include/ccmath/internal/type_traits/floating_point_traits.hpp +++ b/include/ccmath/internal/support/floating_point_traits.hpp @@ -8,6 +8,8 @@ #pragma once +#include "ccmath/internal/types/float128.hpp" + #include #include @@ -29,12 +31,12 @@ namespace ccm::helpers using uint_type = std::uint32_t; - static constexpr std::uint32_t exponent_mask = 0x000000FFu; // (1u << exponent_bits) - 1 - static constexpr std::uint32_t normal_mantissa_mask = 0x00FFFFFFu; // (1u << mantissa_bits) - 1 - static constexpr std::uint32_t denormal_mantissa_mask = 0x007FFFFFu; // (1u << (mantissa_bits - 1)) - 1 - static constexpr std::uint32_t special_nan_mantissa_mask = 0x00400000u; // 1u << (mantissa_bits - 2) - static constexpr std::uint32_t shifted_sign_mask = 0x80000000u; // 1u << sign_shift - static constexpr std::uint32_t shifted_exponent_mask = 0x7F800000u; // exponent_mask << exponent_shift + static constexpr std::uint32_t exponent_mask = 0x000000FFU; // (1u << exponent_bits) - 1 + static constexpr std::uint32_t normal_mantissa_mask = 0x00FFFFFFU; // (1u << mantissa_bits) - 1 + static constexpr std::uint32_t denormal_mantissa_mask = 0x007FFFFFU; // (1u << (mantissa_bits - 1)) - 1 + static constexpr std::uint32_t special_nan_mantissa_mask = 0x00400000U; // 1u << (mantissa_bits - 2) + static constexpr std::uint32_t shifted_sign_mask = 0x80000000U; // 1u << sign_shift + static constexpr std::uint32_t shifted_exponent_mask = 0x7F800000U; // exponent_mask << exponent_shift }; template <> @@ -50,18 +52,25 @@ namespace ccm::helpers using uint_type = std::uint64_t; - static constexpr std::uint64_t exponent_mask = 0x00000000000007FFu; // (1ULL << exponent_bits) - 1 - static constexpr std::uint64_t normal_mantissa_mask = 0x001FFFFFFFFFFFFFu; // (1ULL << mantissa_bits) - 1 - static constexpr std::uint64_t denormal_mantissa_mask = 0x000FFFFFFFFFFFFFu; // (1ULL << (mantissa_bits - 1)) - 1 - static constexpr std::uint64_t special_nan_mantissa_mask = 0x0008000000000000u; // 1ULL << (mantissa_bits - 2) - static constexpr std::uint64_t shifted_sign_mask = 0x8000000000000000u; // 1ULL << sign_shift - static constexpr std::uint64_t shifted_exponent_mask = 0x7FF0000000000000u; // exponent_mask << exponent_shift + static constexpr std::uint64_t exponent_mask = 0x00000000000007FFU; // (1ULL << exponent_bits) - 1 + static constexpr std::uint64_t normal_mantissa_mask = 0x001FFFFFFFFFFFFFU; // (1ULL << mantissa_bits) - 1 + static constexpr std::uint64_t denormal_mantissa_mask = 0x000FFFFFFFFFFFFFU; // (1ULL << (mantissa_bits - 1)) - 1 + static constexpr std::uint64_t special_nan_mantissa_mask = 0x0008000000000000U; // 1ULL << (mantissa_bits - 2) + static constexpr std::uint64_t shifted_sign_mask = 0x8000000000000000U; // 1ULL << sign_shift + static constexpr std::uint64_t shifted_exponent_mask = 0x7FF0000000000000U; // exponent_mask << exponent_shift }; +#ifdef __SIZEOF_FLOAT128__ template <> struct floating_point_traits : floating_point_traits { }; +#else + template <> + struct floating_point_traits : floating_point_traits + { + }; +#endif template using float_bits_t = typename floating_point_traits::uint_type; diff --git a/include/ccmath/internal/type_traits/is_constant_evaluated.hpp b/include/ccmath/internal/support/is_constant_evaluated.hpp similarity index 88% rename from include/ccmath/internal/type_traits/is_constant_evaluated.hpp rename to include/ccmath/internal/support/is_constant_evaluated.hpp index ca3a5ca..9becd0c 100644 --- a/include/ccmath/internal/type_traits/is_constant_evaluated.hpp +++ b/include/ccmath/internal/support/is_constant_evaluated.hpp @@ -34,21 +34,17 @@ # define CCMATH_HAS_BUILTIN_IS_CONSTANT_EVALUATED #endif -namespace ccm::type_traits +namespace ccm::traits { - template - inline constexpr bool is_constant_evaluated(T x) noexcept + inline constexpr bool is_constant_evaluated() noexcept { #if defined(CCMATH_HAS_IS_CONSTANT_EVALUATED) return std::is_constant_evaluated(); #elif defined(CCMATH_HAS_BUILTIN_IS_CONSTANT_EVALUATED) return __builtin_is_constant_evaluated(); -#elif defined(__GNUC__) && (__GNUC__ >= 6) - return __builtin_constant_p(x); #else return false; #endif - } } // namespace ccm::type_traits diff --git a/include/ccmath/internal/support/limits.hpp b/include/ccmath/internal/support/limits.hpp new file mode 100644 index 0000000..9c8919e --- /dev/null +++ b/include/ccmath/internal/support/limits.hpp @@ -0,0 +1,71 @@ +/* + * 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 +#include + +// Light weight wrapper around std::numeric_limits for handling special types not handled by std::numeric_limits +// Still, prefer to use std::numeric_limits. Only use ccm::support::numeric_limits when you can't use std::numeric_limits. + +namespace ccm::support +{ + enum class float_round_style : std::int8_t + { + round_indeterminate = -1, + round_toward_zero = 0, + round_to_nearest = 1, + round_toward_infinity = 2, + round_toward_neg_infinity = 3 + }; + + template > + class numeric_limits + { + public: + using type = T; + + static constexpr const bool is_specialized = std::numeric_limits::is_specialized; + [[nodiscard]] static constexpr type min() noexcept { return std::numeric_limits::min(); } + [[nodiscard]] static constexpr type max() noexcept { return std::numeric_limits::max(); } + [[nodiscard]] static constexpr type lowest() noexcept { return std::numeric_limits::lowest(); } + + static constexpr const int digits = std::numeric_limits::digits; + static constexpr const int digits10 = std::numeric_limits::digits10; + static constexpr const int max_digits10 = std::numeric_limits::max_digits10; + static constexpr const bool is_signed = std::numeric_limits::is_signed; + static constexpr const bool is_integer = std::numeric_limits::is_integer; + static constexpr const bool is_exact = std::numeric_limits::is_exact; + static constexpr const int radix = std::numeric_limits::radix; + [[nodiscard]] static constexpr type epsilon() noexcept { return std::numeric_limits::epsilon(); } + [[nodiscard]] static constexpr type round_error() noexcept { return std::numeric_limits::round_error(); } + + static constexpr const int min_exponent = std::numeric_limits::min_exponent; + static constexpr const int min_exponent10 = std::numeric_limits::min_exponent10; + static constexpr const int max_exponent = std::numeric_limits::max_exponent; + static constexpr const int max_exponent10 = std::numeric_limits::max_exponent10; + + static constexpr const bool has_infinity = std::numeric_limits::has_infinity; + static constexpr const bool has_quiet_NaN = std::numeric_limits::has_quiet_NaN; + static constexpr const bool has_signaling_NaN = std::numeric_limits::has_signaling_NaN; + [[nodiscard]] static constexpr type infinity() noexcept { return std::numeric_limits::infinity(); } + [[nodiscard]] static constexpr type quiet_NaN() noexcept { return std::numeric_limits::quiet_NaN(); } + [[nodiscard]] static constexpr type signaling_NaN() noexcept { return std::numeric_limits::signaling_NaN(); } + [[nodiscard]] static constexpr type denorm_min() noexcept { return std::numeric_limits::denorm_min(); } + + static constexpr const bool is_iec559 = std::numeric_limits::is_iec559; + static constexpr const bool is_bounded = std::numeric_limits::is_bounded; + static constexpr const bool is_modulo = std::numeric_limits::is_modulo; + + static constexpr const bool traps = std::numeric_limits::traps; + static constexpr const bool tinyness_before = std::numeric_limits::tinyness_before; + static constexpr const float_round_style round_style = static_cast(std::numeric_limits::round_style); + }; +} // namespace ccm::support diff --git a/include/ccmath/internal/support/meta_compare.hpp b/include/ccmath/internal/support/meta_compare.hpp new file mode 100644 index 0000000..9856e51 --- /dev/null +++ b/include/ccmath/internal/support/meta_compare.hpp @@ -0,0 +1,58 @@ +/* + * 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::support +{ + /// @cond HIDDEN_SYMBOLS + namespace detail + { + // A variadic alias template that resolves to its first argument. + template + using first_t = T; + + // These are deliberately not defined. + template + auto or_fn(int) -> first_t...>; + + template + auto or_fn(...) -> std::true_type; + + template + auto and_fn(int) -> first_t...>; + + template + auto and_fn(...) -> std::false_type; + } // namespace detail + + template + struct or_ : decltype(detail::or_fn(0)) + { + }; + + template + struct and_ : decltype(detail::and_fn(0)) + { + }; + + template + struct not_ : std::bool_constant + { + }; + /// @endcond + + template + inline constexpr bool or_v = or_::value; + template + inline constexpr bool and_v = and_::value; + template + inline constexpr bool not_v = not_::value; +} // namespace ccm::support diff --git a/include/ccmath/internal/support/type_identity.hpp b/include/ccmath/internal/support/type_identity.hpp new file mode 100644 index 0000000..5375d14 --- /dev/null +++ b/include/ccmath/internal/support/type_identity.hpp @@ -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. + */ + +// C++20 std::type_identity support for C++17 + +#pragma once + +namespace ccm +{ + template + struct type_identity { using type = T; }; + + template + using type_identity_t = typename type_identity::type; +} + diff --git a/include/ccmath/internal/utility/unreachable.hpp b/include/ccmath/internal/support/unreachable.hpp similarity index 70% rename from include/ccmath/internal/utility/unreachable.hpp rename to include/ccmath/internal/support/unreachable.hpp index a695411..189267d 100644 --- a/include/ccmath/internal/utility/unreachable.hpp +++ b/include/ccmath/internal/support/unreachable.hpp @@ -9,25 +9,25 @@ #pragma once #if defined(__cpp_lib_unreachable) && __cpp_lib_unreachable >= 202202L -#include + #include #endif namespace ccm { [[noreturn]] inline void unreachable() { - // If we can't use the standard library's unreachable function + // If we can't use the standard library's unreachable function, // Then we'll use the compiler specific extensions if possible. #if defined(__cpp_lib_unreachable) && __cpp_lib_unreachable >= 202202L std::unreachable(); #elif defined(_MSC_VER) && !defined(__clang__) // MSVC __assume(false); -#elif defined(__GNUC__) || defined(__clang__) // GCC, Clang - __builtin_unreachable(); -#else // Fallback - // In the event that no compiler specific extension is available - // Then do nothing. - static_cast(0); +#elif defined(__GNUC__) || defined(__clang__) // GCC, Clang + __builtin_unreachable(); +#else // Fallback + // In the event that no compiler specific extension is available + // Then do nothing. + static_cast(0); #endif } diff --git a/include/ccmath/internal/types/big_int.hpp b/include/ccmath/internal/types/big_int.hpp new file mode 100644 index 0000000..39b4720 --- /dev/null +++ b/include/ccmath/internal/types/big_int.hpp @@ -0,0 +1,93 @@ +/* + * 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 +#include +#include +#include + +namespace ccm +{ + + template + struct BigInt + { + private: + static_assert(std::is_integral_v && std::is_unsigned_v, "WordType must be unsigned integer."); + + struct Division + { + BigInt quotient; + BigInt remainder; + }; + + public: + using word_type = WordType; + using unsigned_type = BigInt; + using signed_type = BigInt; + + static constexpr bool is_signed = Signed; + static constexpr std::size_t bits = Bits; + static constexpr std::size_t word_size = sizeof(word_type) * CHAR_BIT; + + static_assert(Bits > 0 && Bits % word_size == 0, "Number of bits in BigInt should be a multiple of word_size."); + + static constexpr std::size_t word_count = Bits / word_size; + + std::array data{}; + + constexpr BigInt() = default; + constexpr BigInt(const BigInt & other) = default; + + /** + * @brief Extend the internal representation of the big integer by filling remaining words with a specific value. + * @param index The starting index from where the extension should begin. + * @param is_negative Boolean flag indicating whether the big integer is negative. + * @return void + */ + constexpr void extend(std::size_t index, bool is_negative) + { + const word_type value = is_negative ? std::numeric_limits::max() : std::numeric_limits::min(); + for (size_t i = index; i < word_count; ++i) { data[i] = value; } + } + + template + constexpr BigInt(const BigInt & other) + { + if constexpr (OtherBits > Bits) // truncate bits + { + for (std::size_t i = 0; i < word_count; ++i) { data[i] = other.data[i]; } + } + else // Will be zero or sign extended + { + size_t i = 0; + for (; i < OtherBits / word_size; ++i) { data[i] = other.data[i]; } + extend(i, Signed && other.is_neg()); + } + } + + /** + * @brief Construct a BigInt from a C-style array of words. + * @tparam N The size of the array. + * @param nums The C-style array of words. + */ + template + constexpr BigInt(const WordType (&nums)[N]) // NOLINT + { + static_assert(N == word_count); + for (size_t i = 0; i < word_count; ++i) { data[i] = nums[i]; } + } + + constexpr explicit BigInt(const std::array & words) : data(words) {} + + + }; +} // namespace ccm diff --git a/include/ccmath/internal/types/float128.hpp b/include/ccmath/internal/types/float128.hpp new file mode 100644 index 0000000..5129deb --- /dev/null +++ b/include/ccmath/internal/types/float128.hpp @@ -0,0 +1,30 @@ +/* + * 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 + +// Check for the availability of C23's _Float128 type. +// Currently, _Float128 is available in GCC 7 or later when compiling in C mode. +// Clang does not define _Float128 and instead uses __float128, and only on x86-64 targets. +#if defined(__STDC_IEC_60559_BFP__) && !defined(__clang__) && !defined(__cplusplus) + #define CCM_HAS_FLOAT128 +typedef _Float128 float128; + +// If _Float128 is not available, check for __float128. +// Occasionally, __SIZEOF_FLOAT128__ is defined by gcc and clang to notify the +// availability of __float128. Clang also defines the macro __FLOAT128__ to +// identify the availability of __float128 type. As specified here: https://reviews.llvm.org/D15120 +#elif defined(__FLOAT128__) || defined(__SIZEOF_FLOAT128__) + #define CCM_HAS_FLOAT128 +using float128 = __float128; + +// If neither _Float128 nor __float128 are available, check if long double is 128 bits. +#elif (LDBL_MANT_DIG == 113) + #define CCM_HAS_FLOAT128 +using float128 = long double; +#endif diff --git a/include/ccmath/internal/helpers/floating_point_type.hpp b/include/ccmath/internal/types/fp_types.hpp similarity index 95% rename from include/ccmath/internal/helpers/floating_point_type.hpp rename to include/ccmath/internal/types/fp_types.hpp index 237b990..d15e985 100644 --- a/include/ccmath/internal/helpers/floating_point_type.hpp +++ b/include/ccmath/internal/types/fp_types.hpp @@ -36,6 +36,6 @@ namespace ccm typedef long double float_t; typedef long double double_t; # else - # error "Unknown __GLIBC_FLT_EVAL_METHOD" + # error "Unknown CCM_FLT_EVAL_METHOD" # endif } diff --git a/include/ccmath/internal/types/number_pair.hpp b/include/ccmath/internal/types/number_pair.hpp new file mode 100644 index 0000000..420c6b0 --- /dev/null +++ b/include/ccmath/internal/types/number_pair.hpp @@ -0,0 +1,32 @@ +/* + * 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 +{ + template + struct NumberPair + { + T hi; + T lo; + }; + + template + std::enable_if_t && std::is_unsigned_v, NumberPair> split(T x) + { + constexpr std::size_t half_bits_width = sizeof(T) * 4; + constexpr T Low_mask = (T{1} << half_bits_width) - T{1}; + NumberPair result; + result.lo = x & Low_mask; + result.hi = x >> half_bits_width; + return result; + } +} // namespace ccm diff --git a/include/ccmath/internal/types/sign.hpp b/include/ccmath/internal/types/sign.hpp new file mode 100644 index 0000000..2ae6836 --- /dev/null +++ b/include/ccmath/internal/types/sign.hpp @@ -0,0 +1,31 @@ +/* + * 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 + +// A helper type meant to interact with signed arithmetic types. +struct Sign +{ + [[nodiscard]] inline constexpr bool is_pos() const { return !is_negative; } + [[nodiscard]] inline constexpr bool is_neg() const { return is_negative; } + + inline friend constexpr bool operator==(Sign a, Sign b) { return a.is_negative == b.is_negative; } + + inline friend constexpr bool operator!=(Sign a, Sign b) { return !(a == b); } + + static const Sign positive; + static const Sign negative; + +private: + inline constexpr explicit Sign(bool is_negative) : is_negative(is_negative) {} + + bool is_negative; +}; + +inline constexpr Sign Sign::negative = Sign(true); +inline constexpr Sign Sign::positive = Sign(false); diff --git a/include/ccmath/internal/version.hpp b/include/ccmath/internal/version.hpp index b5fa401..569c75b 100644 --- a/include/ccmath/internal/version.hpp +++ b/include/ccmath/internal/version.hpp @@ -15,61 +15,36 @@ namespace ccm struct Version { public: - constexpr Version(int major, int minor, int patch) - : m_major(major), m_minor(minor), m_patch(patch) - { - } + constexpr Version(int major, int minor, int patch) : m_major(major), m_minor(minor), m_patch(patch) {} - std::string to_string() const - { - return std::to_string(m_major) + "." + std::to_string(m_minor) + "." + std::to_string(m_patch); - } + [[nodiscard]] std::string to_string() const { return std::to_string(m_major) + "." + std::to_string(m_minor) + "." + std::to_string(m_patch); } - constexpr int major() const { return m_major; } - constexpr int minor() const { return m_minor; } - constexpr int patch() const { return m_patch; } - constexpr int full() const { return m_major * 10000 + m_minor * 100 + m_patch; } - constexpr int get() const { return full(); } + [[nodiscard]] constexpr int major() const { return m_major; } + [[nodiscard]] constexpr int minor() const { return m_minor; } + [[nodiscard]] constexpr int patch() const { return m_patch; } + [[nodiscard]] constexpr int full() const { return m_major * 10000 + m_minor * 100 + m_patch; } + [[nodiscard]] constexpr int get() const { return full(); } - constexpr bool operator==(const Version& other) const - { - return m_major == other.m_major && m_minor == other.m_minor && m_patch == other.m_patch; - } + constexpr bool operator==(const Version & other) const { return m_major == other.m_major && m_minor == other.m_minor && m_patch == other.m_patch; } - constexpr bool operator!=(const Version& other) const - { - return !(*this == other); - } + constexpr bool operator!=(const Version & other) const { return !(*this == other); } - constexpr bool operator<(const Version& other) const - { - if (m_major < other.m_major) - return true; - if (m_major > other.m_major) - return false; + constexpr bool operator<(const Version & other) const + { + if (m_major < other.m_major) { return true; } + if (m_major > other.m_major) { return false; } - if (m_minor < other.m_minor) - return true; - if (m_minor > other.m_minor) - return false; + if (m_minor < other.m_minor) { return true; } + if (m_minor > other.m_minor) { return false; } - return m_patch < other.m_patch; - } + return m_patch < other.m_patch; + } - constexpr bool operator>(const Version& other) const - { - return other < *this; - } + constexpr bool operator>(const Version & other) const { return other < *this; } - constexpr bool operator<=(const Version& other) const - { - return !(other < *this); - } + constexpr bool operator<=(const Version & other) const { return !(other < *this); } - constexpr bool operator>=(const Version& other) const - { - return !(*this < other); - } + constexpr bool operator>=(const Version & other) const { return !(*this < other); } private: int m_major; @@ -77,4 +52,4 @@ namespace ccm int m_patch; }; -} +} // namespace ccm diff --git a/include/ccmath/math/basic/abs.hpp b/include/ccmath/math/basic/abs.hpp new file mode 100644 index 0000000..f862641 --- /dev/null +++ b/include/ccmath/math/basic/abs.hpp @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2024-Present Ian Pike + * Copyright (c) 2024-Present cmath contributors + * + * This library is provided under the MIT License. + * See LICENSE for more information. + */ + +#pragma once + +#include "ccmath/internal/predef/unlikely.hpp" +#include "ccmath/math/compare/isinf.hpp" +#include "ccmath/math/compare/isnan.hpp" +#include +#include + +namespace ccm +{ + /** + * @brief Computes the absolute value of a number. + * @tparam T Numeric type. + * @param x Floating-point or integer value. + * @return If successful, returns the absolute value of arg (|arg|). The value returned is exact and does not depend on any rounding modes. + */ + template && !std::is_unsigned_v, bool> = true> + constexpr T abs(T num) noexcept + { + // If num is NaN, return a quiet NaN. + if (CCM_UNLIKELY(ccm::isnan(num))) { return std::numeric_limits::quiet_NaN(); } + + // If num is equal to ±zero, return +zero. + if (static_cast(0) == num) { return static_cast(0); } + + // If num is less than zero, return -num, otherwise return num. + return num < 0 ? -num : num; + } + + /** + * @brief Computes the absolute value of a number. + * @tparam T Numeric type. + * @param x Floating-point or integer value. + * @return If successful, returns the absolute value of arg (|arg|). The value returned is exact and does not depend on any rounding modes. + */ + template && !std::is_unsigned_v, bool> = true> + constexpr T abs(T num) noexcept + { + // If num is less than zero, return -num, otherwise return num. + return num < 0 ? -num : num; + } + + /** + * @brief Computes the absolute value of a number. + * @tparam T Unsigned numeric type. + * @param x Floating-point or integer value. + * @return If successful, returns the absolute value of arg (|arg|). The value returned is exact and does not depend on any rounding modes. + */ + template , bool> = true> + constexpr T abs(T num) noexcept + { + // If abs is called with an argument of type X for which is_unsigned_v is true, and + // if X cannot be converted to int by integral promotion, the program is ill-formed. + // See: http://eel.is/c++draft/c.math.abs#3 + // See: ISO/IEC 9899:2018, 7.12.7.2, 7.22.6.1 + if constexpr (std::is_convertible_v) { return abs(static_cast(num)); } + 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."); + return T(0); + } + } + + /** + * @brief Computes the absolute value of a number. + * @tparam T Floating-point type. + * @param x Floating-point value. + * @return If successful, returns the absolute value of arg (|arg|). The value returned is exact and does not depend on any rounding modes. + */ + template , bool> = true> + constexpr T fabs(T num) noexcept + { + return abs(num); + } + + /** + * @brief Computes the absolute value of a number. + * @tparam Integer Integer type. + * @param x Integer value. + * @return If successful, returns the absolute value of arg (|arg|). The value returned is exact and does not depend on any rounding modes. + */ + template , bool> = true> + constexpr double fabs(Integer num) noexcept + { + return abs(static_cast(num)); + } + + /** + * @brief Computes the absolute value of a number. + * @param x Floating-point value. + * @return If successful, returns the absolute value of arg (|arg|). The value returned is exact and does not depend on any rounding modes. + */ + constexpr float fabsf(float num) noexcept + { + return abs(num); + } + + /** + * @brief Computes the absolute value of a number. + * @param x Floating-point value. + * @return If successful, returns the absolute value of arg (|arg|). The value returned is exact and does not depend on any rounding modes. + */ + constexpr long double fabsl(long double num) noexcept + { + return abs(num); + } + + /** + * @brief Computes the absolute value of a number. + * @param x Integer value. + * @return If successful, returns the absolute value of arg (|arg|). The value returned is exact and does not depend on any rounding modes. + */ + constexpr long labs(long num) noexcept + { + return abs(num); + } + + /** + * @brief Computes the absolute value of a number. + * @param x Integer value. + * @return If successful, returns the absolute value of arg (|arg|). The value returned is exact and does not depend on any rounding modes. + */ + constexpr long long llabs(long long num) noexcept + { + return abs(num); + } +} // namespace ccm + +/// @ingroup basic diff --git a/include/ccmath/detail/basic/fdim.hpp b/include/ccmath/math/basic/fdim.hpp similarity index 72% rename from include/ccmath/detail/basic/fdim.hpp rename to include/ccmath/math/basic/fdim.hpp index 79af765..97346dd 100644 --- a/include/ccmath/detail/basic/fdim.hpp +++ b/include/ccmath/math/basic/fdim.hpp @@ -8,9 +8,10 @@ #pragma once +#include "ccmath/internal/predef/unlikely.hpp" +#include "ccmath/math/compare/isnan.hpp" #include #include -#include "ccmath/detail/compare/isnan.hpp" namespace ccm { @@ -21,14 +22,14 @@ namespace ccm * @param y A floating-point or integer values * @return If successful, returns the positive difference between x and y. */ - template ::value, int> = 0> - inline constexpr T fdim(T x, T y) + template , int> = 0> + constexpr T fdim(T x, T y) { - if (ccm::isnan(x)) { return x; } - if (ccm::isnan(y)) { return y; } - if (x <= y) { return T(+0.0); } - else if ((y < T(0.0)) && (x > (std::numeric_limits::max() + y))) { return std::numeric_limits::infinity(); } - else { return x - y; } + if (CCM_UNLIKELY(ccm::isnan(x))) { return x; } + if (CCM_UNLIKELY(ccm::isnan(y))) { return y; } + if (x <= y) { return static_cast(+0.0); } + if ((y < static_cast(0.0)) && (x > (std::numeric_limits::max() + y))) { return std::numeric_limits::infinity(); } + return x - y; } /** @@ -39,8 +40,8 @@ namespace ccm * @param y A floating-point value. * @return If successful, returns the positive difference between x and y. */ - template ::value && std::is_floating_point::value, int> = 0> - inline constexpr auto fdim(T x, U y) + template && std::is_floating_point_v, int> = 0> + constexpr auto fdim(T x, U y) { // Find the common type of the two arguments using shared_type = std::common_type_t; @@ -56,8 +57,8 @@ namespace ccm * @param y An integral value. * @return If successful, returns the positive difference between x and y. */ - template ::value, int> = 0> - inline constexpr double fdim(Integer x, Integer y) + template , int> = 0> + constexpr double fdim(Integer x, Integer y) { return fdim(static_cast(x), static_cast(y)); } @@ -68,7 +69,7 @@ namespace ccm * @param y A floating-point value. * @return If successful, returns the positive difference between x and y. */ - inline constexpr float fdimf(float x, float y) + constexpr float fdimf(float x, float y) { return fdim(x, y); } @@ -79,7 +80,7 @@ namespace ccm * @param y A floating-point value. * @return If successful, returns the positive difference between x and y. */ - inline constexpr long double fdiml(long double x, long double y) + constexpr long double fdiml(long double x, long double y) { return fdim(x, y); } diff --git a/include/ccmath/math/basic/fma.hpp b/include/ccmath/math/basic/fma.hpp new file mode 100644 index 0000000..4c025f8 --- /dev/null +++ b/include/ccmath/math/basic/fma.hpp @@ -0,0 +1,129 @@ +/* + * 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/internal/predef/unlikely.hpp" +#include "ccmath/math/compare/isinf.hpp" +#include "ccmath/math/compare/isnan.hpp" +#include + +namespace ccm +{ + /** + * @brief Fused multiply-add operation. + * @tparam T Numeric type. + * @param x Floating-point or integer value. + * @param y Floating-point or integer value. + * @param z Floating-point or integer value. + * @return If successful, returns the value of x * y + z as if calculated to infinite precision and rounded once to fit the result type (or, alternatively, + * calculated as a single ternary floating-point operation). + */ + template , bool> = true> + constexpr T fma(T x, T y, T z) noexcept + { + // Handle infinity + if (CCM_UNLIKELY((x == static_cast(0) && ccm::isinf(y)) || (y == T{0} && ccm::isinf(x)))) { return std::numeric_limits::quiet_NaN(); } + if (CCM_UNLIKELY(x * y == std::numeric_limits::infinity() && z == -std::numeric_limits::infinity())) + { + return std::numeric_limits::infinity(); + } + + // Handle NaN + if (CCM_UNLIKELY(ccm::isnan(x) || ccm::isnan(y))) { return std::numeric_limits::quiet_NaN(); } + if (CCM_UNLIKELY(ccm::isnan(z) && (x * y != 0 * std::numeric_limits::infinity() || x * y != std::numeric_limits::infinity() * 0))) + { + return std::numeric_limits::quiet_NaN(); + } + + // We have to hope the compiler optimizes this. Currently there is no builtin fma that works with static_assert. + return (x * y) + z; + } + + template , bool> = true> + constexpr T fma(T x, T y, T z) noexcept + { + return (x * y) + z; + } + + /** + * @brief Fused multiply-add operation. + * @tparam T Floating-point or integer type converted to a common type. + * @tparam U Floating-point or integer type converted to a common type. + * @tparam V Floating-point or integer type converted to a common type. + * @param x Floating-point or integer value converted to a common type. + * @param y Floating-point or integer value converted to a common type. + * @param z Floating-point or integer value converted to a common type. + * @return If successful, returns the value of x * y + z as if calculated to infinite precision and rounded once to fit the result type (or, alternatively, + * calculated as a single ternary floating-point operation). + */ + template + constexpr auto fma(T x, U y, V z) noexcept + { + // If our type is an integer epsilon will be set to 0 by default. + // Instead, set epsilon to 1 so that our type is always at least the widest floating point type. + constexpr auto TCommon = std::numeric_limits::epsilon() > 0 ? std::numeric_limits::epsilon() : 1; + constexpr auto UCommon = std::numeric_limits::epsilon() > 0 ? std::numeric_limits::epsilon() : 1; + constexpr auto VCommon = std::numeric_limits::epsilon() > 0 ? std::numeric_limits::epsilon() : 1; + + using epsilon_type = std::common_type_t; + + using shared_type = std::conditional_t< + TCommon <= std::numeric_limits::epsilon() && TCommon <= UCommon, T, + std::conditional_t::epsilon() && UCommon <= TCommon, U, + std::conditional_t::epsilon() && VCommon <= UCommon, V, epsilon_type>>>; + + return ccm::fma(static_cast(x), static_cast(y), static_cast(z)); + } + + /** + * @brief Fused multiply-add operation. + * @tparam T Integer type converted to a common type. + * @tparam U Integer type converted to a common type. + * @tparam V Integer type converted to a common type. + * @param x Integer value converted to a common type. + * @param y Integer value converted to a common type. + * @param z Integer value converted to a common type. + * @return If successful, returns the value of x * y + z as if calculated to infinite precision and rounded once to fit the result type (or, alternatively, + * calculated as a single ternary floating-point operation). + */ + template && std::is_integral_v && std::is_integral_v, bool> = true> + constexpr auto fma(T x, U y, V z) noexcept // Special case for if all types are integers. + { + using shared_type = std::common_type_t; + return ccm::fma(static_cast(x), static_cast(y), static_cast(z)); + } + + /** + * @brief Fused multiply-add operation. + * @param x Floating-point value. + * @param y Floating-point value. + * @param z Floating-point value. + * @return If successful, returns the value of x * y + z as if calculated to infinite precision and rounded once to fit the result type (or, alternatively, + * calculated as a single ternary floating-point operation). + */ + constexpr float fmaf(float x, float y, float z) noexcept + { + return ccm::fma(x, y, z); + } + + /** + * @brief Fused multiply-add operation. + * @param x Floating-point value. + * @param y Floating-point value. + * @param z Floating-point value. + * @return If successful, returns the value of x * y + z as if calculated to infinite precision and rounded once to fit the result type (or, alternatively, + * calculated as a single ternary floating-point operation). + */ + constexpr long double fmal(long double x, long double y, long double z) noexcept + { + return ccm::fma(x, y, z); + } +} // namespace ccm + +/// @ingroup basic diff --git a/include/ccmath/math/basic/fmod.hpp b/include/ccmath/math/basic/fmod.hpp new file mode 100644 index 0000000..c695050 --- /dev/null +++ b/include/ccmath/math/basic/fmod.hpp @@ -0,0 +1,135 @@ +/* + * 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/internal/predef/unlikely.hpp" +#include "ccmath/math/compare/isfinite.hpp" +#include "ccmath/math/compare/isinf.hpp" +#include "ccmath/math/compare/isnan.hpp" +#include "ccmath/math/compare/signbit.hpp" +#include "ccmath/math/nearest/trunc.hpp" +#include + +namespace ccm +{ + /// @cond MATH_DETAIL + namespace internal::impl + { + template + constexpr T fmod_impl_check(T x, T y) noexcept + { + // Special edge cases for floating-point types. + if constexpr (std::numeric_limits::is_iec559) + { + // If x is ±0 and y is not zero, ±0 is returned. + if (x == static_cast(0.0) && (y != static_cast(0.0))) + { + // The standard specifies that plus or minus 0 is returned depending on the sign of x. + if (ccm::signbit(x)) { return -static_cast(0.0); } + return static_cast(0.0); + } + + // If x is ±∞ and y is not NaN OR if y is ±0 and x is not NaN, -NaN is returned + if (CCM_UNLIKELY(((ccm::isinf(x) && !ccm::isnan(y)) || (y == static_cast(0.0) && !ccm::isnan(x))))) + { + // For some reason, all the major compilers return a negative NaN even though I can't find anywhere + // in the standard that specifies this. I'm going to follow suit and return a negative NaN for now. + // Overall, this has little effect on checking for NaN. We only really care for conformance with the standard. + return -std::numeric_limits::quiet_NaN(); + } + + // If y is ±∞ and x is finite, x is returned. + if (CCM_UNLIKELY(ccm::isinf(y) && ccm::isfinite(x))) { return x; } + + // If either argument is NaN, NaN is returned. + if (CCM_UNLIKELY(ccm::isnan(x) || ccm::isnan(y))) + { + // Same problem as before, but this time all major compilers return a positive NaN. + return std::numeric_limits::quiet_NaN(); + } + } + + // Calculate the remainder of the division of x by y. + // static_cast is required to prevent the compiler from complaining about narrowing with integer types. + return static_cast(x - (ccm::trunc(x / y) * y)); + } + + template > + constexpr TC fmod_impl_type_check(T x, U y) noexcept + { + return fmod_impl_check(static_cast(x), static_cast(y)); + } + } // namespace internal::impl + /// @endcond + + /** + * @brief Returns the floating-point remainder of the division operation x/y. + * @note Some edge cases where NaN is returned are different from std::fmod due to the standard allowing implementation based returns. + * @tparam T A floating-point type. + * @param x A floating-point value. + * @param y A floating-point value. + * @return The floating-point remainder of the division operation x/y. + */ + template , int> = 0> + constexpr Real fmod(Real x, Real y) + { + return internal::impl::fmod_impl_check(x, y); + } + + /** + * @brief Returns the floating-point remainder of the division operation x/y. + * @tparam Integer An integral type. + * @param x An integral value. + * @param y An integral value. + * @return The floating-point remainder of the division operation x/y. + */ + template , int> = 0> + constexpr double fmod(Integer x, Integer y) + { + return internal::impl::fmod_impl_type_check(x, y); + } + + /** + * @brief Returns the floating-point remainder of the division operation x/y. + * @tparam T A floating-point or integral type. + * @tparam U A floating-point or integral type. + * @param x A floating-point or integral value. + * @param y A floating-point or integral value. + * @return The floating-point remainder of the division operation x/y. + */ + template + constexpr auto fmod(T x, T y) + { + return internal::impl::fmod_impl_type_check(x, y); + } + + /** + * @brief Returns the floating-point remainder of the division operation x/y. + * @param x A floating-point value. + * @param y A floating-point value. + * @return The floating-point remainder of the division operation x/y. + */ + constexpr float fmodf(float x, float y) + { + return fmod(x, y); + } + + /** + * @brief Returns the floating-point remainder of the division operation x/y. + * @param x A floating-point value. + * @param y A floating-point value. + * @return The floating-point remainder of the division operation x/y. + */ + constexpr long double fmodl(long double x, long double y) + { + return fmod(x, y); + } +} // namespace ccm + +/// @ingroup basic diff --git a/include/ccmath/math/basic/impl/nan_double_impl.hpp b/include/ccmath/math/basic/impl/nan_double_impl.hpp new file mode 100644 index 0000000..dd52064 --- /dev/null +++ b/include/ccmath/math/basic/impl/nan_double_impl.hpp @@ -0,0 +1,103 @@ +/* + * 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/internal/helpers/digit_to_int.hpp" +#include "ccmath/internal/support/bits.hpp" +#include +#include + +namespace ccm::internal::impl +{ + + constexpr double nan_double_impl(const char * arg) noexcept + { + if constexpr (!std::numeric_limits::is_iec559) + { + return 0.0; + } + +#if defined(_MSC_VER) && !defined(__clang__) + // Currently, MSVC always returns a Quiet NaN no matter if a payload is + // provided or not. This is different from GCC and Clang which do allow payloads to be set. + // So if we detect we are using MSVC without Clang-CL then + // we can just return NaN and not bother doing any extra work. + // To properly mimic the behavior of MSVC. + return std::numeric_limits::quiet_NaN(); // Default NaN +#endif + + std::uint64_t dbl_bits{0}; + bool has_hex_been_detected{false}; + + if (arg == nullptr) + { + return std::numeric_limits::quiet_NaN(); // Default NaN + } + + // NOLINTBEGIN + if (arg[0] == '\0') // + { + return std::numeric_limits::quiet_NaN(); // Default NaN + } + + // Check for a hex prefix and if its detected, skip the prefix and set the flag. + if (arg[0] == '0' && (arg[1] == 'x' || arg[1] == 'X')) + { + arg += 2; + has_hex_been_detected = true; + } + + if (!has_hex_been_detected) + { + // Check that all of are characters are numbers. If we detect a non-number, return the default NaN. + for (std::size_t i = 0; arg[i] != '\0'; ++i) // NOLINT + { + if (arg[i] < '0' || arg[i] > '9') + { + return std::numeric_limits::quiet_NaN(); // Default NaN + } + } + } + + if (has_hex_been_detected) + { + // Calculate tag_value by handling wrapping for numbers larger than 8 digits + for (std::size_t i = 0; arg[i] != '\0'; ++i) + { + dbl_bits *= 16; + dbl_bits += static_cast(ccm::helpers::digit_to_int(arg[i])); // Convert ASCII to numeric value + if (i >= 15) + { + dbl_bits %= static_cast(1e18); // Wrap around for numbers larger than 8 digits + } + } + } + else + { + // Calculate tag_value by handling wrapping for numbers larger than 8 digits + for (std::size_t i = 0; arg[i] != '\0'; ++i) + { + dbl_bits *= 10; + dbl_bits += static_cast(arg[i] - '0'); // Convert ASCII to numeric value + if (i >= 15) + { + dbl_bits %= static_cast(1e18); // Wrap around for numbers larger than 8 digits + } + } + } + // NOLINTEND + + // Set the tag bits for NaN + // dbl_bits |= UINT64_C(0x7FF8000000000000); + dbl_bits |= ccm::support::bit_cast(std::numeric_limits::quiet_NaN()); + + // Convert the uint64_t tag into a double NaN + return ccm::support::bit_cast(dbl_bits); + } +} // namespace ccm::internal::impl diff --git a/include/ccmath/math/basic/impl/nan_float_impl.hpp b/include/ccmath/math/basic/impl/nan_float_impl.hpp new file mode 100644 index 0000000..de943e5 --- /dev/null +++ b/include/ccmath/math/basic/impl/nan_float_impl.hpp @@ -0,0 +1,95 @@ +/* + * 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/internal/helpers/digit_to_int.hpp" +#include "ccmath/internal/support/bits.hpp" + +#include +#include + +namespace ccm::internal::impl +{ + constexpr float nan_float_impl(const char * arg) noexcept + { + if constexpr (!std::numeric_limits::is_iec559) + { + return 0.0; + } + +#if defined(_MSC_VER) && !defined(__clang__) + // Currently, MSVC always returns a Quiet NaN no matter if a payload is + // provided or not. This is different from GCC and Clang which do allow payloads to be set. + // So if we detect we are using MSVC without Clang-CL then + // we can just return NaN and not bother doing any extra work. + // To properly mimic the behavior of MSVC. + return std::numeric_limits::quiet_NaN(); // Default NaN +#endif + + std::uint32_t flt_bits{0}; + bool has_hex_been_detected{false}; + + // NOLINTBEGIN + + // Check for a hex prefix and if its detected, skip the prefix and set the flag. + if (arg[0] == '0' && (arg[1] == 'x' || arg[1] == 'X')) + { + arg += 2; + has_hex_been_detected = true; + } + + if (!has_hex_been_detected) + { + // Check that all of are characters are numbers. If we detect a non-number, return the default NaN. + for (std::size_t i = 0; arg[i] != '\0'; ++i) // NOLINT + { + if (arg[i] < '0' || arg[i] > '9') + { + return std::numeric_limits::quiet_NaN(); // Default NaN + } + } + } + + if (has_hex_been_detected) + { + // Calculate tag_value by handling wrapping for numbers larger than 8 digits + for (std::size_t i = 0; arg[i] != '\0'; ++i) + { + flt_bits *= 16; + flt_bits += static_cast(ccm::helpers::digit_to_int(arg[i])); // Convert ASCII to numeric value + if (i >= 7) + { + flt_bits %= static_cast(1e8); // Wrap around for numbers larger than 8 digits + } + } + } + else + { + // Calculate tag_value by handling wrapping for numbers larger than 8 digits + for (std::size_t i = 0; arg[i] != '\0'; ++i) + { + flt_bits *= 10; + flt_bits += static_cast(ccm::helpers::digit_to_int(arg[i])); // Convert ASCII to numeric value + if (i >= 7) + { + flt_bits %= static_cast(1e8); // Wrap around for numbers larger than 8 digits + } + } + } + + // NOLINTEND + + // Set the tag bits for NaN + // flt_bits |= UINT32_C(0x7F800000); + flt_bits |= ccm::support::bit_cast(std::numeric_limits::quiet_NaN()); + + // Convert the bits to a float + return ccm::support::bit_cast(flt_bits); + } +} // namespace ccm::internal::impl diff --git a/include/ccmath/math/basic/impl/remquo_double_impl.hpp b/include/ccmath/math/basic/impl/remquo_double_impl.hpp new file mode 100644 index 0000000..1ef7779 --- /dev/null +++ b/include/ccmath/math/basic/impl/remquo_double_impl.hpp @@ -0,0 +1,137 @@ +/* + * 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 + +#include "ccmath/internal/predef/unlikely.hpp" +#include "ccmath/internal/support/bits.hpp" +#include "ccmath/math/basic/abs.hpp" +#include "ccmath/math/basic/fmod.hpp" + +#include "ccmath/internal/predef/compiler_warnings_and_errors.hpp" + +namespace ccm::internal +{ + namespace impl + { + // NOLINTNEXTLINE(readability-function-cognitive-complexity) + inline constexpr double remquo_double_impl(double x, double y, int * quo) noexcept + { + std::int64_t x_i64{}; + std::int64_t y_i64{}; + std::uint64_t x_sign{}; + std::uint64_t quotient_sign{}; + int computed_quotient{}; + + x_i64 = ccm::support::double_to_int64(x); + y_i64 = ccm::support::double_to_int64(y); + + // Determine the signs of x and the quotient. + x_sign = static_cast(x_i64) & 0x8000000000000000ULL; + quotient_sign = x_sign ^ (static_cast(y_i64) & 0x8000000000000000ULL); + + // Clear the sign bits from the int64_t representations of x and y. + x_i64 &= 0x7fffffffffffffffULL; + y_i64 &= 0x7fffffffffffffffULL; + + // If y is zero. + if (CCM_UNLIKELY(y_i64 == 0)) { return (x * y) / (x * y); } + + // GCC and Clang do not like comparing signed and unsigned integers. + // The outcome of these comparisons is well-defined, so we can safely disable these warnings. + // clang-format off + CCM_DISABLE_GCC_WARNING(-Wsign-compare) + CCM_DISABLE_CLANG_WARNING(-Wsign-compare) + // clang-format on + + // If x is not finite or y is NaN. + if (CCM_UNLIKELY(x_i64 >= 0x7ff0000000000000ULL || y_i64 > 0x7ff0000000000000ULL)) + { + return (x * y) / (x * y); + } // NOLINT(readability-simplify-boolean-expr) + + // b (or bit 54) represents the highest bit we can compare against for both signed and unsigned integers without causing an overflow. + // Here we are checking that if y_i64 is within the range of signed 64-bit integers that can be represented without setting the MSB (i.e., + // positive or zero). + if (y_i64 <= 0x7fbfffffffffffffULL) + { + x = ccm::fmod(x, 8 * y); // now x < (8 * y) + } + + if (CCM_UNLIKELY(x_i64 == y_i64)) + { + *quo = quotient_sign != 0U ? -1 : 1; + return 0.0 * x; + } + + x = ccm::fabs(x); + y = ccm::support::int64_to_double(y_i64); + computed_quotient = 0; + + if (y_i64 <= 0x7fcfffffffffffffULL && x >= 4 * y) + { + x -= 4 * y; + computed_quotient += 4; + } + + if (y_i64 <= 0x7fdfffffffffffffULL && x >= 2 * y) + { + x -= 2 * y; + computed_quotient += 2; + } + + if (y_i64 < 0x0020000000000000ULL) + { + if (x + x > y) + { + x -= y; + ++computed_quotient; + if (x + x >= y) + { + x -= y; + ++computed_quotient; + } + } + } + else + { + double y_half = 0.5 * y; + if (x > y_half) + { + x -= y; + ++computed_quotient; + if (x >= y_half) + { + x -= y; + ++computed_quotient; + } + } + } + + CCM_RESTORE_GCC_WARNING() + CCM_RESTORE_CLANG_WARNING() + + *quo = quotient_sign != 0U ? -computed_quotient : computed_quotient; + + // Make sure that the correct sign of zero results in round down mode. + if (x == 0.0) { x = 0.0; } + if (x_sign != 0U) { x = -x; } + + return x; + } + } // namespace impl + + template >> + constexpr T remquo_double(T x, T y, int * quo) noexcept + { + return static_cast(impl::remquo_double_impl(x, y, quo)); + } +} // namespace ccm::internal diff --git a/include/ccmath/math/basic/impl/remquo_float_impl.hpp b/include/ccmath/math/basic/impl/remquo_float_impl.hpp new file mode 100644 index 0000000..695509b --- /dev/null +++ b/include/ccmath/math/basic/impl/remquo_float_impl.hpp @@ -0,0 +1,116 @@ +/* + * 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 + +#include "ccmath/internal/predef/unlikely.hpp" +#include "ccmath/internal/support/bits.hpp" +#include "ccmath/math/basic/abs.hpp" +#include "ccmath/math/basic/fmod.hpp" + +namespace ccm::internal +{ + namespace impl + { + // NOLINTNEXTLINE(readability-function-cognitive-complexity) + inline constexpr float remquo_float_impl(float x, float y, int * quo) noexcept + { + std::int32_t x_i32{}; + std::int32_t y_i32{}; + std::uint32_t x_sign{}; + int quotient_sign{}; + int computed_quotient{}; + + x_i32 = ccm::support::float_to_int32(x); + y_i32 = ccm::support::float_to_int32(y); + + // Determine the signs of x and the quotient. + x_sign = static_cast(x_i32) & 0x80000000; + quotient_sign = static_cast(x_sign ^ (static_cast(y_i32) & 0x80000000)); + + // Clear the sign bits from the int32_t representations of x and y. + x_i32 &= 0x7fffffff; + y_i32 &= 0x7fffffff; + + // If y is zero. + if (CCM_UNLIKELY(y_i32 == 0)) { return (x * y) / (x * y); } + + // If x is not finite or y is NaN. + if (CCM_UNLIKELY(x_i32 >= 0x7f800000 || y_i32 > 0x7f800000)) { return (x * y) / (x * y); } // NOLINT(readability-simplify-boolean-expr) + + if (y_i32 <= 0x7dffffff) + { + x = ccm::fmod(x, 8 * y); // now x < (8 * y) + } + + if ((x_i32 - y_i32) == 0) + { + *quo = quotient_sign != 0 ? -1 : 1; + return 0.0F * x; + } + + x = ccm::fabsf(x); + y = ccm::fabsf(y); + computed_quotient = 0; + + if (y_i32 <= 0x7e7fffff && x >= 4 * y) + { + x -= 4 * y; + computed_quotient += 4; + } + if (y_i32 <= 0x7effffff && x >= 2 * y) + { + x -= 2 * y; + computed_quotient += 2; + } + if (y_i32 < 0x01000000) + { + if (x + x > y) + { + x -= y; + ++computed_quotient; + if (x + x >= y) + { + x -= y; + ++computed_quotient; + } + } + } + else + { + float y_half = 0.5F * y; + if (x > y_half) + { + x -= y; + ++computed_quotient; + if (x >= y_half) + { + x -= y; + ++computed_quotient; + } + } + } + *quo = quotient_sign != 0 ? -computed_quotient : computed_quotient; + + // Make sure that the correct sign of zero results in round down mode. + if (x == 0.0F) { x = 0.0F; } + if (x_sign != 0U) { x = -x; } + + return x; + } + } // namespace impl + + template >> + inline constexpr T remquo_float(T x, T y, int * quo) noexcept + { + return static_cast(impl::remquo_float_impl(x, y, quo)); + } +} // namespace ccm::internal diff --git a/include/ccmath/math/basic/impl/remquo_ldouble_impl.hpp b/include/ccmath/math/basic/impl/remquo_ldouble_impl.hpp new file mode 100644 index 0000000..1b9207f --- /dev/null +++ b/include/ccmath/math/basic/impl/remquo_ldouble_impl.hpp @@ -0,0 +1,54 @@ +/* + * 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 + +#include "ccmath/internal/predef/unlikely.hpp" +#include "ccmath/internal/support/bits.hpp" +#include "ccmath/math/basic/abs.hpp" +#include "ccmath/math/basic/fmod.hpp" + +namespace ccm::internal +{ + namespace + { + namespace impl + { + inline constexpr long double remquo_ldouble_impl(long double x, long double y, int * quo) noexcept + { + std::int32_t x_exponent{}; + std::int32_t y_exponent{}; + std::int32_t x_int32{}; + std::int32_t y_int32{}; + + std::uint32_t x_sign{}; + std::uint32_t y_msb{}; + std::uint32_t x_lsb{}; + + int quotient_sign{}; + int computed_quotient{}; + + // TODO: For the time being this is mega on hold until we have access to + // necessary implementations of int128_t. Without them extracting the + // needed bits to perform the necessary operations would be extremely challenging + // due to the fact that long double is extremely platform dependent. + + return 0; + } + } // namespace impl + } // namespace + + template >> + inline constexpr T remquo_ldouble(T x, T y, int * quo) noexcept + { + return static_cast(impl::remquo_ldouble_impl(x, y, quo)); + } +} // namespace ccm::internal diff --git a/include/ccmath/detail/basic/max.hpp b/include/ccmath/math/basic/max.hpp similarity index 82% rename from include/ccmath/detail/basic/max.hpp rename to include/ccmath/math/basic/max.hpp index 220c376..87a8ea6 100644 --- a/include/ccmath/detail/basic/max.hpp +++ b/include/ccmath/math/basic/max.hpp @@ -8,11 +8,11 @@ #pragma once +#include "ccmath/internal/predef/unlikely.hpp" +#include "ccmath/math/compare/isnan.hpp" #include #include -#include "ccmath/detail/compare/isnan.hpp" - namespace ccm { /** @@ -23,15 +23,15 @@ namespace ccm * @return If successful, returns the larger of two floating point values. The value returned is exact and does not depend on any rounding modes. */ template - inline constexpr T max(T x, T y) noexcept + constexpr T max(T x, T y) noexcept { - if constexpr (std::is_floating_point::value) + if constexpr (std::is_floating_point_v) { - if (ccm::isnan(x) && ccm::isnan(y)) { return std::numeric_limits::quiet_NaN(); } + if (CCM_UNLIKELY(ccm::isnan(x) && ccm::isnan(y))) { return std::numeric_limits::quiet_NaN(); } - if (ccm::isnan(x)) { return y; } + if (CCM_UNLIKELY(ccm::isnan(x))) { return y; } - if (ccm::isnan(y)) { return x; } + if (CCM_UNLIKELY(ccm::isnan(y))) { return x; } } return (x > y) ? x : y; @@ -46,7 +46,7 @@ namespace ccm * @return */ template - inline constexpr auto max(T x, U y) noexcept + constexpr auto max(T x, U y) noexcept { // Find the common type of the two arguments using shared_type = std::common_type_t; @@ -63,7 +63,7 @@ namespace ccm * @return If successful, returns the larger of two floating point values. The value returned is exact and does not depend on any rounding modes. */ template - inline constexpr T fmax(T x, T y) noexcept + constexpr T fmax(T x, T y) noexcept { return max(x, y); } @@ -77,7 +77,7 @@ namespace ccm * @return If successful, returns the larger of two floating point values. The value returned is exact and does not depend on any rounding modes. */ template - inline constexpr auto fmax(T x, U y) noexcept + constexpr auto fmax(T x, U y) noexcept { // Find the common type of the two arguments using shared_type = std::common_type_t; @@ -92,8 +92,8 @@ namespace ccm * @param y Right-hand side of the comparison. * @return If successful, returns the larger of two floating point values. The value returned is exact and does not depend on any rounding modes. */ - template ::value, int> = 0> - inline constexpr double fmax(Integer x, Integer y) noexcept + template , int> = 0> + constexpr double fmax(Integer x, Integer y) noexcept { return max(static_cast(x), static_cast(y)); } @@ -104,7 +104,7 @@ namespace ccm * @param y Right-hand side of the comparison. * @return If successful, returns the larger of two floating point values. The value returned is exact and does not depend on any rounding modes. */ - inline constexpr float fmaxf(float x, float y) noexcept + constexpr float fmaxf(float x, float y) noexcept { return fmax(x, y); } @@ -115,7 +115,7 @@ namespace ccm * @param y Right-hand side of the comparison. * @return If successful, returns the larger of two floating point values. The value returned is exact and does not depend on any rounding modes. */ - inline constexpr long double fmaxl(long double x, long double y) noexcept + constexpr long double fmaxl(long double x, long double y) noexcept { return fmax(x, y); } diff --git a/include/ccmath/detail/basic/min.hpp b/include/ccmath/math/basic/min.hpp similarity index 85% rename from include/ccmath/detail/basic/min.hpp rename to include/ccmath/math/basic/min.hpp index 6809219..61a804c 100644 --- a/include/ccmath/detail/basic/min.hpp +++ b/include/ccmath/math/basic/min.hpp @@ -8,8 +8,9 @@ #pragma once +#include "ccmath/internal/predef/unlikely.hpp" +#include "ccmath/math/compare/isnan.hpp" #include -#include "ccmath/detail/compare/isnan.hpp" namespace ccm { @@ -21,13 +22,13 @@ namespace ccm * @return If successful, returns the smaller of two floating point values. The value returned is exact and does not depend on any rounding modes. */ template - inline constexpr T min(const T x, const T y) noexcept + constexpr T min(const T x, const T y) noexcept { // If we are comparing floating point numbers, we need to check for NaN if constexpr (std::is_floating_point_v) { - if (ccm::isnan(x)) { return y; } - else if (ccm::isnan(y)) { return x; } + if (CCM_UNLIKELY(ccm::isnan(x))) { return y; } + if (CCM_UNLIKELY(ccm::isnan(y))) { return x; } } return (x < y) ? x : y; @@ -36,13 +37,13 @@ namespace ccm /** * @brief Computes the smaller of the two values. * @tparam T Left-hand type of the left-hand value to compare. - * @tparam U Right-hand type of the right-hand value to compare. + * @tparam U Right-hand type of the right-hand value to compare. * @param x Left-hand side of the comparison. * @param y Right-hand side of the comparison. * @return If successful, returns the smaller of two floating point values. The value returned is exact and does not depend on any rounding modes. */ template - inline constexpr T min(const T x, const U y) noexcept + constexpr T min(const T x, const U y) noexcept { // Find the common type of the two arguments using shared_type = std::common_type_t; @@ -59,7 +60,7 @@ namespace ccm * @return If successful, returns the smaller of two floating point values. The value returned is exact and does not depend on any rounding modes. */ template , bool> = true> - inline constexpr Real fmin(const Real x, const Real y) noexcept + constexpr Real fmin(const Real x, const Real y) noexcept { return min(x, y); } @@ -73,7 +74,7 @@ namespace ccm * @return If successful, returns the smaller of two floating point values. The value returned is exact and does not depend on any rounding modes. */ template - inline constexpr auto fmin(const T x, const U y) noexcept + constexpr auto fmin(const T x, const U y) noexcept { // Find the common type of the two arguments using shared_type = std::common_type_t; @@ -90,7 +91,7 @@ namespace ccm * @return If successful, returns the smaller of two floating point values. The value returned is exact and does not depend on any rounding modes. */ template , bool> = true> - inline constexpr Integer fmin(const Integer x, const Integer y) noexcept + constexpr Integer fmin(const Integer x, const Integer y) noexcept { return min(x, y); } diff --git a/include/ccmath/math/basic/nan.hpp b/include/ccmath/math/basic/nan.hpp new file mode 100644 index 0000000..51a9efa --- /dev/null +++ b/include/ccmath/math/basic/nan.hpp @@ -0,0 +1,53 @@ +/* + * 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/math/basic/impl/nan_double_impl.hpp" +#include "ccmath/math/basic/impl/nan_float_impl.hpp" + +namespace ccm +{ + /** + * @brief not-a-number (NaN) function + * @param arg Narrow character string identifying the contents of a NaN + * @return The quiet NaN value that corresponds to the identifying string arg or zero if the implementation does not support quiet NaNs. + */ + constexpr double nan(const char * arg) noexcept + { + return ccm::internal::impl::nan_double_impl(arg); + } + + /** + * @brief not-a-number (NaN) function + * @param arg Narrow character string identifying the contents of a NaN + * @return The quiet NaN value that corresponds to the identifying string arg or zero if the implementation does not support quiet NaNs. + */ + constexpr float nanf(const char * arg) noexcept + { + return ccm::internal::impl::nan_float_impl(arg); + } + + /** + * @brief not-a-number (NaN) function + * @param arg Narrow character string identifying the contents of a NaN + * @return The quiet NaN value that corresponds to the identifying string arg or zero if the implementation does not support quiet NaNs. + * @note This function is not yet supported for long double and can only return either 0.0 or a quiet NaN based on if quiet NaNs are supported. + */ + constexpr long double nanl(const char * /* arg */) noexcept + { + if constexpr (!std::numeric_limits::is_iec559) + { + return 0.0; + } + + // Currently we do not yet support long double for ccm::nan + // Idk if we ever will, but for the time being if someone calls the function. Just return a quiet NaN. + return std::numeric_limits::quiet_NaN(); + } +} // namespace ccm diff --git a/include/ccmath/detail/basic/remainder.hpp b/include/ccmath/math/basic/remainder.hpp similarity index 57% rename from include/ccmath/detail/basic/remainder.hpp rename to include/ccmath/math/basic/remainder.hpp index 114417b..a22abd7 100644 --- a/include/ccmath/detail/basic/remainder.hpp +++ b/include/ccmath/math/basic/remainder.hpp @@ -8,9 +8,10 @@ #pragma once -#include "ccmath/detail/compare/isinf.hpp" -#include "ccmath/detail/compare/isnan.hpp" -#include "ccmath/detail/nearest/trunc.hpp" +#include "ccmath/internal/predef/unlikely.hpp" +#include "ccmath/math/compare/isinf.hpp" +#include "ccmath/math/compare/isnan.hpp" +#include "ccmath/math/nearest/trunc.hpp" namespace ccm { @@ -21,19 +22,16 @@ namespace ccm * @param y Divisor. * @return The remainder of the division of x by y. */ - template ::value, int> = 0> - inline constexpr T remainder(T x, T y) + template , int> = 0> + constexpr T remainder(T x, T y) { - if constexpr (std::is_floating_point_v) + // If x is ±∞ and y is not NaN, NaN is returned. + // If y is ±0 and x is not NaN, NaN is returned. + // If either argument is NaN, NaN is returned. + if (CCM_UNLIKELY((ccm::isinf(x) && !ccm::isnan(y)) || (y == 0 && !ccm::isnan(x)) || (ccm::isnan(x) || ccm::isnan(y)))) { - // If x is ±∞ and y is not NaN, NaN is returned. - // If y is ±0 and x is not NaN, NaN is returned. - // If either argument is NaN, NaN is returned. - if ((ccm::isinf(x) && !ccm::isnan(y)) || (y == 0 && !ccm::isnan(x)) || (ccm::isnan(x) || ccm::isnan(y))) - { - // All major compilers return -NaN. - return -std::numeric_limits::quiet_NaN(); - } + // All major compilers return -NaN. + return -std::numeric_limits::quiet_NaN(); } return static_cast(x - (ccm::trunc(x / y) * y)); @@ -46,8 +44,8 @@ namespace ccm * @param y Divisor. * @return The remainder of the division of x by y as a double. */ - template ::value, int> = 0> - inline constexpr double remainder(Integer x, Integer y) + template , int> = 0> + constexpr double remainder(Integer x, Integer y) { return remainder(static_cast(x), static_cast(y)); } @@ -58,7 +56,7 @@ namespace ccm * @param y Divisor. * @return The remainder of the division of x by y. */ - inline constexpr float remainderf(float x, float y) + constexpr float remainderf(float x, float y) { return remainder(x, y); } @@ -69,7 +67,7 @@ namespace ccm * @param y Divisor. * @return The remainder of the division of x by y. */ - inline constexpr long double remainderl(long double x, long double y) + constexpr long double remainderl(long double x, long double y) { return remainder(x, y); } diff --git a/include/ccmath/detail/basic/remquo.hpp b/include/ccmath/math/basic/remquo.hpp similarity index 92% rename from include/ccmath/detail/basic/remquo.hpp rename to include/ccmath/math/basic/remquo.hpp index 1a047c1..c9dc9b7 100644 --- a/include/ccmath/detail/basic/remquo.hpp +++ b/include/ccmath/math/basic/remquo.hpp @@ -8,8 +8,8 @@ #pragma once -#include "ccmath/detail/basic/impl/remquo_double_impl.hpp" -#include "ccmath/detail/basic/impl/remquo_float_impl.hpp" +#include "ccmath/math/basic/impl/remquo_double_impl.hpp" +#include "ccmath/math/basic/impl/remquo_float_impl.hpp" namespace ccm { @@ -42,7 +42,7 @@ namespace ccm * @endcode */ template , bool> = true> - inline constexpr T remquo(T x, T y, int * quo) + constexpr T remquo(T x, T y, int * quo) { if constexpr (std::is_same_v) { return ccm::internal::remquo_float(x, y, quo); } else { return ccm::internal::remquo_double(x, y, quo); } @@ -78,7 +78,7 @@ namespace ccm * @endcode */ template && std::is_arithmetic_v, bool> = true> - inline constexpr std::common_type_t remquo(Arithmetic1 x, Arithmetic2 y, int * quo) + constexpr std::common_type_t remquo(Arithmetic1 x, Arithmetic2 y, int * quo) { using shared_type = std::common_type_t; return ccm::remquo(static_cast(x), static_cast(y), quo); @@ -111,7 +111,7 @@ namespace ccm * } * @endcode */ - inline constexpr float remquof(float x, float y, int * quo) + constexpr float remquof(float x, float y, int * quo) { return ccm::remquo(x, y, quo); } @@ -143,7 +143,7 @@ namespace ccm * } * @endcode */ - inline constexpr long double remquol(long double x, long double y, int * quo) + constexpr long double remquol(long double x, long double y, int * quo) { // Currently, due to technical issues, the long double version of remquo is not implemented. // For now, we will cast the long double variables to a double and call the double version of remquo. diff --git a/include/ccmath/math/compare/fpclassify.hpp b/include/ccmath/math/compare/fpclassify.hpp new file mode 100644 index 0000000..28d128b --- /dev/null +++ b/include/ccmath/math/compare/fpclassify.hpp @@ -0,0 +1,37 @@ +/* + * 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/internal/helpers/fpclassify_helper.hpp" +#include "ccmath/math/basic/abs.hpp" +#include "ccmath/math/compare/isinf.hpp" +#include "ccmath/math/compare/isnan.hpp" + +namespace ccm +{ + /** + * @brief Classify the floating point number + * @attention This function is heavily implementation defined and may not work as expected on unknown compilers + * @tparam T The type of the floating point number + * @param num The number to classify + * @return The classification of the number as an integer + */ + template , bool> = true> + constexpr int fpclassify(T num) + { + if (ccm::isnan(num)) { return ccm::helpers::floating_point_defines::eFP_NAN; } + if (ccm::isinf(num)) { return ccm::helpers::floating_point_defines::eFP_INFINITE; } + if (num == static_cast(0)) { return ccm::helpers::floating_point_defines::eFP_ZERO; } + if (ccm::abs(num) < std::numeric_limits::min() && ccm::abs(num) > 0) + { + return ccm::helpers::floating_point_defines::eFP_SUBNORMAL; + } + return ccm::helpers::floating_point_defines::eFP_NORMAL; + } +} // namespace ccm diff --git a/include/ccmath/detail/compare/isfinite.hpp b/include/ccmath/math/compare/isfinite.hpp similarity index 84% rename from include/ccmath/detail/compare/isfinite.hpp rename to include/ccmath/math/compare/isfinite.hpp index c77ff73..970d7b2 100644 --- a/include/ccmath/detail/compare/isfinite.hpp +++ b/include/ccmath/math/compare/isfinite.hpp @@ -8,8 +8,8 @@ #pragma once -#include "ccmath/detail/compare/isinf.hpp" -#include "ccmath/detail/compare/isnan.hpp" +#include "ccmath/math/compare/isinf.hpp" +#include "ccmath/math/compare/isnan.hpp" namespace ccm { @@ -20,7 +20,7 @@ namespace ccm * @return true if the number has a finite value, false otherwise. */ template , bool> = true> - inline constexpr bool isfinite(T x) + constexpr bool isfinite(T x) { return (!ccm::isnan(x)) && (!ccm::isinf(x)); } @@ -32,7 +32,7 @@ namespace ccm * @return true if the number has a finite value, false otherwise. */ template , bool> = true> - inline constexpr bool isfinite(Integer /* x */) + constexpr bool isfinite(Integer /* x */) { return false; // All integers are finite } diff --git a/include/ccmath/detail/compare/isgreater.hpp b/include/ccmath/math/compare/isgreater.hpp similarity index 89% rename from include/ccmath/detail/compare/isgreater.hpp rename to include/ccmath/math/compare/isgreater.hpp index 825c7aa..72f6942 100644 --- a/include/ccmath/detail/compare/isgreater.hpp +++ b/include/ccmath/math/compare/isgreater.hpp @@ -20,10 +20,10 @@ namespace ccm * @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; - } + constexpr bool isgreater(T x, T y) noexcept + { + return x > y; + } /** * @brief Checks if the first argument is greater than the second. @@ -34,14 +34,14 @@ namespace ccm * @return true if the first argument is greater than the second, false otherwise. */ template - inline constexpr bool isgreater(T x, U y) noexcept - { + 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/math/compare/isgreaterequal.hpp b/include/ccmath/math/compare/isgreaterequal.hpp new file mode 100644 index 0000000..36b90c6 --- /dev/null +++ b/include/ccmath/math/compare/isgreaterequal.hpp @@ -0,0 +1,47 @@ +/* + * 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 +{ + /** + * @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 + 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 + 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/isinf.hpp b/include/ccmath/math/compare/isinf.hpp similarity index 91% rename from include/ccmath/detail/compare/isinf.hpp rename to include/ccmath/math/compare/isinf.hpp index 8ce37ee..0685c1f 100644 --- a/include/ccmath/detail/compare/isinf.hpp +++ b/include/ccmath/math/compare/isinf.hpp @@ -20,7 +20,7 @@ namespace ccm * @return True if the number is infinite, false otherwise. */ template , bool> = true> - inline constexpr bool isinf(T x) noexcept + constexpr bool isinf(T x) noexcept { if constexpr (std::numeric_limits::is_signed) { return x == -std::numeric_limits::infinity() || x == std::numeric_limits::infinity(); } else { return x == std::numeric_limits::infinity(); } @@ -33,7 +33,7 @@ namespace ccm * @return True if the number is infinite, false otherwise. */ template , bool> = true> - inline constexpr bool isinf(Integer /* x */) noexcept + constexpr bool isinf(Integer /* x */) noexcept { return false; // Integers cannot be infinite } diff --git a/include/ccmath/math/compare/isless.hpp b/include/ccmath/math/compare/isless.hpp new file mode 100644 index 0000000..646d44c --- /dev/null +++ b/include/ccmath/math/compare/isless.hpp @@ -0,0 +1,47 @@ +/* + * 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 +{ + /** + * @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 + 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 + 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/math/compare/islessequal.hpp b/include/ccmath/math/compare/islessequal.hpp new file mode 100644 index 0000000..d8ff348 --- /dev/null +++ b/include/ccmath/math/compare/islessequal.hpp @@ -0,0 +1,47 @@ +/* + * 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 +{ + /** + * @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 + 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 + 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/math/compare/islessgreater.hpp b/include/ccmath/math/compare/islessgreater.hpp new file mode 100644 index 0000000..b7528a4 --- /dev/null +++ b/include/ccmath/math/compare/islessgreater.hpp @@ -0,0 +1,44 @@ +/* + * 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 +{ + /** + * @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 + 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 + 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/isnan.hpp b/include/ccmath/math/compare/isnan.hpp similarity index 87% rename from include/ccmath/detail/compare/isnan.hpp rename to include/ccmath/math/compare/isnan.hpp index f8c9cbb..4394cfe 100644 --- a/include/ccmath/detail/compare/isnan.hpp +++ b/include/ccmath/math/compare/isnan.hpp @@ -19,9 +19,9 @@ namespace ccm * @return True if the number is NaN, false otherwise. */ template , int> = 0> - [[nodiscard]] inline constexpr bool isnan(T x) noexcept + [[nodiscard]] constexpr bool isnan(T x) noexcept { -#if defined(__GNUC__) +#if defined(__GNUC__) || defined(__clang__) return __builtin_isnan(x); // GCC and Clang implement this as constexpr #else // If we can't use the builtin, fallback to this comparison and hope for the best. return x != x; // NOLINT @@ -34,7 +34,7 @@ namespace ccm * @return False, as integers can never be NaN. */ template , int> = 0> - [[nodiscard]] inline constexpr bool isnan(Integer /* x */) + [[nodiscard]] constexpr bool isnan(Integer /* x */) { return false; // Integers can never be NaN. } diff --git a/include/ccmath/math/compare/isnormal.hpp b/include/ccmath/math/compare/isnormal.hpp new file mode 100644 index 0000000..fd1e6ee --- /dev/null +++ b/include/ccmath/math/compare/isnormal.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 "ccmath/math/basic/abs.hpp" +#include "ccmath/math/compare/isinf.hpp" +#include "ccmath/math/compare/isnan.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> + 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> + constexpr bool isnormal(Integer num) noexcept + { + return num != static_cast(0); + } + +} // namespace ccm diff --git a/include/ccmath/math/compare/isunordered.hpp b/include/ccmath/math/compare/isunordered.hpp new file mode 100644 index 0000000..1d3c598 --- /dev/null +++ b/include/ccmath/math/compare/isunordered.hpp @@ -0,0 +1,58 @@ +/* + * 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/math/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> + 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> + 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 + 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/math/compare/signbit.hpp similarity index 86% rename from include/ccmath/detail/compare/signbit.hpp rename to include/ccmath/math/compare/signbit.hpp index e0cec29..2219bc0 100644 --- a/include/ccmath/detail/compare/signbit.hpp +++ b/include/ccmath/math/compare/signbit.hpp @@ -11,7 +11,7 @@ #pragma once -#include "ccmath/detail/compare/isnan.hpp" +#include "ccmath/math/compare/isnan.hpp" // Some compilers have __builtin_signbit which is constexpr for some compilers #ifndef CCMATH_HAS_CONSTEXPR_BUILTIN_SIGNBIT @@ -37,7 +37,7 @@ #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 "ccmath/internal/support/floating_point_traits.hpp" #include // for std::numeric_limits #include // for std::uint64_t #endif @@ -64,8 +64,8 @@ namespace ccm * if your compiler is not supported a static assertion will be triggered. If this happens to you please report it to * the dev team and we will try to bring support to your compiler ASAP if we are able to! */ - template ::value, int> = 0> - [[nodiscard]] inline constexpr bool signbit(T x) noexcept + template , int> = 0> + [[nodiscard]] constexpr bool signbit(T x) noexcept { #if defined(CCMATH_HAS_CONSTEXPR_BUILTIN_SIGNBIT) return __builtin_signbit(x); @@ -75,7 +75,6 @@ namespace ccm { // If constexpr only works with gcc 7.1+. Without if constexpr we work till GCC 5.1+ // This works with clang 5.0.0 no problem even with if constexpr - // TODO: Add a compatibility layer for if constexpr to allow us to continue using gcc 5.1+ if constexpr (std::is_same_v) { return __builtin_copysignf(1.0f, x) < 0; } else if constexpr (std::is_same_v) { return __builtin_copysign(1.0, x) < 0; } else if constexpr (std::is_same_v) { return __builtin_copysignl(1.0l, x) < 0; } @@ -107,8 +106,8 @@ namespace ccm * * @note This function is constexpr and will return the same values as std::signbit along with being static_assert-able. */ - template ::value && std::is_signed::value, int> = 0> - [[nodiscard]] inline constexpr bool signbit(Integer x) noexcept + template && std::is_signed_v, int> = 0> + [[nodiscard]] constexpr bool signbit(Integer x) noexcept { // There is no concept of -0 for integers. So we can just check if the number is less than 0. return x < 0; @@ -122,10 +121,10 @@ namespace ccm * * @note This function is constexpr and will return the same values as std::signbit along with being static_assert-able. */ - template ::value && !std::is_signed::value, int> = 0> - [[nodiscard]] inline constexpr bool signbit(Integer /* unused */) noexcept + template && !std::is_signed_v, int> = 0> + [[nodiscard]] constexpr bool signbit(Integer /* unused */) noexcept { - // If the number is unsigned then it can't be negative. + // If the number is unsigned, then it can't be negative. return false; } diff --git a/include/ccmath/math/exponential/exp.hpp b/include/ccmath/math/exponential/exp.hpp new file mode 100644 index 0000000..b22267f --- /dev/null +++ b/include/ccmath/math/exponential/exp.hpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2024-Present Ian Pike + * Copyright (c) 2024-Present cmath contributors + * + * This library is provided under the MIT License. + * See LICENSE for more information. + */ + +#pragma once + +#include "ccmath/math/exponential/impl/exp_double_impl.hpp" +#include "ccmath/math/exponential/impl/exp_float_impl.hpp" + +namespace ccm +{ + /** + * @brief Computes e raised to the given power + * @tparam T floating-point or integer type + * @param num floating-point or integer value + * @return If no errors occur, the base-e exponential of num (e^num) is returned. + */ + template , bool> = true> + constexpr T exp(T num) + { + if constexpr (std::is_same_v) { return ccm::internal::impl::exp_float_impl(num); } + else { return ccm::internal::impl::exp_double_impl(num); } + } + + /** + * @brief Computes e raised to the given power + * @tparam Integer integer type + * @param num integer value + * @return If no errors occur, the base-e exponential of num (e^num) is returned as double. + */ + template , bool> = true> + constexpr double exp(Integer num) + { + return ccm::exp(static_cast(num)); + } + + /** + * @brief Computes e raised to the given power + * @param num floating-point value + * @return If no errors occur, the base-e exponential of num (e^num) is returned as float. + */ + constexpr float expf(float num) + { + return ccm::internal::impl::exp_float_impl(num); + } + + /** + * @brief Computes e raised to the given power + * @param num floating-point value + * @return If no errors occur, the base-e exponential of num (e^num) is returned as double. + */ + constexpr long double expl(long double num) + { + return static_cast(ccm::exp(static_cast(num))); + } + +} // namespace ccm diff --git a/include/ccmath/detail/exponential/exp2.hpp b/include/ccmath/math/exponential/exp2.hpp similarity index 74% rename from include/ccmath/detail/exponential/exp2.hpp rename to include/ccmath/math/exponential/exp2.hpp index b950a84..e41553b 100644 --- a/include/ccmath/detail/exponential/exp2.hpp +++ b/include/ccmath/math/exponential/exp2.hpp @@ -10,5 +10,10 @@ namespace ccm { + template + constexpr T exp2([[maybe_unused]] T x) + { + return 0; + } } // namespace ccm diff --git a/include/ccmath/detail/exponential/expm1.hpp b/include/ccmath/math/exponential/expm1.hpp similarity index 100% rename from include/ccmath/detail/exponential/expm1.hpp rename to include/ccmath/math/exponential/expm1.hpp diff --git a/include/ccmath/math/exponential/impl/exp2_data.hpp b/include/ccmath/math/exponential/impl/exp2_data.hpp new file mode 100644 index 0000000..436cf57 --- /dev/null +++ b/include/ccmath/math/exponential/impl/exp2_data.hpp @@ -0,0 +1,334 @@ +/* + * 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. + */ +// Values borrowed from gcc + +#pragma once + +#include +#include +#include +#include + +namespace ccm::internal +{ + // Float constants + constexpr std::size_t k_exp_table_bits_flt = 5; + constexpr std::size_t k_exp2_poly_order_flt = 3; + + // Double constants + constexpr std::size_t k_exp_table_bits_dbl = 7; + constexpr std::size_t k_exp_poly_order_dbl = 5; + constexpr std::size_t k_exp2_poly_order_dbl = 5; + + template , int> = 0> + struct exp_data; + + template <> + struct exp_data + { + }; + + template <> + struct exp_data + { + double invln2N{0x1.71547652b82fep0 * (1 << k_exp_table_bits_dbl)}; // N/ln2 + double shift{0x1.8p52}; + double negln2hiN{-0x1.62e42fefa0000p-8}; + double negln2loN{-0x1.cf79abc9e3b3ap-47}; + + // Last four coefficients. + // abs error: 1.555*2^-66 + // ulp error: 0.511 + // if |x| < ln2/256+eps + // abs error if |x| < ln2/128: 1.7145*2^-56 + std::array poly = { + 0x1.ffffffffffdbdp-2, + 0x1.555555555543cp-3, + 0x1.55555cf172b91p-5, + 0x1.1111167a4d017p-7, + }; + + double exp2_shift{0x1.8p52 / (1 << k_exp_table_bits_dbl)}; + + // exp2 polynomial coefficients. + // abs error: 1.2195*2^-65 + // ulp error: 0.511 + // if |x| < 1/256 + // abs error if |x| < 1/128: 1.9941*2^-56 + std::array exp2_poly = { + 0x1.62e42fefa39efp-1, 0x1.ebfbdff82c424p-3, 0x1.c6b08d70cf4b5p-5, 0x1.3b2abd24650ccp-7, 0x1.5d7e09b4e3a84p-10, + }; + + // 2^(k/N) ~= H[k]*(1 + T[k]) for int k in [0,N) + // tab[2*k] = ccm::helpers::double_to_uint64(T[k]) + // tab[2*k+1] = ccm::helpers::double_to_uint64(H[k]) - (k << 52)/N + std::array(2 * (1 << k_exp_table_bits_dbl))> tab{ + 0x0, + 0x3ff0000000000000, + 0x3c9b3b4f1a88bf6e, + 0x3feff63da9fb3335, + 0xbc7160139cd8dc5d, + 0x3fefec9a3e778061, + 0xbc905e7a108766d1, + 0x3fefe315e86e7f85, + 0x3c8cd2523567f613, + 0x3fefd9b0d3158574, + 0xbc8bce8023f98efa, + 0x3fefd06b29ddf6de, + 0x3c60f74e61e6c861, + 0x3fefc74518759bc8, + 0x3c90a3e45b33d399, + 0x3fefbe3ecac6f383, + 0x3c979aa65d837b6d, + 0x3fefb5586cf9890f, + 0x3c8eb51a92fdeffc, + 0x3fefac922b7247f7, + 0x3c3ebe3d702f9cd1, + 0x3fefa3ec32d3d1a2, + 0xbc6a033489906e0b, + 0x3fef9b66affed31b, + 0xbc9556522a2fbd0e, + 0x3fef9301d0125b51, + 0xbc5080ef8c4eea55, + 0x3fef8abdc06c31cc, + 0xbc91c923b9d5f416, + 0x3fef829aaea92de0, + 0x3c80d3e3e95c55af, + 0x3fef7a98c8a58e51, + 0xbc801b15eaa59348, + 0x3fef72b83c7d517b, + 0xbc8f1ff055de323d, + 0x3fef6af9388c8dea, + 0x3c8b898c3f1353bf, + 0x3fef635beb6fcb75, + 0xbc96d99c7611eb26, + 0x3fef5be084045cd4, + 0x3c9aecf73e3a2f60, + 0x3fef54873168b9aa, + 0xbc8fe782cb86389d, + 0x3fef4d5022fcd91d, + 0x3c8a6f4144a6c38d, + 0x3fef463b88628cd6, + 0x3c807a05b0e4047d, + 0x3fef3f49917ddc96, + 0x3c968efde3a8a894, + 0x3fef387a6e756238, + 0x3c875e18f274487d, + 0x3fef31ce4fb2a63f, + 0x3c80472b981fe7f2, + 0x3fef2b4565e27cdd, + 0xbc96b87b3f71085e, + 0x3fef24dfe1f56381, + 0x3c82f7e16d09ab31, + 0x3fef1e9df51fdee1, + 0xbc3d219b1a6fbffa, + 0x3fef187fd0dad990, + 0x3c8b3782720c0ab4, + 0x3fef1285a6e4030b, + 0x3c6e149289cecb8f, + 0x3fef0cafa93e2f56, + 0x3c834d754db0abb6, + 0x3fef06fe0a31b715, + 0x3c864201e2ac744c, + 0x3fef0170fc4cd831, + 0x3c8fdd395dd3f84a, + 0x3feefc08b26416ff, + 0xbc86a3803b8e5b04, + 0x3feef6c55f929ff1, + 0xbc924aedcc4b5068, + 0x3feef1a7373aa9cb, + 0xbc9907f81b512d8e, + 0x3feeecae6d05d866, + 0xbc71d1e83e9436d2, + 0x3feee7db34e59ff7, + 0xbc991919b3ce1b15, + 0x3feee32dc313a8e5, + 0x3c859f48a72a4c6d, + 0x3feedea64c123422, + 0xbc9312607a28698a, + 0x3feeda4504ac801c, + 0xbc58a78f4817895b, + 0x3feed60a21f72e2a, + 0xbc7c2c9b67499a1b, + 0x3feed1f5d950a897, + 0x3c4363ed60c2ac11, + 0x3feece086061892d, + 0x3c9666093b0664ef, + 0x3feeca41ed1d0057, + 0x3c6ecce1daa10379, + 0x3feec6a2b5c13cd0, + 0x3c93ff8e3f0f1230, + 0x3feec32af0d7d3de, + 0x3c7690cebb7aafb0, + 0x3feebfdad5362a27, + 0x3c931dbdeb54e077, + 0x3feebcb299fddd0d, + 0xbc8f94340071a38e, + 0x3feeb9b2769d2ca7, + 0xbc87deccdc93a349, + 0x3feeb6daa2cf6642, + 0xbc78dec6bd0f385f, + 0x3feeb42b569d4f82, + 0xbc861246ec7b5cf6, + 0x3feeb1a4ca5d920f, + 0x3c93350518fdd78e, + 0x3feeaf4736b527da, + 0x3c7b98b72f8a9b05, + 0x3feead12d497c7fd, + 0x3c9063e1e21c5409, + 0x3feeab07dd485429, + 0x3c34c7855019c6ea, + 0x3feea9268a5946b7, + 0x3c9432e62b64c035, + 0x3feea76f15ad2148, + 0xbc8ce44a6199769f, + 0x3feea5e1b976dc09, + 0xbc8c33c53bef4da8, + 0x3feea47eb03a5585, + 0xbc845378892be9ae, + 0x3feea34634ccc320, + 0xbc93cedd78565858, + 0x3feea23882552225, + 0x3c5710aa807e1964, + 0x3feea155d44ca973, + 0xbc93b3efbf5e2228, + 0x3feea09e667f3bcd, + 0xbc6a12ad8734b982, + 0x3feea012750bdabf, + 0xbc6367efb86da9ee, + 0x3fee9fb23c651a2f, + 0xbc80dc3d54e08851, + 0x3fee9f7df9519484, + 0xbc781f647e5a3ecf, + 0x3fee9f75e8ec5f74, + 0xbc86ee4ac08b7db0, + 0x3fee9f9a48a58174, + 0xbc8619321e55e68a, + 0x3fee9feb564267c9, + 0x3c909ccb5e09d4d3, + 0x3feea0694fde5d3f, + 0xbc7b32dcb94da51d, + 0x3feea11473eb0187, + 0x3c94ecfd5467c06b, + 0x3feea1ed0130c132, + 0x3c65ebe1abd66c55, + 0x3feea2f336cf4e62, + 0xbc88a1c52fb3cf42, + 0x3feea427543e1a12, + 0xbc9369b6f13b3734, + 0x3feea589994cce13, + 0xbc805e843a19ff1e, + 0x3feea71a4623c7ad, + 0xbc94d450d872576e, + 0x3feea8d99b4492ed, + 0x3c90ad675b0e8a00, + 0x3feeaac7d98a6699, + 0x3c8db72fc1f0eab4, + 0x3feeace5422aa0db, + 0xbc65b6609cc5e7ff, + 0x3feeaf3216b5448c, + 0x3c7bf68359f35f44, + 0x3feeb1ae99157736, + 0xbc93091fa71e3d83, + 0x3feeb45b0b91ffc6, + 0xbc5da9b88b6c1e29, + 0x3feeb737b0cdc5e5, + 0xbc6c23f97c90b959, + 0x3feeba44cbc8520f, + 0xbc92434322f4f9aa, + 0x3feebd829fde4e50, + 0xbc85ca6cd7668e4b, + 0x3feec0f170ca07ba, + 0x3c71affc2b91ce27, + 0x3feec49182a3f090, + 0x3c6dd235e10a73bb, + 0x3feec86319e32323, + 0xbc87c50422622263, + 0x3feecc667b5de565, + 0x3c8b1c86e3e231d5, + 0x3feed09bec4a2d33, + 0xbc91bbd1d3bcbb15, + 0x3feed503b23e255d, + 0x3c90cc319cee31d2, + 0x3feed99e1330b358, + 0x3c8469846e735ab3, + 0x3feede6b5579fdbf, + 0xbc82dfcd978e9db4, + 0x3feee36bbfd3f37a, + 0x3c8c1a7792cb3387, + 0x3feee89f995ad3ad, + 0xbc907b8f4ad1d9fa, + 0x3feeee07298db666, + 0xbc55c3d956dcaeba, + 0x3feef3a2b84f15fb, + 0xbc90a40e3da6f640, + 0x3feef9728de5593a, + 0xbc68d6f438ad9334, + 0x3feeff76f2fb5e47, + 0xbc91eee26b588a35, + 0x3fef05b030a1064a, + 0x3c74ffd70a5fddcd, + 0x3fef0c1e904bc1d2, + 0xbc91bdfbfa9298ac, + 0x3fef12c25bd71e09, + 0x3c736eae30af0cb3, + 0x3fef199bdd85529c, + 0x3c8ee3325c9ffd94, + 0x3fef20ab5fffd07a, + 0x3c84e08fd10959ac, + 0x3fef27f12e57d14b, + 0x3c63cdaf384e1a67, + 0x3fef2f6d9406e7b5, + 0x3c676b2c6c921968, + 0x3fef3720dcef9069, + 0xbc808a1883ccb5d2, + 0x3fef3f0b555dc3fa, + 0xbc8fad5d3ffffa6f, + 0x3fef472d4a07897c, + 0xbc900dae3875a949, + 0x3fef4f87080d89f2, + 0x3c74a385a63d07a7, + 0x3fef5818dcfba487, + 0xbc82919e2040220f, + 0x3fef60e316c98398, + 0x3c8e5a50d5c192ac, + 0x3fef69e603db3285, + 0x3c843a59ac016b4b, + 0x3fef7321f301b460, + 0xbc82d52107b43e1f, + 0x3fef7c97337b9b5f, + 0xbc892ab93b470dc9, + 0x3fef864614f5a129, + 0x3c74b604603a88d3, + 0x3fef902ee78b3ff6, + 0x3c83c5ec519d7271, + 0x3fef9a51fbc74c83, + 0xbc8ff7128fd391f0, + 0x3fefa4afa2a490da, + 0xbc8dae98e223747d, + 0x3fefaf482d8e67f1, + 0x3c8ec3bc41aa2008, + 0x3fefba1bee615a27, + 0x3c842b94c3a9eb32, + 0x3fefc52b376bba97, + 0x3c8a64a931d185ee, + 0x3fefd0765b6e4540, + 0xbc8e37bae43be3ed, + 0x3fefdbfdad9cbe14, + 0x3c77893b4d91cd9d, + 0x3fefe7c1819e90d8, + 0x3c5305c14160cc89, + 0x3feff3c22b8f71f1, + }; + }; + + template <> + struct exp_data : exp_data + { + }; +} // namespace ccm::internal diff --git a/include/ccmath/math/exponential/impl/exp_data.hpp b/include/ccmath/math/exponential/impl/exp_data.hpp new file mode 100644 index 0000000..2de4107 --- /dev/null +++ b/include/ccmath/math/exponential/impl/exp_data.hpp @@ -0,0 +1,337 @@ +/* + * 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. + */ +// Values borrowed from gcc + +#pragma once + +#include +#include +#include +#include + +namespace ccm::internal +{ + // Float constants + constexpr std::size_t k_exp_table_bits_flt = 5; + constexpr std::size_t k_exp_poly_order_flt = 3; + + // Double constants + constexpr std::size_t k_exp_table_bits_dbl = 7; + constexpr std::size_t k_exp_poly_order_dbl = 5; + + template , int> = 0> + struct exp_data; + + template <> + struct exp_data + { + double invln2_scaled{0x1.71547652b82fep0 * (1 << k_exp_table_bits_flt)}; + double shift{0x1.8p+52}; + std::array tab = { + 0x3ff0000000000000, 0x3fefd9b0d3158574, 0x3fefb5586cf9890f, 0x3fef9301d0125b51, 0x3fef72b83c7d517b, 0x3fef54873168b9aa, 0x3fef387a6e756238, + 0x3fef1e9df51fdee1, 0x3fef06fe0a31b715, 0x3feef1a7373aa9cb, 0x3feedea64c123422, 0x3feece086061892d, 0x3feebfdad5362a27, 0x3feeb42b569d4f82, + 0x3feeab07dd485429, 0x3feea47eb03a5585, 0x3feea09e667f3bcd, 0x3fee9f75e8ec5f74, 0x3feea11473eb0187, 0x3feea589994cce13, 0x3feeace5422aa0db, + 0x3feeb737b0cdc5e5, 0x3feec49182a3f090, 0x3feed503b23e255d, 0x3feee89f995ad3ad, 0x3feeff76f2fb5e47, 0x3fef199bdd85529c, 0x3fef3720dcef9069, + 0x3fef5818dcfba487, 0x3fef7c97337b9b5f, 0x3fefa4afa2a490da, 0x3fefd0765b6e4540, + }; + + std::array poly_scaled = { + 0x1.c6af84b912394p-5 / (1 << k_exp_table_bits_flt) / (1 << k_exp_table_bits_flt) / (1 << k_exp_table_bits_flt), + 0x1.ebfce50fac4f3p-3 / (1 << k_exp_table_bits_flt) / (1 << k_exp_table_bits_flt), + 0x1.62e42ff0c52d6p-1 / (1 << k_exp_table_bits_flt), + }; + }; + + template <> + struct exp_data + { + double invln2N{0x1.71547652b82fep0 * (1 << k_exp_table_bits_dbl)}; // N/ln2 + double shift{0x1.8p52}; + double negln2hiN{-0x1.62e42fefa0000p-8}; + double negln2loN{-0x1.cf79abc9e3b3ap-47}; + + // Last four coefficients. + // abs error: 1.555*2^-66 + // ulp error: 0.511 + // if |x| < ln2/256+eps + // abs error if |x| < ln2/128: 1.7145*2^-56 + std::array poly = { + 0x1.ffffffffffdbdp-2, + 0x1.555555555543cp-3, + 0x1.55555cf172b91p-5, + 0x1.1111167a4d017p-7, + }; + + // 2^(k/N) ~= H[k]*(1 + T[k]) for int k in [0,N) + // tab[2*k] = ccm::helpers::double_to_uint64(T[k]) + // tab[2*k+1] = ccm::helpers::double_to_uint64(H[k]) - (k << 52)/N + std::array(2 * (1 << k_exp_table_bits_dbl))> tab{ + 0x0, + 0x3ff0000000000000, + 0x3c9b3b4f1a88bf6e, + 0x3feff63da9fb3335, + 0xbc7160139cd8dc5d, + 0x3fefec9a3e778061, + 0xbc905e7a108766d1, + 0x3fefe315e86e7f85, + 0x3c8cd2523567f613, + 0x3fefd9b0d3158574, + 0xbc8bce8023f98efa, + 0x3fefd06b29ddf6de, + 0x3c60f74e61e6c861, + 0x3fefc74518759bc8, + 0x3c90a3e45b33d399, + 0x3fefbe3ecac6f383, + 0x3c979aa65d837b6d, + 0x3fefb5586cf9890f, + 0x3c8eb51a92fdeffc, + 0x3fefac922b7247f7, + 0x3c3ebe3d702f9cd1, + 0x3fefa3ec32d3d1a2, + 0xbc6a033489906e0b, + 0x3fef9b66affed31b, + 0xbc9556522a2fbd0e, + 0x3fef9301d0125b51, + 0xbc5080ef8c4eea55, + 0x3fef8abdc06c31cc, + 0xbc91c923b9d5f416, + 0x3fef829aaea92de0, + 0x3c80d3e3e95c55af, + 0x3fef7a98c8a58e51, + 0xbc801b15eaa59348, + 0x3fef72b83c7d517b, + 0xbc8f1ff055de323d, + 0x3fef6af9388c8dea, + 0x3c8b898c3f1353bf, + 0x3fef635beb6fcb75, + 0xbc96d99c7611eb26, + 0x3fef5be084045cd4, + 0x3c9aecf73e3a2f60, + 0x3fef54873168b9aa, + 0xbc8fe782cb86389d, + 0x3fef4d5022fcd91d, + 0x3c8a6f4144a6c38d, + 0x3fef463b88628cd6, + 0x3c807a05b0e4047d, + 0x3fef3f49917ddc96, + 0x3c968efde3a8a894, + 0x3fef387a6e756238, + 0x3c875e18f274487d, + 0x3fef31ce4fb2a63f, + 0x3c80472b981fe7f2, + 0x3fef2b4565e27cdd, + 0xbc96b87b3f71085e, + 0x3fef24dfe1f56381, + 0x3c82f7e16d09ab31, + 0x3fef1e9df51fdee1, + 0xbc3d219b1a6fbffa, + 0x3fef187fd0dad990, + 0x3c8b3782720c0ab4, + 0x3fef1285a6e4030b, + 0x3c6e149289cecb8f, + 0x3fef0cafa93e2f56, + 0x3c834d754db0abb6, + 0x3fef06fe0a31b715, + 0x3c864201e2ac744c, + 0x3fef0170fc4cd831, + 0x3c8fdd395dd3f84a, + 0x3feefc08b26416ff, + 0xbc86a3803b8e5b04, + 0x3feef6c55f929ff1, + 0xbc924aedcc4b5068, + 0x3feef1a7373aa9cb, + 0xbc9907f81b512d8e, + 0x3feeecae6d05d866, + 0xbc71d1e83e9436d2, + 0x3feee7db34e59ff7, + 0xbc991919b3ce1b15, + 0x3feee32dc313a8e5, + 0x3c859f48a72a4c6d, + 0x3feedea64c123422, + 0xbc9312607a28698a, + 0x3feeda4504ac801c, + 0xbc58a78f4817895b, + 0x3feed60a21f72e2a, + 0xbc7c2c9b67499a1b, + 0x3feed1f5d950a897, + 0x3c4363ed60c2ac11, + 0x3feece086061892d, + 0x3c9666093b0664ef, + 0x3feeca41ed1d0057, + 0x3c6ecce1daa10379, + 0x3feec6a2b5c13cd0, + 0x3c93ff8e3f0f1230, + 0x3feec32af0d7d3de, + 0x3c7690cebb7aafb0, + 0x3feebfdad5362a27, + 0x3c931dbdeb54e077, + 0x3feebcb299fddd0d, + 0xbc8f94340071a38e, + 0x3feeb9b2769d2ca7, + 0xbc87deccdc93a349, + 0x3feeb6daa2cf6642, + 0xbc78dec6bd0f385f, + 0x3feeb42b569d4f82, + 0xbc861246ec7b5cf6, + 0x3feeb1a4ca5d920f, + 0x3c93350518fdd78e, + 0x3feeaf4736b527da, + 0x3c7b98b72f8a9b05, + 0x3feead12d497c7fd, + 0x3c9063e1e21c5409, + 0x3feeab07dd485429, + 0x3c34c7855019c6ea, + 0x3feea9268a5946b7, + 0x3c9432e62b64c035, + 0x3feea76f15ad2148, + 0xbc8ce44a6199769f, + 0x3feea5e1b976dc09, + 0xbc8c33c53bef4da8, + 0x3feea47eb03a5585, + 0xbc845378892be9ae, + 0x3feea34634ccc320, + 0xbc93cedd78565858, + 0x3feea23882552225, + 0x3c5710aa807e1964, + 0x3feea155d44ca973, + 0xbc93b3efbf5e2228, + 0x3feea09e667f3bcd, + 0xbc6a12ad8734b982, + 0x3feea012750bdabf, + 0xbc6367efb86da9ee, + 0x3fee9fb23c651a2f, + 0xbc80dc3d54e08851, + 0x3fee9f7df9519484, + 0xbc781f647e5a3ecf, + 0x3fee9f75e8ec5f74, + 0xbc86ee4ac08b7db0, + 0x3fee9f9a48a58174, + 0xbc8619321e55e68a, + 0x3fee9feb564267c9, + 0x3c909ccb5e09d4d3, + 0x3feea0694fde5d3f, + 0xbc7b32dcb94da51d, + 0x3feea11473eb0187, + 0x3c94ecfd5467c06b, + 0x3feea1ed0130c132, + 0x3c65ebe1abd66c55, + 0x3feea2f336cf4e62, + 0xbc88a1c52fb3cf42, + 0x3feea427543e1a12, + 0xbc9369b6f13b3734, + 0x3feea589994cce13, + 0xbc805e843a19ff1e, + 0x3feea71a4623c7ad, + 0xbc94d450d872576e, + 0x3feea8d99b4492ed, + 0x3c90ad675b0e8a00, + 0x3feeaac7d98a6699, + 0x3c8db72fc1f0eab4, + 0x3feeace5422aa0db, + 0xbc65b6609cc5e7ff, + 0x3feeaf3216b5448c, + 0x3c7bf68359f35f44, + 0x3feeb1ae99157736, + 0xbc93091fa71e3d83, + 0x3feeb45b0b91ffc6, + 0xbc5da9b88b6c1e29, + 0x3feeb737b0cdc5e5, + 0xbc6c23f97c90b959, + 0x3feeba44cbc8520f, + 0xbc92434322f4f9aa, + 0x3feebd829fde4e50, + 0xbc85ca6cd7668e4b, + 0x3feec0f170ca07ba, + 0x3c71affc2b91ce27, + 0x3feec49182a3f090, + 0x3c6dd235e10a73bb, + 0x3feec86319e32323, + 0xbc87c50422622263, + 0x3feecc667b5de565, + 0x3c8b1c86e3e231d5, + 0x3feed09bec4a2d33, + 0xbc91bbd1d3bcbb15, + 0x3feed503b23e255d, + 0x3c90cc319cee31d2, + 0x3feed99e1330b358, + 0x3c8469846e735ab3, + 0x3feede6b5579fdbf, + 0xbc82dfcd978e9db4, + 0x3feee36bbfd3f37a, + 0x3c8c1a7792cb3387, + 0x3feee89f995ad3ad, + 0xbc907b8f4ad1d9fa, + 0x3feeee07298db666, + 0xbc55c3d956dcaeba, + 0x3feef3a2b84f15fb, + 0xbc90a40e3da6f640, + 0x3feef9728de5593a, + 0xbc68d6f438ad9334, + 0x3feeff76f2fb5e47, + 0xbc91eee26b588a35, + 0x3fef05b030a1064a, + 0x3c74ffd70a5fddcd, + 0x3fef0c1e904bc1d2, + 0xbc91bdfbfa9298ac, + 0x3fef12c25bd71e09, + 0x3c736eae30af0cb3, + 0x3fef199bdd85529c, + 0x3c8ee3325c9ffd94, + 0x3fef20ab5fffd07a, + 0x3c84e08fd10959ac, + 0x3fef27f12e57d14b, + 0x3c63cdaf384e1a67, + 0x3fef2f6d9406e7b5, + 0x3c676b2c6c921968, + 0x3fef3720dcef9069, + 0xbc808a1883ccb5d2, + 0x3fef3f0b555dc3fa, + 0xbc8fad5d3ffffa6f, + 0x3fef472d4a07897c, + 0xbc900dae3875a949, + 0x3fef4f87080d89f2, + 0x3c74a385a63d07a7, + 0x3fef5818dcfba487, + 0xbc82919e2040220f, + 0x3fef60e316c98398, + 0x3c8e5a50d5c192ac, + 0x3fef69e603db3285, + 0x3c843a59ac016b4b, + 0x3fef7321f301b460, + 0xbc82d52107b43e1f, + 0x3fef7c97337b9b5f, + 0xbc892ab93b470dc9, + 0x3fef864614f5a129, + 0x3c74b604603a88d3, + 0x3fef902ee78b3ff6, + 0x3c83c5ec519d7271, + 0x3fef9a51fbc74c83, + 0xbc8ff7128fd391f0, + 0x3fefa4afa2a490da, + 0xbc8dae98e223747d, + 0x3fefaf482d8e67f1, + 0x3c8ec3bc41aa2008, + 0x3fefba1bee615a27, + 0x3c842b94c3a9eb32, + 0x3fefc52b376bba97, + 0x3c8a64a931d185ee, + 0x3fefd0765b6e4540, + 0xbc8e37bae43be3ed, + 0x3fefdbfdad9cbe14, + 0x3c77893b4d91cd9d, + 0x3fefe7c1819e90d8, + 0x3c5305c14160cc89, + 0x3feff3c22b8f71f1, + }; + }; + + template <> + struct exp_data : exp_data + { + }; +} // namespace ccm::internal diff --git a/include/ccmath/math/exponential/impl/exp_double_impl.hpp b/include/ccmath/math/exponential/impl/exp_double_impl.hpp new file mode 100644 index 0000000..e8272b0 --- /dev/null +++ b/include/ccmath/math/exponential/impl/exp_double_impl.hpp @@ -0,0 +1,143 @@ +/* + * 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/internal/helpers/exp_helpers.hpp" +#include "ccmath/internal/predef/unlikely.hpp" +#include "ccmath/internal/types/fp_types.hpp" +#include "ccmath/math/exponential/impl/exp_data.hpp" +#include +#include + +namespace ccm::internal::impl +{ + constexpr ccm::internal::exp_data internal_exp_data_dbl = ccm::internal::exp_data(); + constexpr auto exp_invLn2N_dbl = internal_exp_data_dbl.invln2N; + constexpr auto exp_negLn2HiN_dbl = internal_exp_data_dbl.negln2hiN; + constexpr auto exp_negLn2LoN_dbl = internal_exp_data_dbl.negln2loN; + constexpr auto exp_shift_dbl = internal_exp_data_dbl.shift; + constexpr auto exp_tab_dbl = internal_exp_data_dbl.tab; + constexpr auto exp_poly_coeff_one_dbl = internal_exp_data_dbl.poly[5 - k_exp_poly_order_dbl]; + constexpr auto exp_poly_coeff_two_dbl = internal_exp_data_dbl.poly[6 - k_exp_poly_order_dbl]; + constexpr auto exp_poly_coeff_three_dbl = internal_exp_data_dbl.poly[7 - k_exp_poly_order_dbl]; + constexpr auto exp_poly_coeff_four_dbl = internal_exp_data_dbl.poly[8 - k_exp_poly_order_dbl]; + constexpr auto k_exp_table_n_dbl = (1 << ccm::internal::k_exp_table_bits_dbl); + + constexpr double handle_special_case(ccm::double_t tmp, std::uint64_t sign_bits, std::uint64_t exponent_int64) // NOLINT + { + ccm::double_t scale{}; + ccm::double_t result{}; + + if ((exponent_int64 & 0x80000000) == 0) + { + // If k > 0 then the exponent of scale might have overflowed by <= 460. + sign_bits -= 1009ULL << 52; + scale = ccm::support::uint64_to_double(sign_bits); + result = 0x1p1009 * (scale + scale * tmp); + return result; + } + + // If k < 0 then we need special care in the subnormal range. + sign_bits += 1022ULL << 52; + scale = ccm::support::uint64_to_double(sign_bits); + result = scale + scale * tmp; + if (result < 1.0) + { + // We need to round y into the correct precision before we can begin scaling it into + // the subnormal range so that we can avoid double round that could cause a 0.5+E/2 ulp error + // where E is the worst case ulp error outside the subnormal range. + ccm::double_t hi{}; + ccm::double_t lo{}; + lo = scale - result + scale * tmp; + hi = 1.0 + result; + lo = 1.0 - hi + result + lo; + result = ccm::helpers::narrow_eval(hi + lo) - 1.0; + // Prevent -0.0 with downward rounding. + if (result == 0.0) { result = 0.0; } + } + + + result = 0x1p-1022 * result; + + return result; + } + + constexpr double exp_double_impl(double x) + { + std::uint32_t abs_top{}; + std::uint64_t expo_int64{}; + std::uint64_t index{}; + std::uint64_t top{}; + std::uint64_t sign_bits{}; + + ccm::double_t expo{}; + ccm::double_t scaled_input{}; + ccm::double_t rem{}; + ccm::double_t remSqr{}; + ccm::double_t scale{}; + ccm::double_t tail{}; + ccm::double_t tmp{}; + + abs_top = ccm::support::top12_bits_of_double(x) & 0x7ff; + if (CCM_UNLIKELY(abs_top - ccm::support::top12_bits_of_double(0x1p-54) >= + ccm::support::top12_bits_of_double(512.0) - ccm::support::top12_bits_of_double(0x1p-54))) + { + // Avoid raising underflow for tiny x. 0 is a common input. + if (abs_top - ccm::support::top12_bits_of_double(0x1p-54) >= 0x80000000) { return 1.0 + x; } + + if (abs_top >= ccm::support::top12_bits_of_double(1024.0)) + { + if (ccm::support::double_to_uint64(x) == ccm::support::double_to_uint64(-std::numeric_limits::infinity())) { return 0.0; } + + if (abs_top >= ccm::support::top12_bits_of_double(std::numeric_limits::infinity())) { return 1.0 + x; } + + // Handle underflow + if ((ccm::support::double_to_uint64(x) >> 63) != 0U) { return 0x1p-767 * 0x1p-767; } + + // Handle Overflow + return 0x1p769 * 0x1p769; + } + // A large x is handled below. + abs_top = 0; + } + + // exp(x) = 2^(expo/N) * exp(r), with exp(rem) in [2^(-1/2N),2^(1/2N)]. + // x = ln2/N*expo + rem, with int expo and rem in [-ln2/2N, ln2/2N]. + scaled_input = exp_invLn2N_dbl * x; + + // scaled_input - expo is in [-1, 1] in non-nearest rounding modes. + expo = ccm::helpers::narrow_eval(scaled_input + exp_shift_dbl); + expo_int64 = ccm::support::double_to_uint64(expo); + expo -= exp_shift_dbl; + + rem = x + expo * exp_negLn2HiN_dbl + expo * exp_negLn2LoN_dbl; + + // 2^(expo/N) ~= scale * (1 + tail). + index = 2 * (expo_int64 % k_exp_table_n_dbl); + top = expo_int64 << (52 - k_exp_table_bits_dbl); + tail = ccm::support::uint64_to_double(exp_tab_dbl.at(index)); + + // This is only a valid scale when -1023*N < expo < 1024*N. + sign_bits = exp_tab_dbl.at(index + 1) + top; + + // exp(x) = 2^(expo/N) * exp(rem) ~= scale + scale * (tail + exp(rem) - 1). + // This evaluation is optimized assuming super scalar pipelined execution. + remSqr = rem * rem; + + // Worst case error is less than (0.5+1.11/N+(abs poly error * 2^53))+0.25/N ulp. + tmp = tail + rem + remSqr * (exp_poly_coeff_one_dbl + rem * exp_poly_coeff_two_dbl) + + remSqr * remSqr * (exp_poly_coeff_three_dbl + rem * exp_poly_coeff_four_dbl); + if (CCM_UNLIKELY(abs_top == 0.0)) { return handle_special_case(tmp, sign_bits, expo_int64); } + + scale = ccm::support::uint64_to_double(sign_bits); + + // Note: tmp == 0 or |tmp| > 2^-65 and scale > 2^-739, so there is no spurious underflow here. + return scale + scale * tmp; + } +} // namespace ccm::internal::impl diff --git a/include/ccmath/math/exponential/impl/exp_float_impl.hpp b/include/ccmath/math/exponential/impl/exp_float_impl.hpp new file mode 100644 index 0000000..2bdb038 --- /dev/null +++ b/include/ccmath/math/exponential/impl/exp_float_impl.hpp @@ -0,0 +1,87 @@ +/* + * 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/internal/helpers/exp_helpers.hpp" +#include "ccmath/internal/types/fp_types.hpp" +#include +#include + +namespace ccm::internal::impl +{ + constexpr ccm::internal::exp_data internal_exp_data_flt = ccm::internal::exp_data(); + constexpr auto exp_inv_ln2_N_flt = internal_exp_data_flt.invln2_scaled; + constexpr auto exp_shift_flt = internal_exp_data_flt.shift; + constexpr auto exp_tab_flt = internal_exp_data_flt.tab; + constexpr auto exp_poly_scaled_flt = internal_exp_data_flt.poly_scaled; + constexpr auto k_exp_table_n_flt = (1 << ccm::internal::k_exp_table_bits_flt); + + constexpr float exp_float_impl(float x) + { + std::uint32_t abs_top{}; + std::uint64_t expo_int64{}; + std::uint64_t tmp{}; + + ccm::double_t expo{}; + ccm::double_t x_dbl_t{}; + ccm::double_t scaled_input{}; + ccm::double_t rem{}; + ccm::double_t remSqr{}; + ccm::double_t result{}; + ccm::double_t scale{}; + + x_dbl_t = static_cast(x); + abs_top = ccm::support::top12_bits_of_float(x) & 0x7ff; + + if (CCM_UNLIKELY(abs_top >= ccm::support::top12_bits_of_float(88.0F))) + { + // |x| >= 88 or x is nan. + if (ccm::support::float_to_uint32(x) == ccm::support::float_to_uint32(-std::numeric_limits::infinity())) { return 0.0F; } + + if (abs_top >= ccm::support::top12_bits_of_float(std::numeric_limits::infinity())) { return x + x; } + + // Handle overflow + if (x > 0x1.62e42ep6F) // x > log(0x1p128) ~= 88.72 + { + return 0x1p97F * 0x1p97F; + } + + // Handle underflow + if (x < -0x1.9fe368p6F) // x < log(0x1p-150) ~= -103.97 + { + return 0x1.4p-75F * 0x1.4p-75F; + } + } + + // x*N/Ln2 = k + r with r in [-1/2, 1/2] and int k. + scaled_input = exp_inv_ln2_N_flt * x_dbl_t; + + /* + * Round and convert z to int, the result is in [-150*N, 128*N] and + * ideally ties-to-even rule is used, otherwise the magnitude of r + * can be bigger which gives larger approximation error. + */ + expo = ccm::helpers::narrow_eval(static_cast(scaled_input + exp_shift_flt)); // Must be double + expo_int64 = ccm::support::double_to_uint64(expo); + expo -= exp_shift_flt; + rem = scaled_input - expo; + + // exp(x) = 2^(k/N) * 2^(r/N) ~= s * (C0*r^3 + C1*r^2 + C2*r + 1) + tmp = static_cast(exp_tab_flt.at(expo_int64 % k_exp_table_n_flt)); + tmp += (expo_int64 << (52 - ccm::internal::k_exp_table_bits_flt)); + scale = ccm::support::uint64_to_double(tmp); + scaled_input = exp_poly_scaled_flt.at(0) * rem + exp_poly_scaled_flt.at(1); + remSqr = rem * rem; + result = exp_poly_scaled_flt.at(2) * rem + 1.0F; + result = scaled_input * remSqr + result; + result = scale * result; + + return static_cast(result); + } +} // namespace ccm::internal::impl diff --git a/include/ccmath/math/exponential/impl/log2_data.hpp b/include/ccmath/math/exponential/impl/log2_data.hpp new file mode 100644 index 0000000..ab648d9 --- /dev/null +++ b/include/ccmath/math/exponential/impl/log2_data.hpp @@ -0,0 +1,166 @@ +/* + * 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. + */ +// Values borrowed from gcc + +#pragma once + +#include +#include + +namespace ccm::internal +{ + // 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 TabEntry + { + double invc; + double logc; + }; + + static constexpr std::array tab = { + TabEntry{0x1.661ec79f8f3bep+0, -0x1.efec65b963019p-2}, TabEntry{0x1.571ed4aaf883dp+0, -0x1.b0b6832d4fca4p-2}, + TabEntry{0x1.49539f0f010bp+0, -0x1.7418b0a1fb77bp-2}, TabEntry{0x1.3c995b0b80385p+0, -0x1.39de91a6dcf7bp-2}, + TabEntry{0x1.30d190c8864a5p+0, -0x1.01d9bf3f2b631p-2}, TabEntry{0x1.25e227b0b8eap+0, -0x1.97c1d1b3b7afp-3}, + TabEntry{0x1.1bb4a4a1a343fp+0, -0x1.2f9e393af3c9fp-3}, TabEntry{0x1.12358f08ae5bap+0, -0x1.960cbbf788d5cp-4}, + TabEntry{0x1.0953f419900a7p+0, -0x1.a6f9db6475fcep-5}, TabEntry{0x1p+0, 0x0p+0}, + TabEntry{0x1.e608cfd9a47acp-1, 0x1.338ca9f24f53dp-4}, TabEntry{0x1.ca4b31f026aap-1, 0x1.476a9543891bap-3}, + TabEntry{0x1.b2036576afce6p-1, 0x1.e840b4ac4e4d2p-3}, TabEntry{0x1.9c2d163a1aa2dp-1, 0x1.40645f0c6651cp-2}, + TabEntry{0x1.886e6037841edp-1, 0x1.88e9c2c1b9ff8p-2}, TabEntry{0x1.767dcf5534862p-1, 0x1.ce0a44eb17bccp-2}, + }; + + std::array poly{{ + -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 TabEntry + { + double invc; + double logc; + }; + + static constexpr std::array tab = { + TabEntry{0x1.724286bb1acf8p+0, -0x1.1095feecdb000p-1}, TabEntry{0x1.6e1f766d2cca1p+0, -0x1.08494bd76d000p-1}, + TabEntry{0x1.6a13d0e30d48ap+0, -0x1.00143aee8f800p-1}, TabEntry{0x1.661ec32d06c85p+0, -0x1.efec5360b4000p-2}, + TabEntry{0x1.623fa951198f8p+0, -0x1.dfdd91ab7e000p-2}, TabEntry{0x1.5e75ba4cf026cp+0, -0x1.cffae0cc79000p-2}, + TabEntry{0x1.5ac055a214fb8p+0, -0x1.c043811fda000p-2}, TabEntry{0x1.571ed0f166e1ep+0, -0x1.b0b67323ae000p-2}, + TabEntry{0x1.53909590bf835p+0, -0x1.a152f5a2db000p-2}, TabEntry{0x1.5014fed61adddp+0, -0x1.9217f5af86000p-2}, + TabEntry{0x1.4cab88e487bd0p+0, -0x1.8304db0719000p-2}, TabEntry{0x1.49539b4334feep+0, -0x1.74189f9a9e000p-2}, + TabEntry{0x1.460cbdfafd569p+0, -0x1.6552bb5199000p-2}, TabEntry{0x1.42d664ee4b953p+0, -0x1.56b23a29b1000p-2}, + TabEntry{0x1.3fb01111dd8a6p+0, -0x1.483650f5fa000p-2}, TabEntry{0x1.3c995b70c5836p+0, -0x1.39de937f6a000p-2}, + TabEntry{0x1.3991c4ab6fd4ap+0, -0x1.2baa1538d6000p-2}, TabEntry{0x1.3698e0ce099b5p+0, -0x1.1d98340ca4000p-2}, + TabEntry{0x1.33ae48213e7b2p+0, -0x1.0fa853a40e000p-2}, TabEntry{0x1.30d191985bdb1p+0, -0x1.01d9c32e73000p-2}, + TabEntry{0x1.2e025cab271d7p+0, -0x1.e857da2fa6000p-3}, TabEntry{0x1.2b404cf13cd82p+0, -0x1.cd3c8633d8000p-3}, + TabEntry{0x1.288b02c7ccb50p+0, -0x1.b26034c14a000p-3}, TabEntry{0x1.25e2263944de5p+0, -0x1.97c1c2f4fe000p-3}, + TabEntry{0x1.234563d8615b1p+0, -0x1.7d6023f800000p-3}, TabEntry{0x1.20b46e33eaf38p+0, -0x1.633a71a05e000p-3}, + TabEntry{0x1.1e2eefdcda3ddp+0, -0x1.494f5e9570000p-3}, TabEntry{0x1.1bb4a580b3930p+0, -0x1.2f9e424e0a000p-3}, + TabEntry{0x1.19453847f2200p+0, -0x1.162595afdc000p-3}, TabEntry{0x1.16e06c0d5d73cp+0, -0x1.f9c9a75bd8000p-4}, + TabEntry{0x1.1485f47b7e4c2p+0, -0x1.c7b575bf9c000p-4}, TabEntry{0x1.12358ad0085d1p+0, -0x1.960c60ff48000p-4}, + TabEntry{0x1.0fef00f532227p+0, -0x1.64ce247b60000p-4}, TabEntry{0x1.0db2077d03a8fp+0, -0x1.33f78b2014000p-4}, + TabEntry{0x1.0b7e6d65980d9p+0, -0x1.0387d1a42c000p-4}, TabEntry{0x1.0953efe7b408dp+0, -0x1.a6f9208b50000p-5}, + TabEntry{0x1.07325cac53b83p+0, -0x1.47a954f770000p-5}, TabEntry{0x1.05197e40d1b5cp+0, -0x1.d23a8c50c0000p-6}, + TabEntry{0x1.03091c1208ea2p+0, -0x1.16a2629780000p-6}, TabEntry{0x1.0101025b37e21p+0, -0x1.720f8d8e80000p-8}, + TabEntry{0x1.fc07ef9caa76bp-1, 0x1.6fe53b1500000p-7}, TabEntry{0x1.f4465d3f6f184p-1, 0x1.11ccce10f8000p-5}, + TabEntry{0x1.ecc079f84107fp-1, 0x1.c4dfc8c8b8000p-5}, TabEntry{0x1.e573a99975ae8p-1, 0x1.3aa321e574000p-4}, + TabEntry{0x1.de5d6f0bd3de6p-1, 0x1.918a0d08b8000p-4}, TabEntry{0x1.d77b681ff38b3p-1, 0x1.e72e9da044000p-4}, + TabEntry{0x1.d0cb5724de943p-1, 0x1.1dcd2507f6000p-3}, TabEntry{0x1.ca4b2dc0e7563p-1, 0x1.476ab03dea000p-3}, + TabEntry{0x1.c3f8ee8d6cb51p-1, 0x1.7074377e22000p-3}, TabEntry{0x1.bdd2b4f020c4cp-1, 0x1.98ede8ba94000p-3}, + TabEntry{0x1.b7d6c006015cap-1, 0x1.c0db86ad2e000p-3}, TabEntry{0x1.b20366e2e338fp-1, 0x1.e840aafcee000p-3}, + TabEntry{0x1.ac57026295039p-1, 0x1.0790ab4678000p-2}, TabEntry{0x1.a6d01bc2731ddp-1, 0x1.1ac056801c000p-2}, + TabEntry{0x1.a16d3bc3ff18bp-1, 0x1.2db11d4fee000p-2}, TabEntry{0x1.9c2d14967feadp-1, 0x1.406464ec58000p-2}, + TabEntry{0x1.970e4f47c9902p-1, 0x1.52dbe093af000p-2}, TabEntry{0x1.920fb3982bcf2p-1, 0x1.651902050d000p-2}, + TabEntry{0x1.8d30187f759f1p-1, 0x1.771d2cdeaf000p-2}, TabEntry{0x1.886e5ebb9f66dp-1, 0x1.88e9c857d9000p-2}, + TabEntry{0x1.83c97b658b994p-1, 0x1.9a80155e16000p-2}, TabEntry{0x1.7f405ffc61022p-1, 0x1.abe186ed3d000p-2}, + TabEntry{0x1.7ad22181415cap-1, 0x1.bd0f2aea0e000p-2}, TabEntry{0x1.767dcf99eff8cp-1, 0x1.ce0a43dbf4000p-2}, + }; + + struct Tab2Entry + { + double chi; + double clo; + }; + + static constexpr std::array tab2 = { + Tab2Entry{0x1.6200012b90a8ep-1, 0x1.904ab0644b605p-55}, Tab2Entry{0x1.66000045734a6p-1, 0x1.1ff9bea62f7a9p-57}, + Tab2Entry{0x1.69fffc325f2c5p-1, 0x1.27ecfcb3c90bap-55}, Tab2Entry{0x1.6e00038b95a04p-1, 0x1.8ff8856739326p-55}, + Tab2Entry{0x1.71fffe09994e3p-1, 0x1.afd40275f82b1p-55}, Tab2Entry{0x1.7600015590e1p-1, -0x1.2fd75b4238341p-56}, + Tab2Entry{0x1.7a00012655bd5p-1, 0x1.808e67c242b76p-56}, Tab2Entry{0x1.7e0003259e9a6p-1, -0x1.208e426f622b7p-57}, + Tab2Entry{0x1.81fffedb4b2d2p-1, -0x1.402461ea5c92fp-55}, Tab2Entry{0x1.860002dfafcc3p-1, 0x1.df7f4a2f29a1fp-57}, + Tab2Entry{0x1.89ffff78c6b5p-1, -0x1.e0453094995fdp-55}, Tab2Entry{0x1.8e00039671566p-1, -0x1.a04f3bec77b45p-55}, + Tab2Entry{0x1.91fffe2bf1745p-1, -0x1.7fa34400e203cp-56}, Tab2Entry{0x1.95fffcc5c9fd1p-1, -0x1.6ff8005a0695dp-56}, + Tab2Entry{0x1.9a0003bba4767p-1, 0x1.0f8c4c4ec7e03p-56}, Tab2Entry{0x1.9dfffe7b92da5p-1, 0x1.e7fd9478c4602p-55}, + Tab2Entry{0x1.a1fffd72efdafp-1, -0x1.a0c554dcdae7ep-57}, Tab2Entry{0x1.a5fffde04ff95p-1, 0x1.67da98ce9b26bp-55}, + Tab2Entry{0x1.a9fffca5e8d2bp-1, -0x1.284c9b54c13dep-55}, Tab2Entry{0x1.adfffddad03eap-1, 0x1.812c8ea602e3cp-58}, + Tab2Entry{0x1.b1ffff10d3d4dp-1, -0x1.efaddad27789cp-55}, Tab2Entry{0x1.b5fffce21165ap-1, 0x1.3cb1719c61237p-58}, + Tab2Entry{0x1.b9fffd950e674p-1, 0x1.3f7d94194cep-56}, Tab2Entry{0x1.be000139ca8afp-1, 0x1.50ac4215d9bcp-56}, + Tab2Entry{0x1.c20005b46df99p-1, 0x1.beea653e9c1c9p-57}, Tab2Entry{0x1.c600040b9f7aep-1, -0x1.c079f274a70d6p-56}, + Tab2Entry{0x1.ca0006255fd8ap-1, -0x1.a0b4076e84c1fp-56}, Tab2Entry{0x1.cdfffd94c095dp-1, 0x1.8f933f99ab5d7p-55}, + Tab2Entry{0x1.d1ffff975d6cfp-1, -0x1.82c08665fe1bep-58}, Tab2Entry{0x1.d5fffa2561c93p-1, -0x1.b04289bd295f3p-56}, + Tab2Entry{0x1.d9fff9d228b0cp-1, 0x1.70251340fa236p-55}, Tab2Entry{0x1.de00065bc7e16p-1, -0x1.5011e16a4d80cp-56}, + Tab2Entry{0x1.e200002f64791p-1, 0x1.9802f09ef62ep-55}, Tab2Entry{0x1.e600057d7a6d8p-1, -0x1.e0b75580cf7fap-56}, + Tab2Entry{0x1.ea00027edc00cp-1, -0x1.c848309459811p-55}, Tab2Entry{0x1.ee0006cf5cb7cp-1, -0x1.f8027951576f4p-55}, + Tab2Entry{0x1.f2000782b7dccp-1, -0x1.f81d97274538fp-55}, Tab2Entry{0x1.f6000260c450ap-1, -0x1.071002727ffdcp-59}, + Tab2Entry{0x1.f9fffe88cd533p-1, -0x1.81bdce1fda8bp-58}, Tab2Entry{0x1.fdfffd50f8689p-1, 0x1.7f91acb918e6ep-55}, + Tab2Entry{0x1.0200004292367p+0, 0x1.b7ff365324681p-54}, Tab2Entry{0x1.05fffe3e3d668p+0, 0x1.6fa08ddae957bp-55}, + Tab2Entry{0x1.0a0000a85a757p+0, -0x1.7e2de80d3fb91p-58}, Tab2Entry{0x1.0e0001a5f3fccp+0, -0x1.1823305c5f014p-54}, + Tab2Entry{0x1.11ffff8afbaf5p+0, -0x1.bfabb6680bac2p-55}, Tab2Entry{0x1.15fffe54d91adp+0, -0x1.d7f121737e7efp-54}, + Tab2Entry{0x1.1a00011ac36e1p+0, 0x1.c000a0516f5ffp-54}, Tab2Entry{0x1.1e00019c84248p+0, -0x1.082fbe4da5dap-54}, + Tab2Entry{0x1.220000ffe5e6ep+0, -0x1.8fdd04c9cfb43p-55}, Tab2Entry{0x1.26000269fd891p+0, 0x1.cfe2a7994d182p-55}, + Tab2Entry{0x1.2a00029a6e6dap+0, -0x1.00273715e8bc5p-56}, Tab2Entry{0x1.2dfffe0293e39p+0, 0x1.b7c39dab2a6f9p-54}, + Tab2Entry{0x1.31ffff7dcf082p+0, 0x1.df1336edc5254p-56}, Tab2Entry{0x1.35ffff05a8b6p+0, -0x1.e03564ccd31ebp-54}, + Tab2Entry{0x1.3a0002e0eaeccp+0, 0x1.5f0e74bd3a477p-56}, Tab2Entry{0x1.3e000043bb236p+0, 0x1.c7dcb149d8833p-54}, + Tab2Entry{0x1.4200002d187ffp+0, 0x1.e08afcf2d3d28p-56}, Tab2Entry{0x1.460000d387cb1p+0, 0x1.20837856599a6p-55}, + Tab2Entry{0x1.4a00004569f89p+0, -0x1.9fa5c904fbcd2p-55}, Tab2Entry{0x1.4e000043543f3p+0, -0x1.81125ed175329p-56}, + Tab2Entry{0x1.51fffcc027f0fp+0, 0x1.883d8847754dcp-54}, Tab2Entry{0x1.55ffffd87b36fp+0, -0x1.709e731d02807p-55}, + Tab2Entry{0x1.59ffff21df7bap+0, 0x1.7f79f68727b02p-55}, Tab2Entry{0x1.5dfffebfc3481p+0, -0x1.180902e30e93ep-54}, + }; + }; + + template <> + struct log2_data : log2_data + { + }; +} // namespace ccm::internal diff --git a/include/ccmath/math/exponential/impl/log2_double_impl.hpp b/include/ccmath/math/exponential/impl/log2_double_impl.hpp new file mode 100644 index 0000000..1caf963 --- /dev/null +++ b/include/ccmath/math/exponential/impl/log2_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/internal/predef/unlikely.hpp" +#include "ccmath/internal/support/bits.hpp" +#include "ccmath/internal/types/fp_types.hpp" +#include "ccmath/math/compare/isnan.hpp" +#include "ccmath/math/exponential/impl/log2_data.hpp" + +#include +#include +#include +#include + +namespace ccm::internal +{ + namespace impl + { + constexpr ccm::internal::log2_data internalLog2DataDbl = ccm::internal::log2_data(); + constexpr auto log2_tab_values_dbl = ccm::internal::log2_data::tab; + constexpr auto log2_tab2_values_dbl = ccm::internal::log2_data::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; + + 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 intNorm{}; + std::uint64_t tmp{}; + + std::int64_t expo{}; + std::int64_t i{}; + + std::uint64_t intX = ccm::support::double_to_uint64(x); + std::uint32_t top = ccm::support::top16_bits_of_double(x); + + constexpr std::uint64_t low = ccm::support::double_to_uint64(1.0 - 0x1.5b51p-5); + constexpr std::uint64_t high = ccm::support::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::support::double_to_uint64(1.0))) { return 0.0; } + + rem = x - 1.0; + + ccm::double_t remHi{}; + ccm::double_t remLo{}; + remHi = ccm::support::uint64_to_double(ccm::support::double_to_uint64(rem) & -1ULL << 32); + remLo = rem - remHi; + highPart = remHi * log2_inverse_ln2_high_value_dbl; + lowPart = remLo * log2_inverse_ln2_high_value_dbl + rem * log2_inverse_ln2_low_value_dbl; + + remSqr = rem * rem; // rounding error: 0x1p-62. + remQuad = remSqr * remSqr; + + // The Worst-case error is less than 0.55 ULP + polynomialTerm = remSqr * (log2_poly1_values_dbl[0] + rem * log2_poly1_values_dbl[1]); + result = highPart + polynomialTerm; + lowPart += highPart - result + polynomialTerm; + lowPart += 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; + } + + if (CCM_UNLIKELY(top - 0x0010 >= 0x7ff0 - 0x0010)) + { + + // x is subnormal, normalize it. + intX = ccm::support::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_log2TableOff_dbl; + // NOLINTBEGIN + i = (tmp >> (52 - ccm::internal::k_log2TableBitsDbl)) % k_log2TableN_dbl; + expo = static_cast(tmp) >> 52; // Arithmetic shift. + // NOLINTEND + intNorm = intX - (tmp & 0xfffULL << 52); + inverseCoeff = log2_tab_values_dbl.at(static_cast(i)).invc; + logarithmCoeff = log2_tab_values_dbl.at(static_cast(i)).logc; + normVal = ccm::support::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 - log2_tab2_values_dbl.at(static_cast(i)).chi - log2_tab2_values_dbl.at(static_cast(i)).clo) * + inverseCoeff; + remHi = ccm::support::uint64_to_double(ccm::support::double_to_uint64(rem) & -1ULL << 32); + remLo = rem - remHi; + 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; + 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 = 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; + } + } // namespace impl + + template + [[nodiscard]] 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/math/exponential/impl/log2_float_impl.hpp b/include/ccmath/math/exponential/impl/log2_float_impl.hpp new file mode 100644 index 0000000..a48cd79 --- /dev/null +++ b/include/ccmath/math/exponential/impl/log2_float_impl.hpp @@ -0,0 +1,105 @@ +/* + * 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/internal/predef/unlikely.hpp" +#include "ccmath/internal/support/bits.hpp" +#include "ccmath/internal/types/fp_types.hpp" +#include "ccmath/math/compare/isnan.hpp" +#include "ccmath/math/exponential/impl/log2_data.hpp" + +#include +#include +#include +#include + +namespace ccm::internal +{ + namespace impl + { + constexpr ccm::internal::log2_data internalLog2DataFlt = ccm::internal::log2_data(); + constexpr auto log2_tab_values_flt = ccm::internal::log2_data::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) + { + 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::support::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) != 0U) || intX * 2 >= 0xff000000) + { + float invalidResult = (x - x) / (x - x); + return invalidResult; + } + + // If x is subnormal, normalize it + intX = ccm::support::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_log2TableOff_flt; + i = (tmp >> (23 - ccm::internal::k_log2TableBitsFlt)) % k_log2TableN_flt; // NOLINT + top = tmp & 0xff800000; + intNorm = intX - top; + // NOLINTNEXTLINE + expo = tmp >> 23; // Arithmetic shift. + inverseCoeff = log2_tab_values_flt.at(static_cast(i)).invc; + logarithmCoeff = log2_tab_values_flt.at(static_cast(i)).logc; + normVal = static_cast(ccm::support::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 = 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; + } + } // namespace impl + + template + [[nodiscard]] constexpr T log2_float(T num) noexcept + { + return static_cast(impl::log2_float_impl(static_cast(num))); + } +} // namespace ccm::internal diff --git a/include/ccmath/math/exponential/impl/log_data.hpp b/include/ccmath/math/exponential/impl/log_data.hpp new file mode 100644 index 0000000..902b14b --- /dev/null +++ b/include/ccmath/math/exponential/impl/log_data.hpp @@ -0,0 +1,238 @@ +/* + * 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. + */ +// Values borrowed from gcc +#pragma once + +#include +#include + +namespace ccm::internal +{ + // 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 TabEntry + { + double invc; + double logc; + }; + + static constexpr std::array tab = { + TabEntry{0x1.661ec79f8f3bep+0, -0x1.57bf7808caadep-2}, TabEntry{0x1.571ed4aaf883dp+0, -0x1.2bef0a7c06ddbp-2}, + TabEntry{0x1.49539f0f010bp+0, -0x1.01eae7f513a67p-2}, TabEntry{0x1.3c995b0b80385p+0, -0x1.b31d8a68224e9p-3}, + TabEntry{0x1.30d190c8864a5p+0, -0x1.6574f0ac07758p-3}, TabEntry{0x1.25e227b0b8eap+0, -0x1.1aa2bc79c81p-3}, + TabEntry{0x1.1bb4a4a1a343fp+0, -0x1.a4e76ce8c0e5ep-4}, TabEntry{0x1.12358f08ae5bap+0, -0x1.1973c5a611cccp-4}, + TabEntry{0x1.0953f419900a7p+0, -0x1.252f438e10c1ep-5}, TabEntry{0x1p+0, 0x0p+0}, + TabEntry{0x1.e608cfd9a47acp-1, 0x1.aa5aa5df25984p-5}, TabEntry{0x1.ca4b31f026aap-1, 0x1.c5e53aa362eb4p-4}, + TabEntry{0x1.b2036576afce6p-1, 0x1.526e57720db08p-3}, TabEntry{0x1.9c2d163a1aa2dp-1, 0x1.bc2860d22477p-3}, + TabEntry{0x1.886e6037841edp-1, 0x1.1058bc8a07ee1p-2}, TabEntry{0x1.767dcf5534862p-1, 0x1.4043057b6ee09p-2}, + }; + + double ln2 = 0x1.62e42fefa39efp-1; + + // The First order polynomial coefficient is 1. + std::array poly{ + -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 TabEntry + { + double invc; + double logc; + }; + + static constexpr std::array tab = { + TabEntry{0x1.734f0c3e0de9fp+0, -0x1.7cc7f79e69000p-2}, TabEntry{0x1.713786a2ce91fp+0, -0x1.76feec20d0000p-2}, + TabEntry{0x1.6f26008fab5a0p+0, -0x1.713e31351e000p-2}, TabEntry{0x1.6d1a61f138c7dp+0, -0x1.6b85b38287800p-2}, + TabEntry{0x1.6b1490bc5b4d1p+0, -0x1.65d5590807800p-2}, TabEntry{0x1.69147332f0cbap+0, -0x1.602d076180000p-2}, + TabEntry{0x1.6719f18224223p+0, -0x1.5a8ca86909000p-2}, TabEntry{0x1.6524f99a51ed9p+0, -0x1.54f4356035000p-2}, + TabEntry{0x1.63356aa8f24c4p+0, -0x1.4f637c36b4000p-2}, TabEntry{0x1.614b36b9ddc14p+0, -0x1.49da7fda85000p-2}, + TabEntry{0x1.5f66452c65c4cp+0, -0x1.445923989a800p-2}, TabEntry{0x1.5d867b5912c4fp+0, -0x1.3edf439b0b800p-2}, + TabEntry{0x1.5babccb5b90dep+0, -0x1.396ce448f7000p-2}, TabEntry{0x1.59d61f2d91a78p+0, -0x1.3401e17bda000p-2}, + TabEntry{0x1.5805612465687p+0, -0x1.2e9e2ef468000p-2}, TabEntry{0x1.56397cee76bd3p+0, -0x1.2941b3830e000p-2}, + TabEntry{0x1.54725e2a77f93p+0, -0x1.23ec58cda8800p-2}, TabEntry{0x1.52aff42064583p+0, -0x1.1e9e129279000p-2}, + TabEntry{0x1.50f22dbb2bddfp+0, -0x1.1956d2b48f800p-2}, TabEntry{0x1.4f38f4734ded7p+0, -0x1.141679ab9f800p-2}, + TabEntry{0x1.4d843cfde2840p+0, -0x1.0edd094ef9800p-2}, TabEntry{0x1.4bd3ec078a3c8p+0, -0x1.09aa518db1000p-2}, + TabEntry{0x1.4a27fc3e0258ap+0, -0x1.047e65263b800p-2}, TabEntry{0x1.4880524d48434p+0, -0x1.feb224586f000p-3}, + TabEntry{0x1.46dce1b192d0bp+0, -0x1.f474a7517b000p-3}, TabEntry{0x1.453d9d3391854p+0, -0x1.ea4443d103000p-3}, + TabEntry{0x1.43a2744b4845ap+0, -0x1.e020d44e9b000p-3}, TabEntry{0x1.420b54115f8fbp+0, -0x1.d60a22977f000p-3}, + TabEntry{0x1.40782da3ef4b1p+0, -0x1.cc00104959000p-3}, TabEntry{0x1.3ee8f5d57fe8fp+0, -0x1.c202956891000p-3}, + TabEntry{0x1.3d5d9a00b4ce9p+0, -0x1.b81178d811000p-3}, TabEntry{0x1.3bd60c010c12bp+0, -0x1.ae2c9ccd3d000p-3}, + TabEntry{0x1.3a5242b75dab8p+0, -0x1.a45402e129000p-3}, TabEntry{0x1.38d22cd9fd002p+0, -0x1.9a877681df000p-3}, + TabEntry{0x1.3755bc5847a1cp+0, -0x1.90c6d69483000p-3}, TabEntry{0x1.35dce49ad36e2p+0, -0x1.87120a645c000p-3}, + TabEntry{0x1.34679984dd440p+0, -0x1.7d68fb4143000p-3}, TabEntry{0x1.32f5cceffcb24p+0, -0x1.73cb83c627000p-3}, + TabEntry{0x1.3187775a10d49p+0, -0x1.6a39a9b376000p-3}, TabEntry{0x1.301c8373e3990p+0, -0x1.60b3154b7a000p-3}, + TabEntry{0x1.2eb4ebb95f841p+0, -0x1.5737d76243000p-3}, TabEntry{0x1.2d50a0219a9d1p+0, -0x1.4dc7b8fc23000p-3}, + TabEntry{0x1.2bef9a8b7fd2ap+0, -0x1.4462c51d20000p-3}, TabEntry{0x1.2a91c7a0c1babp+0, -0x1.3b08abc830000p-3}, + TabEntry{0x1.293726014b530p+0, -0x1.31b996b490000p-3}, TabEntry{0x1.27dfa5757a1f5p+0, -0x1.2875490a44000p-3}, + TabEntry{0x1.268b39b1d3bbfp+0, -0x1.1f3b9f879a000p-3}, TabEntry{0x1.2539d838ff5bdp+0, -0x1.160c8252ca000p-3}, + TabEntry{0x1.23eb7aac9083bp+0, -0x1.0ce7f57f72000p-3}, TabEntry{0x1.22a012ba940b6p+0, -0x1.03cdc49fea000p-3}, + TabEntry{0x1.2157996cc4132p+0, -0x1.f57bdbc4b8000p-4}, TabEntry{0x1.201201dd2fc9bp+0, -0x1.e370896404000p-4}, + TabEntry{0x1.1ecf4494d480bp+0, -0x1.d17983ef94000p-4}, TabEntry{0x1.1d8f5528f6569p+0, -0x1.bf9674ed8a000p-4}, + TabEntry{0x1.1c52311577e7cp+0, -0x1.adc79202f6000p-4}, TabEntry{0x1.1b17c74cb26e9p+0, -0x1.9c0c3e7288000p-4}, + TabEntry{0x1.19e010c2c1ab6p+0, -0x1.8a646b372c000p-4}, TabEntry{0x1.18ab07bb670bdp+0, -0x1.78d01b3ac0000p-4}, + TabEntry{0x1.1778a25efbcb6p+0, -0x1.674f145380000p-4}, TabEntry{0x1.1648d354c31dap+0, -0x1.55e0e6d878000p-4}, + TabEntry{0x1.151b990275fddp+0, -0x1.4485cdea1e000p-4}, TabEntry{0x1.13f0ea432d24cp+0, -0x1.333d94d6aa000p-4}, + TabEntry{0x1.12c8b7210f9dap+0, -0x1.22079f8c56000p-4}, TabEntry{0x1.11a3028ecb531p+0, -0x1.10e4698622000p-4}, + TabEntry{0x1.107fbda8434afp+0, -0x1.ffa6c6ad20000p-5}, TabEntry{0x1.0f5ee0f4e6bb3p+0, -0x1.dda8d4a774000p-5}, + TabEntry{0x1.0e4065d2a9fcep+0, -0x1.bbcece4850000p-5}, TabEntry{0x1.0d244632ca521p+0, -0x1.9a1894012c000p-5}, + TabEntry{0x1.0c0a77ce2981ap+0, -0x1.788583302c000p-5}, TabEntry{0x1.0af2f83c636d1p+0, -0x1.5715e67d68000p-5}, + TabEntry{0x1.09ddb98a01339p+0, -0x1.35c8a49658000p-5}, TabEntry{0x1.08cabaf52e7dfp+0, -0x1.149e364154000p-5}, + TabEntry{0x1.07b9f2f4e28fbp+0, -0x1.e72c082eb8000p-6}, TabEntry{0x1.06ab58c358f19p+0, -0x1.a55f152528000p-6}, + TabEntry{0x1.059eea5ecf92cp+0, -0x1.63d62cf818000p-6}, TabEntry{0x1.04949cdd12c90p+0, -0x1.228fb8caa0000p-6}, + TabEntry{0x1.038c6c6f0ada9p+0, -0x1.c317b20f90000p-7}, TabEntry{0x1.02865137932a9p+0, -0x1.419355daa0000p-7}, + TabEntry{0x1.0182427ea7348p+0, -0x1.81203c2ec0000p-8}, TabEntry{0x1.008040614b195p+0, -0x1.0040979240000p-9}, + TabEntry{0x1.fe01ff726fa1ap-1, 0x1.feff384900000p-9}, TabEntry{0x1.fa11cc261ea74p-1, 0x1.7dc41353d0000p-7}, + TabEntry{0x1.f6310b081992ep-1, 0x1.3cea3c4c28000p-6}, TabEntry{0x1.f25f63ceeadcdp-1, 0x1.b9fc114890000p-6}, + TabEntry{0x1.ee9c8039113e7p-1, 0x1.1b0d8ce110000p-5}, TabEntry{0x1.eae8078cbb1abp-1, 0x1.58a5bd001c000p-5}, + TabEntry{0x1.e741aa29d0c9bp-1, 0x1.95c8340d88000p-5}, TabEntry{0x1.e3a91830a99b5p-1, 0x1.d276aef578000p-5}, + TabEntry{0x1.e01e009609a56p-1, 0x1.07598e598c000p-4}, TabEntry{0x1.dca01e577bb98p-1, 0x1.253f5e30d2000p-4}, + TabEntry{0x1.d92f20b7c9103p-1, 0x1.42edd8b380000p-4}, TabEntry{0x1.d5cac66fb5ccep-1, 0x1.606598757c000p-4}, + TabEntry{0x1.d272caa5ede9dp-1, 0x1.7da76356a0000p-4}, TabEntry{0x1.cf26e3e6b2ccdp-1, 0x1.9ab434e1c6000p-4}, + TabEntry{0x1.cbe6da2a77902p-1, 0x1.b78c7bb0d6000p-4}, TabEntry{0x1.c8b266d37086dp-1, 0x1.d431332e72000p-4}, + TabEntry{0x1.c5894bd5d5804p-1, 0x1.f0a3171de6000p-4}, TabEntry{0x1.c26b533bb9f8cp-1, 0x1.067152b914000p-3}, + TabEntry{0x1.bf583eeece73fp-1, 0x1.147858292b000p-3}, TabEntry{0x1.bc4fd75db96c1p-1, 0x1.2266ecdca3000p-3}, + TabEntry{0x1.b951e0c864a28p-1, 0x1.303d7a6c55000p-3}, TabEntry{0x1.b65e2c5ef3e2cp-1, 0x1.3dfc33c331000p-3}, + TabEntry{0x1.b374867c9888bp-1, 0x1.4ba366b7a8000p-3}, TabEntry{0x1.b094b211d304ap-1, 0x1.5933928d1f000p-3}, + TabEntry{0x1.adbe885f2ef7ep-1, 0x1.66acd2418f000p-3}, TabEntry{0x1.aaf1d31603da2p-1, 0x1.740f8ec669000p-3}, + TabEntry{0x1.a82e63fd358a7p-1, 0x1.815c0f51af000p-3}, TabEntry{0x1.a5740ef09738bp-1, 0x1.8e92954f68000p-3}, + TabEntry{0x1.a2c2a90ab4b27p-1, 0x1.9bb3602f84000p-3}, TabEntry{0x1.a01a01393f2d1p-1, 0x1.a8bed1c2c0000p-3}, + TabEntry{0x1.9d79f24db3c1bp-1, 0x1.b5b515c01d000p-3}, TabEntry{0x1.9ae2505c7b190p-1, 0x1.c2967ccbcc000p-3}, + TabEntry{0x1.9852ef297ce2fp-1, 0x1.cf635d5486000p-3}, TabEntry{0x1.95cbaeea44b75p-1, 0x1.dc1bd3446c000p-3}, + TabEntry{0x1.934c69de74838p-1, 0x1.e8c01b8cfe000p-3}, TabEntry{0x1.90d4f2f6752e6p-1, 0x1.f5509c0179000p-3}, + TabEntry{0x1.8e6528effd79dp-1, 0x1.00e6c121fb800p-2}, TabEntry{0x1.8bfce9fcc007cp-1, 0x1.071b80e93d000p-2}, + TabEntry{0x1.899c0dabec30ep-1, 0x1.0d46b9e867000p-2}, TabEntry{0x1.87427aa2317fbp-1, 0x1.13687334bd000p-2}, + TabEntry{0x1.84f00acb39a08p-1, 0x1.1980d67234800p-2}, TabEntry{0x1.82a49e8653e55p-1, 0x1.1f8ffe0cc8000p-2}, + TabEntry{0x1.8060195f40260p-1, 0x1.2595fd7636800p-2}, TabEntry{0x1.7e22563e0a329p-1, 0x1.2b9300914a800p-2}, + TabEntry{0x1.7beb377dcb5adp-1, 0x1.3187210436000p-2}, TabEntry{0x1.79baa679725c2p-1, 0x1.377266dec1800p-2}, + TabEntry{0x1.77907f2170657p-1, 0x1.3d54ffbaf3000p-2}, + }; + + struct Tab2Entry + { + double chi; + double clo; + }; + + static constexpr std::array tab2 = { + Tab2Entry{0x1.61000014fb66bp-1, 0x1.e026c91425b3cp-56}, Tab2Entry{0x1.63000034db495p-1, 0x1.dbfea48005d41p-55}, + Tab2Entry{0x1.650000d94d478p-1, 0x1.e7fa786d6a5b7p-55}, Tab2Entry{0x1.67000074e6fadp-1, 0x1.1fcea6b54254cp-57}, + Tab2Entry{0x1.68ffffedf0faep-1, -0x1.c7e274c590efdp-56}, Tab2Entry{0x1.6b0000763c5bcp-1, -0x1.ac16848dcda01p-55}, + Tab2Entry{0x1.6d0001e5cc1f6p-1, 0x1.33f1c9d499311p-55}, Tab2Entry{0x1.6efffeb05f63ep-1, -0x1.e80041ae22d53p-56}, + Tab2Entry{0x1.710000e86978p-1, 0x1.bff6671097952p-56}, Tab2Entry{0x1.72ffffc67e912p-1, 0x1.c00e226bd8724p-55}, + Tab2Entry{0x1.74fffdf81116ap-1, -0x1.e02916ef101d2p-57}, Tab2Entry{0x1.770000f679c9p-1, -0x1.7fc71cd549c74p-57}, + Tab2Entry{0x1.78ffffa7ec835p-1, 0x1.1bec19ef50483p-55}, Tab2Entry{0x1.7affffe20c2e6p-1, -0x1.07e1729cc6465p-56}, + Tab2Entry{0x1.7cfffed3fc9p-1, -0x1.08072087b8b1cp-55}, Tab2Entry{0x1.7efffe9261a76p-1, 0x1.dc0286d9df9aep-55}, + Tab2Entry{0x1.81000049ca3e8p-1, 0x1.97fd251e54c33p-55}, Tab2Entry{0x1.8300017932c8fp-1, -0x1.afee9b630f381p-55}, + Tab2Entry{0x1.850000633739cp-1, 0x1.9bfbf6b6535bcp-55}, Tab2Entry{0x1.87000204289c6p-1, -0x1.bbf65f3117b75p-55}, + Tab2Entry{0x1.88fffebf57904p-1, -0x1.9006ea23dcb57p-55}, Tab2Entry{0x1.8b00022bc04dfp-1, -0x1.d00df38e04b0ap-56}, + Tab2Entry{0x1.8cfffe50c1b8ap-1, -0x1.8007146ff9f05p-55}, Tab2Entry{0x1.8effffc918e43p-1, 0x1.3817bd07a7038p-55}, + Tab2Entry{0x1.910001efa5fc7p-1, 0x1.93e9176dfb403p-55}, Tab2Entry{0x1.9300013467bb9p-1, 0x1.f804e4b980276p-56}, + Tab2Entry{0x1.94fffe6ee076fp-1, -0x1.f7ef0d9ff622ep-55}, Tab2Entry{0x1.96fffde3c12d1p-1, -0x1.082aa962638bap-56}, + Tab2Entry{0x1.98ffff4458a0dp-1, -0x1.7801b9164a8efp-55}, Tab2Entry{0x1.9afffdd982e3ep-1, -0x1.740e08a5a9337p-55}, + Tab2Entry{0x1.9cfffed49fb66p-1, 0x1.fce08c19bep-60}, Tab2Entry{0x1.9f00020f19c51p-1, -0x1.a3faa27885b0ap-55}, + Tab2Entry{0x1.a10001145b006p-1, 0x1.4ff489958da56p-56}, Tab2Entry{0x1.a300007bbf6fap-1, 0x1.cbeab8a2b6d18p-55}, + Tab2Entry{0x1.a500010971d79p-1, 0x1.8fecadd78793p-55}, Tab2Entry{0x1.a70001df52e48p-1, -0x1.f41763dd8abdbp-55}, + Tab2Entry{0x1.a90001c593352p-1, -0x1.ebf0284c27612p-55}, Tab2Entry{0x1.ab0002a4f3e4bp-1, -0x1.9fd043cff3f5fp-57}, + Tab2Entry{0x1.acfffd7ae1ed1p-1, -0x1.23ee7129070b4p-55}, Tab2Entry{0x1.aefffee510478p-1, 0x1.a063ee00edea3p-57}, + Tab2Entry{0x1.b0fffdb650d5bp-1, 0x1.a06c8381f0ab9p-58}, Tab2Entry{0x1.b2ffffeaaca57p-1, -0x1.9011e74233c1dp-56}, + Tab2Entry{0x1.b4fffd995badcp-1, -0x1.9ff1068862a9fp-56}, Tab2Entry{0x1.b7000249e659cp-1, 0x1.aff45d0864f3ep-55}, + Tab2Entry{0x1.b8ffff987164p-1, 0x1.cfe7796c2c3f9p-56}, Tab2Entry{0x1.bafffd204cb4fp-1, -0x1.3ff27eef22bc4p-57}, + Tab2Entry{0x1.bcfffd2415c45p-1, -0x1.cffb7ee3bea21p-57}, Tab2Entry{0x1.beffff86309dfp-1, -0x1.14103972e0b5cp-55}, + Tab2Entry{0x1.c0fffe1b57653p-1, 0x1.bc16494b76a19p-55}, Tab2Entry{0x1.c2ffff1fa57e3p-1, -0x1.4feef8d30c6edp-57}, + Tab2Entry{0x1.c4fffdcbfe424p-1, -0x1.43f68bcec4775p-55}, Tab2Entry{0x1.c6fffed54b9f7p-1, 0x1.47ea3f053e0ecp-55}, + Tab2Entry{0x1.c8fffeb998fd5p-1, 0x1.383068df992f1p-56}, Tab2Entry{0x1.cb0002125219ap-1, -0x1.8fd8e64180e04p-57}, + Tab2Entry{0x1.ccfffdd94469cp-1, 0x1.e7ebe1cc7ea72p-55}, Tab2Entry{0x1.cefffeafdc476p-1, 0x1.ebe39ad9f88fep-55}, + Tab2Entry{0x1.d1000169af82bp-1, 0x1.57d91a8b95a71p-56}, Tab2Entry{0x1.d30000d0ff71dp-1, 0x1.9c1906970c7dap-55}, + Tab2Entry{0x1.d4fffea790fc4p-1, -0x1.80e37c558fe0cp-58}, Tab2Entry{0x1.d70002edc87e5p-1, -0x1.f80d64dc10f44p-56}, + Tab2Entry{0x1.d900021dc82aap-1, -0x1.47c8f94fd5c5cp-56}, Tab2Entry{0x1.dafffd86b0283p-1, 0x1.c7f1dc521617ep-55}, + Tab2Entry{0x1.dd000296c4739p-1, 0x1.8019eb2ffb153p-55}, Tab2Entry{0x1.defffe54490f5p-1, 0x1.e00d2c652cc89p-57}, + Tab2Entry{0x1.e0fffcdabf694p-1, -0x1.f8340202d69d2p-56}, Tab2Entry{0x1.e2fffdb52c8ddp-1, 0x1.b00c1ca1b0864p-56}, + Tab2Entry{0x1.e4ffff24216efp-1, 0x1.2ffa8b094ab51p-56}, Tab2Entry{0x1.e6fffe88a5e11p-1, -0x1.7f673b1efbe59p-58}, + Tab2Entry{0x1.e9000119eff0dp-1, -0x1.4808d5e0bc801p-55}, Tab2Entry{0x1.eafffdfa51744p-1, 0x1.80006d54320b5p-56}, + Tab2Entry{0x1.ed0001a127fa1p-1, -0x1.002f860565c92p-58}, Tab2Entry{0x1.ef00007babcc4p-1, -0x1.540445d35e611p-55}, + Tab2Entry{0x1.f0ffff57a8d02p-1, -0x1.ffb3139ef9105p-59}, Tab2Entry{0x1.f30001ee58ac7p-1, 0x1.a81acf2731155p-55}, + Tab2Entry{0x1.f4ffff5823494p-1, 0x1.a3f41d4d7c743p-55}, Tab2Entry{0x1.f6ffffca94c6bp-1, -0x1.202f41c987875p-57}, + Tab2Entry{0x1.f8fffe1f9c441p-1, 0x1.77dd1f477e74bp-56}, Tab2Entry{0x1.fafffd2e0e37ep-1, -0x1.f01199a7ca331p-57}, + Tab2Entry{0x1.fd0001c77e49ep-1, 0x1.181ee4bceacb1p-56}, Tab2Entry{0x1.feffff7e0c331p-1, -0x1.e05370170875ap-57}, + Tab2Entry{0x1.00ffff465606ep+0, -0x1.a7ead491c0adap-55}, Tab2Entry{0x1.02ffff3867a58p+0, -0x1.77f69c3fcb2ep-54}, + Tab2Entry{0x1.04ffffdfc0d17p+0, 0x1.7bffe34cb945bp-54}, Tab2Entry{0x1.0700003cd4d82p+0, 0x1.20083c0e456cbp-55}, + Tab2Entry{0x1.08ffff9f2cbe8p+0, -0x1.dffdfbe37751ap-57}, Tab2Entry{0x1.0b000010cda65p+0, -0x1.13f7faee626ebp-54}, + Tab2Entry{0x1.0d00001a4d338p+0, 0x1.07dfa79489ff7p-55}, Tab2Entry{0x1.0effffadafdfdp+0, -0x1.7040570d66bcp-56}, + Tab2Entry{0x1.110000bbafd96p+0, 0x1.e80d4846d0b62p-55}, Tab2Entry{0x1.12ffffae5f45dp+0, 0x1.dbffa64fd36efp-54}, + Tab2Entry{0x1.150000dd59ad9p+0, 0x1.a0077701250aep-54}, Tab2Entry{0x1.170000f21559ap+0, 0x1.dfdf9e2e3deeep-55}, + Tab2Entry{0x1.18ffffc275426p+0, 0x1.10030dc3b7273p-54}, Tab2Entry{0x1.1b000123d3c59p+0, 0x1.97f7980030188p-54}, + Tab2Entry{0x1.1cffff8299eb7p+0, -0x1.5f932ab9f8c67p-57}, Tab2Entry{0x1.1effff48ad4p+0, 0x1.37fbf9da75bebp-54}, + Tab2Entry{0x1.210000c8b86a4p+0, 0x1.f806b91fd5b22p-54}, Tab2Entry{0x1.2300003854303p+0, 0x1.3ffc2eb9fbf33p-54}, + Tab2Entry{0x1.24fffffbcf684p+0, 0x1.601e77e2e2e72p-56}, Tab2Entry{0x1.26ffff52921d9p+0, 0x1.ffcbb767f0c61p-56}, + Tab2Entry{0x1.2900014933a3cp+0, -0x1.202ca3c02412bp-56}, Tab2Entry{0x1.2b00014556313p+0, -0x1.2808233f21f02p-54}, + Tab2Entry{0x1.2cfffebfe523bp+0, -0x1.8ff7e384fdcf2p-55}, Tab2Entry{0x1.2f0000bb8ad96p+0, -0x1.5ff51503041c5p-55}, + Tab2Entry{0x1.30ffffb7ae2afp+0, -0x1.10071885e289dp-55}, Tab2Entry{0x1.32ffffeac5f7fp+0, -0x1.1ff5d3fb7b715p-54}, + Tab2Entry{0x1.350000ca66756p+0, 0x1.57f82228b82bdp-54}, Tab2Entry{0x1.3700011fbf721p+0, 0x1.000bac40dd5ccp-55}, + Tab2Entry{0x1.38ffff9592fb9p+0, -0x1.43f9d2db2a751p-54}, Tab2Entry{0x1.3b00004ddd242p+0, 0x1.57f6b707638e1p-55}, + Tab2Entry{0x1.3cffff5b2c957p+0, 0x1.a023a10bf1231p-56}, Tab2Entry{0x1.3efffeab0b418p+0, 0x1.87f6d66b152bp-54}, + Tab2Entry{0x1.410001532aff4p+0, 0x1.7f8375f198524p-57}, Tab2Entry{0x1.4300017478b29p+0, 0x1.301e672dc5143p-55}, + Tab2Entry{0x1.44fffe795b463p+0, 0x1.9ff69b8b2895ap-55}, Tab2Entry{0x1.46fffe80475ep+0, -0x1.5c0b19bc2f254p-54}, + Tab2Entry{0x1.48fffef6fc1e7p+0, 0x1.b4009f23a2a72p-54}, Tab2Entry{0x1.4afffe5bea704p+0, -0x1.4ffb7bf0d7d45p-54}, + Tab2Entry{0x1.4d000171027dep+0, -0x1.9c06471dc6a3dp-54}, Tab2Entry{0x1.4f0000ff03ee2p+0, 0x1.77f890b85531cp-54}, + Tab2Entry{0x1.5100012dc4bd1p+0, 0x1.004657166a436p-57}, Tab2Entry{0x1.530001605277ap+0, -0x1.6bfcece233209p-54}, + Tab2Entry{0x1.54fffecdb704cp+0, -0x1.902720505a1d7p-55}, Tab2Entry{0x1.56fffef5f54a9p+0, 0x1.bbfe60ec96412p-54}, + Tab2Entry{0x1.5900017e61012p+0, 0x1.87ec581afef9p-55}, Tab2Entry{0x1.5b00003c93e92p+0, -0x1.f41080abf0ccp-54}, + Tab2Entry{0x1.5d0001d4919bcp+0, -0x1.8812afb254729p-54}, Tab2Entry{0x1.5efffe7b87a89p+0, -0x1.47eb780ed6904p-54}, + }; + }; + + template <> + struct log_data : log_data + { + }; +} // namespace ccm::internal diff --git a/include/ccmath/math/exponential/impl/log_double_impl.hpp b/include/ccmath/math/exponential/impl/log_double_impl.hpp new file mode 100644 index 0000000..d6a72ff --- /dev/null +++ b/include/ccmath/math/exponential/impl/log_double_impl.hpp @@ -0,0 +1,151 @@ +/* + * 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/internal/predef/unlikely.hpp" +#include "ccmath/internal/support/bits.hpp" +#include "ccmath/internal/types/fp_types.hpp" +#include "ccmath/math/compare/isnan.hpp" +#include "ccmath/math/exponential/impl/log_data.hpp" + +#include +#include +#include +#include + +namespace ccm::internal +{ + namespace impl + { + constexpr ccm::internal::log_data internalLogDataDbl = ccm::internal::log_data(); + constexpr auto log_tab_values_dbl = ccm::internal::log_data::tab; + constexpr auto log_tab2_values_dbl = ccm::internal::log_data::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; + + 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 intNorm{}; + std::uint64_t tmp{}; + + // Declare variables for exponent and loop iteration + std::int64_t expo{}; + std::int64_t i{}; + + // Convert input double to uint64_t and extract top 16 bits + std::uint64_t intX = ccm::support::double_to_uint64(x); + std::uint32_t top = ccm::support::top16_bits_of_double(x); + + // Constants for comparison + constexpr std::uint64_t low = ccm::support::double_to_uint64(1.0 - 0x1p-4); + constexpr std::uint64_t high = ccm::support::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::support::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 * (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 * log_poly1_values_dbl[0]; // poly1_values[0] == -0.5. + highPart = rem + workspace; + lowPart = rem - highPart + workspace; + lowPart += log_poly1_values_dbl[0] * rlo * (rhi + rem); + result += lowPart; + result += highPart; + return 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::support::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 = log_tab_values_dbl.at(static_cast(i)).invc; + logarithmCoeff = log_tab_values_dbl.at(static_cast(i)).logc; + normVal = ccm::support::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 - log_tab2_values_dbl.at(static_cast(i)).chi - log_tab2_values_dbl.at(static_cast(i)).clo) * + inverseCoeff; + scaleFactor = static_cast(expo); + + // Calculate high and low parts of logarithm + // hi + lo = r + log(c) + expo*Ln2. + workspace = scaleFactor * log_ln2hi_value_dbl + logarithmCoeff; + highPart = workspace + rem; + 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 * 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 result; + } + } // namespace impl + + template + [[nodiscard]] 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/math/exponential/impl/log_float_impl.hpp b/include/ccmath/math/exponential/impl/log_float_impl.hpp new file mode 100644 index 0000000..721403f --- /dev/null +++ b/include/ccmath/math/exponential/impl/log_float_impl.hpp @@ -0,0 +1,94 @@ +/* + * 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/internal/predef/unlikely.hpp" +#include "ccmath/internal/support/bits.hpp" +#include "ccmath/internal/types/fp_types.hpp" +#include "ccmath/math/compare/isnan.hpp" +#include "ccmath/math/exponential/impl/log_data.hpp" + +#include +#include +#include +#include + +namespace ccm::internal +{ + namespace impl + { + constexpr ccm::internal::log_data internalLogDataFlt = ccm::internal::log_data(); + constexpr auto log_tab_values_flt = ccm::internal::log_data::tab; + constexpr auto log_poly_values_flt = internalLogDataFlt.poly; + constexpr auto log_ln2_value_flt = internalLogDataFlt.ln2; + constexpr auto k_logTableN_flt = (1 << ccm::internal::k_logTableBitsFlt); + constexpr auto k_logTableOff_flt = 0x3f330000; + + 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::support::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 then normalize it. + intX = ccm::support::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 = log_tab_values_flt.at(static_cast(i)).invc; + logarithmCoeff = log_tab_values_flt.at(static_cast(i)).logc; + normVal = static_cast(ccm::support::uint32_to_float(intNorm)); + + // log(x) = log1p(normVal / inverseCoeff - 1) + log(inverseCoeff) + expo * Ln2 + rem = normVal * inverseCoeff - 1; + result0 = logarithmCoeff + static_cast(expo) * log_ln2_value_flt; + + // Polynomial approximation for log1p(rem) + remSqr = rem * rem; + result = log_poly_values_flt[1] * rem + log_poly_values_flt[2]; + result = log_poly_values_flt[0] * remSqr + result; + result = result * remSqr + (result0 + rem); + + return static_cast(result); + } + + } // namespace impl + + template + [[nodiscard]] 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/log.hpp b/include/ccmath/math/exponential/log.hpp similarity index 83% rename from include/ccmath/detail/exponential/log.hpp rename to include/ccmath/math/exponential/log.hpp index 2226223..aac424f 100644 --- a/include/ccmath/detail/exponential/log.hpp +++ b/include/ccmath/math/exponential/log.hpp @@ -8,8 +8,8 @@ #pragma once -#include "ccmath/detail/exponential/impl/log_double_impl.hpp" -#include "ccmath/detail/exponential/impl/log_float_impl.hpp" +#include "ccmath/math/exponential/impl/log_double_impl.hpp" +#include "ccmath/math/exponential/impl/log_float_impl.hpp" namespace ccm { @@ -22,22 +22,22 @@ 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 , bool> = true> - inline constexpr T log(const T num) noexcept + 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 ±0, -∞ is returned. + if (num == static_cast(0)) { return -std::numeric_limits::infinity(); } + // 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 (CCM_UNLIKELY(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(); } + if (CCM_UNLIKELY(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); } @@ -51,7 +51,7 @@ namespace ccm * @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 + constexpr double log(const Integer num) noexcept { return ccm::log(static_cast(num)); } @@ -61,7 +61,7 @@ namespace ccm * @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 + constexpr float logf(const float num) noexcept { return ccm::log(num); } @@ -71,7 +71,7 @@ namespace ccm * @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 + constexpr double logl(const double num) noexcept { return ccm::log(num); } diff --git a/include/ccmath/detail/exponential/log10.hpp b/include/ccmath/math/exponential/log10.hpp similarity index 100% rename from include/ccmath/detail/exponential/log10.hpp rename to include/ccmath/math/exponential/log10.hpp diff --git a/include/ccmath/detail/exponential/log1p.hpp b/include/ccmath/math/exponential/log1p.hpp similarity index 100% rename from include/ccmath/detail/exponential/log1p.hpp rename to include/ccmath/math/exponential/log1p.hpp diff --git a/include/ccmath/detail/exponential/log2.hpp b/include/ccmath/math/exponential/log2.hpp similarity index 72% rename from include/ccmath/detail/exponential/log2.hpp rename to include/ccmath/math/exponential/log2.hpp index c1efed9..69d7664 100644 --- a/include/ccmath/detail/exponential/log2.hpp +++ b/include/ccmath/math/exponential/log2.hpp @@ -8,10 +8,10 @@ #pragma once -#include "ccmath/detail/compare/isnan.hpp" -#include "ccmath/detail/compare/signbit.hpp" -#include "ccmath/detail/exponential/impl/log2_double_impl.hpp" -#include "ccmath/detail/exponential/impl/log2_float_impl.hpp" +#include "ccmath/math/compare/isnan.hpp" +#include "ccmath/math/compare/signbit.hpp" +#include "ccmath/math/exponential/impl/log2_double_impl.hpp" +#include "ccmath/math/exponential/impl/log2_float_impl.hpp" #include #include @@ -25,7 +25,7 @@ namespace ccm * @return The base 2 logarithm of the number. */ template , int> = 0> - inline constexpr T log2(T num) noexcept + constexpr T log2(T num) noexcept { // If the argument is ±0, -∞ is returned if (num == static_cast(0)) { return -std::numeric_limits::infinity(); } @@ -43,26 +43,27 @@ namespace ccm if (ccm::isnan(num)) { if (ccm::signbit(num)) { return -std::numeric_limits::quiet_NaN(); } - else { return std::numeric_limits::quiet_NaN(); } + return std::numeric_limits::quiet_NaN(); } - // We can not handle long double at this time due to problems with long double being platform dependent with its bit size. + // We cannot handle long double at this time due to problems + // with long double being platform-dependent with its bit size. 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 + constexpr double log2(Integer num) noexcept { return ccm::log2(static_cast(num)); } - inline constexpr float log2f(float num) + constexpr float log2f(float num) { return ccm::log2(num); } - inline constexpr long double log2l(long double num) + constexpr long double log2l(long double num) { return ccm::log2(num); } diff --git a/include/ccmath/detail/fmanip/copysign.hpp b/include/ccmath/math/fmanip/copysign.hpp similarity index 61% rename from include/ccmath/detail/fmanip/copysign.hpp rename to include/ccmath/math/fmanip/copysign.hpp index cdcfe54..4402d5c 100644 --- a/include/ccmath/detail/fmanip/copysign.hpp +++ b/include/ccmath/math/fmanip/copysign.hpp @@ -8,9 +8,9 @@ #pragma once -#include "ccmath/detail/basic/abs.hpp" -#include "ccmath/detail/compare/isnan.hpp" -#include "ccmath/detail/compare/signbit.hpp" +#include "ccmath/math/basic/abs.hpp" +#include "ccmath/math/compare/isnan.hpp" +#include "ccmath/math/compare/signbit.hpp" namespace ccm { @@ -22,12 +22,12 @@ namespace ccm * @return If no errors occur, the floating point value with the magnitude of mag and the sign of sgn is returned. */ template , bool> = true> - inline constexpr T copysign(T mag, T sgn) + constexpr T copysign(T mag, T sgn) { if (ccm::isnan(mag) || ccm::isnan(sgn)) { if (ccm::signbit(sgn)) { return -std::numeric_limits::quiet_NaN(); } - else { return std::numeric_limits::quiet_NaN(); } + return std::numeric_limits::quiet_NaN(); } T sign_bit = ccm::signbit(sgn) ? -1 : 1; @@ -42,29 +42,29 @@ namespace ccm * @return If no errors occur, the floating point value with the magnitude of mag and the sign of sgn is returned. */ template , bool> = true> - inline constexpr double copysign(Integer mag, Integer sgn) + constexpr double copysign(Integer mag, Integer sgn) { return copysign(static_cast(mag), static_cast(sgn)); } /** - * @brief Copies the sign of a floating point value. - * @param x A floating-point. - * @param y A floating-point. - * @return If no errors occur, the floating point value with the magnitude of mag and the sign of sgn is returned. - */ - inline constexpr float copysignf(float mag, float sgn) + * @brief Copies the sign of a floating point value. + * @param x A floating-point. + * @param y A floating-point. + * @return If no errors occur, the floating point value with the magnitude of mag and the sign of sgn is returned. + */ + constexpr float copysignf(float mag, float sgn) { return copysign(mag, sgn); } /** - * @brief Copies the sign of a floating point value. - * @param x A floating-point. - * @param y A floating-point. - * @return If no errors occur, the floating point value with the magnitude of mag and the sign of sgn is returned. + * @brief Copies the sign of a floating point value. + * @param x A floating-point. + * @param y A floating-point. + * @return If no errors occur, the floating point value with the magnitude of mag and the sign of sgn is returned. */ - inline constexpr long double copysignl(long double mag, long double sgn) + constexpr long double copysignl(long double mag, long double sgn) { return copysign(mag, sgn); } diff --git a/include/ccmath/detail/fmanip/frexp.hpp b/include/ccmath/math/fmanip/frexp.hpp similarity index 100% rename from include/ccmath/detail/fmanip/frexp.hpp rename to include/ccmath/math/fmanip/frexp.hpp diff --git a/include/ccmath/detail/fmanip/ilogb.hpp b/include/ccmath/math/fmanip/ilogb.hpp similarity index 100% rename from include/ccmath/detail/fmanip/ilogb.hpp rename to include/ccmath/math/fmanip/ilogb.hpp diff --git a/include/ccmath/math/fmanip/impl/scalbn_double_impl.hpp b/include/ccmath/math/fmanip/impl/scalbn_double_impl.hpp new file mode 100644 index 0000000..a8c5fb2 --- /dev/null +++ b/include/ccmath/math/fmanip/impl/scalbn_double_impl.hpp @@ -0,0 +1,53 @@ +/* + * 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/internal/support/bits.hpp" +#include "ccmath/internal/types/fp_types.hpp" +#include "ccmath/math/compare/isinf.hpp" +#include "ccmath/math/compare/isnan.hpp" + +#include +#include + +namespace ccm::internal::impl +{ + constexpr double scalbn_double_impl(double arg, int exp) noexcept + { + ccm::double_t tmp = arg; + + if (exp > 1023) + { + tmp *= 0x1p1023; + exp -= 1023; + if (exp > 1023) + { + tmp *= 0x1p1023; + exp -= 1023; + if (exp > 1023) { exp = 1023; } + } + } + else if (exp < -1022) + { + tmp *= 0x1p-1022 * 0x1p53; + exp += 1022 - 53; + if (exp < -1022) + { + tmp *= 0x1p-1022 * 0x1p53; + exp += 1022 - 53; + if (exp < -1022) { exp = -1022; } + } + } + + const std::uint64_t bits = ccm::support::bit_cast(1023 + static_cast(exp)) << 52; + arg = tmp * ccm::support::bit_cast(bits); + + return arg; + } +} // namespace ccm::internal::impl diff --git a/include/ccmath/math/fmanip/impl/scalbn_float_impl.hpp b/include/ccmath/math/fmanip/impl/scalbn_float_impl.hpp new file mode 100644 index 0000000..e137f85 --- /dev/null +++ b/include/ccmath/math/fmanip/impl/scalbn_float_impl.hpp @@ -0,0 +1,53 @@ +/* + * 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/internal/support/bits.hpp" +#include "ccmath/math/compare/isinf.hpp" +#include "ccmath/math/compare/isnan.hpp" + +#include +#include + +namespace ccm::internal::impl +{ + constexpr float scalbn_float_impl(float arg, int exp) noexcept + { + ccm::float_t tmp = arg; + + if (exp > 127) + { + tmp *= 0x1p127F; + exp -= 127; + if (exp > 127) + { + tmp *= 0x1p127F; + exp -= 127; + if (exp > 127) { exp = 127; } + } + } + else if (exp < -126) + { + tmp *= 0x1p-126F * 0x1p24F; + exp += 126 - 24; + if (exp < -126) + { + tmp *= 0x1p-126F * 0x1p24F; + exp += 126 - 24; + if (exp < -126) { exp = -126; } + } + } + + const std::uint32_t bits = ccm::support::bit_cast(127 + exp) << 23; + arg = tmp * ccm::support::bit_cast(bits); + + return arg; + } + +} // namespace ccm::internal::impl diff --git a/include/ccmath/math/fmanip/impl/scalbn_ldouble_impl.hpp b/include/ccmath/math/fmanip/impl/scalbn_ldouble_impl.hpp new file mode 100644 index 0000000..7d1a9fb --- /dev/null +++ b/include/ccmath/math/fmanip/impl/scalbn_ldouble_impl.hpp @@ -0,0 +1,69 @@ +/* + * 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 + +#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024 + #include "ccmath/math/fmanip/impl/scalbn_double_impl.hpp" +#endif + +#if (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384 + #include "ccmath/math/compare/isinf.hpp" + #include "ccmath/math/compare/isnan.hpp" + #include +#endif + +namespace ccm::internal::impl +{ + constexpr long double scalbn_ldouble_impl(long double arg, int exp) noexcept + { +#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024 // If long double is the same as double + return ccm::internal::impl::scalbn_double_impl(arg, exp); +#elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384 // If long double is 80-bit or 128-bit large + // This is a generic implementation for long double scalbn that does not use bit manipulation. + // May be much slower than the double and float version. + // Need to benchmark it. + // TODO: Possibly implement a bit manipulation version of this function in the future. + if (arg == static_cast(0)) { return arg; } + + if (ccm::isinf(arg)) { return arg; } + + if (exp == static_cast(0)) { return arg; } + + if (ccm::isnan(arg)) { return std::numeric_limits::quiet_NaN(); } + + long double mult(1); + if (exp > 0) + { + mult = std::numeric_limits::radix; + --exp; + } + else + { + ++exp; + exp = -exp; + mult /= std::numeric_limits::radix; + } + + while (exp > 0) + { + if ((exp & 1) == 0) + { + mult *= mult; + exp >>= 1; + } + else + { + arg *= mult; + --exp; + } + } + return arg; +#endif + } +} // namespace ccm::internal::impl diff --git a/include/ccmath/detail/fmanip/ldexp.hpp b/include/ccmath/math/fmanip/ldexp.hpp similarity index 100% rename from include/ccmath/detail/fmanip/ldexp.hpp rename to include/ccmath/math/fmanip/ldexp.hpp diff --git a/include/ccmath/detail/fmanip/logb.hpp b/include/ccmath/math/fmanip/logb.hpp similarity index 100% rename from include/ccmath/detail/fmanip/logb.hpp rename to include/ccmath/math/fmanip/logb.hpp diff --git a/include/ccmath/detail/fmanip/modf.hpp b/include/ccmath/math/fmanip/modf.hpp similarity index 100% rename from include/ccmath/detail/fmanip/modf.hpp rename to include/ccmath/math/fmanip/modf.hpp diff --git a/include/ccmath/detail/fmanip/nextafter.hpp b/include/ccmath/math/fmanip/nextafter.hpp similarity index 100% rename from include/ccmath/detail/fmanip/nextafter.hpp rename to include/ccmath/math/fmanip/nextafter.hpp diff --git a/include/ccmath/math/fmanip/scalbn.hpp b/include/ccmath/math/fmanip/scalbn.hpp new file mode 100644 index 0000000..b4b6023 --- /dev/null +++ b/include/ccmath/math/fmanip/scalbn.hpp @@ -0,0 +1,25 @@ +/* + * 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/math/fmanip/impl/scalbn_double_impl.hpp" +#include "ccmath/math/fmanip/impl/scalbn_float_impl.hpp" +#include "ccmath/math/fmanip/impl/scalbn_ldouble_impl.hpp" + +namespace ccm +{ + template + constexpr T scalbn(T x, int n) noexcept + { + if constexpr (std::is_same_v) { return ccm::internal::impl::scalbn_float_impl(x, n); } + if constexpr (std::is_same_v) { return ccm::internal::impl::scalbn_ldouble_impl(x, n); } + return static_cast(ccm::internal::impl::scalbn_double_impl(x, n)); + } + +} // namespace ccm diff --git a/include/ccmath/detail/gamma.hpp b/include/ccmath/math/gamma.hpp similarity index 100% rename from include/ccmath/detail/gamma.hpp rename to include/ccmath/math/gamma.hpp diff --git a/include/ccmath/detail/hyperbolic/acosh.hpp b/include/ccmath/math/hyperbolic/acosh.hpp similarity index 100% rename from include/ccmath/detail/hyperbolic/acosh.hpp rename to include/ccmath/math/hyperbolic/acosh.hpp diff --git a/include/ccmath/detail/hyperbolic/asinh.hpp b/include/ccmath/math/hyperbolic/asinh.hpp similarity index 100% rename from include/ccmath/detail/hyperbolic/asinh.hpp rename to include/ccmath/math/hyperbolic/asinh.hpp diff --git a/include/ccmath/detail/hyperbolic/atanh.hpp b/include/ccmath/math/hyperbolic/atanh.hpp similarity index 100% rename from include/ccmath/detail/hyperbolic/atanh.hpp rename to include/ccmath/math/hyperbolic/atanh.hpp diff --git a/include/ccmath/detail/hyperbolic/cosh.hpp b/include/ccmath/math/hyperbolic/cosh.hpp similarity index 100% rename from include/ccmath/detail/hyperbolic/cosh.hpp rename to include/ccmath/math/hyperbolic/cosh.hpp diff --git a/include/ccmath/detail/hyperbolic/sinh.hpp b/include/ccmath/math/hyperbolic/sinh.hpp similarity index 100% rename from include/ccmath/detail/hyperbolic/sinh.hpp rename to include/ccmath/math/hyperbolic/sinh.hpp diff --git a/include/ccmath/detail/hyperbolic/tanh.hpp b/include/ccmath/math/hyperbolic/tanh.hpp similarity index 100% rename from include/ccmath/detail/hyperbolic/tanh.hpp rename to include/ccmath/math/hyperbolic/tanh.hpp diff --git a/include/ccmath/math/lerp.hpp b/include/ccmath/math/lerp.hpp new file mode 100644 index 0000000..4187665 --- /dev/null +++ b/include/ccmath/math/lerp.hpp @@ -0,0 +1,49 @@ +/* + * 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/internal/predef/unlikely.hpp" +#include "ccmath/math/basic/fma.hpp" + +namespace ccm +{ + template + constexpr T lerp(T a, T b, T t) noexcept + { + // Optimized version of lerp + // https://developer.nvidia.com/blog/lerp-faster-cuda/ + // TODO: Validate this works for all cases of a lerp. + return ccm::fma(t, b, ccm::fma(-t, a, a)); + } + + template + constexpr typename std::enable_if_t && std::is_arithmetic_v && std::is_arithmetic_v, std::common_type_t> + lerp(T a, U b, V t) noexcept + { + using result_type = typename std::common_type_t; + static_assert(!(std::is_same_v && std::is_same_v && std::is_same_v)); + return lerp(static_cast(a), static_cast(b), static_cast(t)); + } + + // TODO: Remove this once we confirm the new lerp is 100% stable + template + [[maybe_unused]] [[deprecated( + "Do not use ccm::lerp_old it is only being kept as a fallback until ccm::lerp has been validated as conforming to std::lerp")]] constexpr T + old_lerp(T a, T b, T t) noexcept + { + if ((a <= 0 && b >= 0) || (a >= 0 && b <= 0)) { return t * b + (1 - t) * a; } + + if (t == 1) { return b; } + + const T x = a + t * (b - a); + if ((t > 1) == (b > a)) { return b < x ? x : b; } + + return x < b ? x : b; + } +} // namespace ccm diff --git a/include/ccmath/detail/lgamma.hpp b/include/ccmath/math/lgamma.hpp similarity index 100% rename from include/ccmath/detail/lgamma.hpp rename to include/ccmath/math/lgamma.hpp diff --git a/include/ccmath/detail/nearest/ceil.hpp b/include/ccmath/math/nearest/ceil.hpp similarity index 100% rename from include/ccmath/detail/nearest/ceil.hpp rename to include/ccmath/math/nearest/ceil.hpp diff --git a/include/ccmath/math/nearest/floor.hpp b/include/ccmath/math/nearest/floor.hpp new file mode 100644 index 0000000..df824ac --- /dev/null +++ b/include/ccmath/math/nearest/floor.hpp @@ -0,0 +1,73 @@ +/* + * 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/math/compare/isinf.hpp" +#include "ccmath/math/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 + 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> + 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. + */ + 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. + */ + constexpr double floorl(double num) noexcept + { + return floor(num); + } +} // namespace ccm + +/// @ingroup nearest diff --git a/include/ccmath/detail/nearest/nearbyint.hpp b/include/ccmath/math/nearest/nearbyint.hpp similarity index 100% rename from include/ccmath/detail/nearest/nearbyint.hpp rename to include/ccmath/math/nearest/nearbyint.hpp diff --git a/include/ccmath/detail/nearest/rint.hpp b/include/ccmath/math/nearest/rint.hpp similarity index 100% rename from include/ccmath/detail/nearest/rint.hpp rename to include/ccmath/math/nearest/rint.hpp diff --git a/include/ccmath/detail/nearest/round.hpp b/include/ccmath/math/nearest/round.hpp similarity index 100% rename from include/ccmath/detail/nearest/round.hpp rename to include/ccmath/math/nearest/round.hpp diff --git a/include/ccmath/detail/nearest/trunc.hpp b/include/ccmath/math/nearest/trunc.hpp similarity index 56% rename from include/ccmath/detail/nearest/trunc.hpp rename to include/ccmath/math/nearest/trunc.hpp index f23bae5..85ecefb 100644 --- a/include/ccmath/detail/nearest/trunc.hpp +++ b/include/ccmath/math/nearest/trunc.hpp @@ -8,8 +8,8 @@ #pragma once -#include "ccmath/detail/basic/abs.hpp" -#include "ccmath/detail/compare/signbit.hpp" +#include "ccmath/math/basic/abs.hpp" +#include "ccmath/math/compare/signbit.hpp" namespace ccm { @@ -19,23 +19,21 @@ namespace ccm * @param x The value to truncate. * @return Returns a truncated value. */ - // Follows the requirements of std::trunc - // https://en.cppreference.com/w/cpp/numeric/math/trunc - template ::value, int> = 0> - inline constexpr T trunc(T x) noexcept + template , int> = 0> + constexpr T trunc(T x) noexcept { - // If x is NaN then return Positive NaN or Negative NaN depending on the sign of x - if (ccm::isnan(x)) - { - if (ccm::signbit(x)) { return -std::numeric_limits::quiet_NaN(); } - else { return std::numeric_limits::quiet_NaN(); } - } + // If x is NaN then return Positive NaN or Negative NaN depending on the sign of x + if (ccm::isnan(x)) + { + if (ccm::signbit(x)) { return -std::numeric_limits::quiet_NaN(); } + return std::numeric_limits::quiet_NaN(); + } - // If x == ±∞ then return x - if (x == std::numeric_limits::infinity() || x == -std::numeric_limits::infinity()) { return x; } + // If x == ±∞ then return x + if (x == std::numeric_limits::infinity() || x == -std::numeric_limits::infinity()) { return x; } - // If x == ±0 then return x - if (x == static_cast(0.0)) { return x; } + // If x == ±0 then return x + if (x == static_cast(0.0)) { return x; } return static_cast(static_cast(x)); } @@ -46,8 +44,8 @@ namespace ccm * @param x The value to truncate. * @return Returns a truncated value. */ - template ::value, int> = 0> - inline constexpr double trunc(Integer x) noexcept + template , int> = 0> + constexpr double trunc(Integer x) noexcept { return static_cast(x); } @@ -57,7 +55,7 @@ namespace ccm * @param x The float to truncate. * @return Returns a truncated float. */ - inline constexpr float truncf(float x) noexcept + constexpr float truncf(float x) noexcept { return trunc(x); } @@ -68,7 +66,7 @@ namespace ccm * @param x The long double to truncate. * @return Returns a truncated long double. */ - inline constexpr long double truncl(long double x) noexcept + constexpr long double truncl(long double x) noexcept { return trunc(x); } diff --git a/include/ccmath/detail/power/cbrt.hpp b/include/ccmath/math/power/cbrt.hpp similarity index 100% rename from include/ccmath/detail/power/cbrt.hpp rename to include/ccmath/math/power/cbrt.hpp diff --git a/include/ccmath/detail/power/hypot.hpp b/include/ccmath/math/power/hypot.hpp similarity index 100% rename from include/ccmath/detail/power/hypot.hpp rename to include/ccmath/math/power/hypot.hpp diff --git a/include/ccmath/math/power/pow.hpp b/include/ccmath/math/power/pow.hpp new file mode 100644 index 0000000..cfd9a3f --- /dev/null +++ b/include/ccmath/math/power/pow.hpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2024-Present Ian Pike + * Copyright (c) 2024-Present cmath contributors + * + * This library is provided under the MIT License. + * See LICENSE for more information. + */ + +#pragma once + +#include +#include +#include "ccmath/ccmath.hpp" +#include "ccmath/math/exponential/exp.hpp" +#include "ccmath/math/exponential/log.hpp" + +namespace ccm +{ + namespace internal::impl + { + template && std::is_unsigned_v, bool> = true> + constexpr T pow_expo_by_sqr(T base, T exp) noexcept + { + // Handle common cases + if (exp == 0) { return 1; } // Anything to the power of 0 is 1 + if (exp == 1) { return base; } // Anything to the power of 1 is itself + if (exp == 2) { return base * base; } // Anything to the power of 2 is itself squared + if (base == 0) { return 0; } // 0 to any power is 0 + if (base == 1) { return 1; } // 1 to any power is 1 + + // If the base is 2, we can use the bit shift operator to calculate the power. + if (base == 2) { return 1 << exp; } + + // This is pretty fast with smaller numbers, but is slower than the standard when dealing with large numbers. + // TODO: Find a way to optimize this for larger numbers. + T result = 1; + for (;;) + { + if (exp & 1) { result *= base; } + exp >>= 1; + if (!exp) { break; } + base *= base; + } + + return result; + } + } // namespace internal::impl + + template + constexpr T pow(T x, T y) noexcept + { + if constexpr (std::is_integral_v && std::is_unsigned_v) { return internal::impl::pow_expo_by_sqr(x, y); } + return 0; + } + +} // namespace ccm diff --git a/include/ccmath/detail/power/sqrt.hpp b/include/ccmath/math/power/sqrt.hpp similarity index 100% rename from include/ccmath/detail/power/sqrt.hpp rename to include/ccmath/math/power/sqrt.hpp diff --git a/include/ccmath/detail/special/assoc_laguerre.hpp b/include/ccmath/math/special/assoc_laguerre.hpp similarity index 100% rename from include/ccmath/detail/special/assoc_laguerre.hpp rename to include/ccmath/math/special/assoc_laguerre.hpp diff --git a/include/ccmath/detail/special/assoc_legendre.hpp b/include/ccmath/math/special/assoc_legendre.hpp similarity index 100% rename from include/ccmath/detail/special/assoc_legendre.hpp rename to include/ccmath/math/special/assoc_legendre.hpp diff --git a/include/ccmath/detail/special/beta.hpp b/include/ccmath/math/special/beta.hpp similarity index 100% rename from include/ccmath/detail/special/beta.hpp rename to include/ccmath/math/special/beta.hpp diff --git a/include/ccmath/detail/special/comp_ellint_1.hpp b/include/ccmath/math/special/comp_ellint_1.hpp similarity index 100% rename from include/ccmath/detail/special/comp_ellint_1.hpp rename to include/ccmath/math/special/comp_ellint_1.hpp diff --git a/include/ccmath/detail/special/comp_ellint_2.hpp b/include/ccmath/math/special/comp_ellint_2.hpp similarity index 100% rename from include/ccmath/detail/special/comp_ellint_2.hpp rename to include/ccmath/math/special/comp_ellint_2.hpp diff --git a/include/ccmath/detail/special/comp_ellint_3.hpp b/include/ccmath/math/special/comp_ellint_3.hpp similarity index 100% rename from include/ccmath/detail/special/comp_ellint_3.hpp rename to include/ccmath/math/special/comp_ellint_3.hpp diff --git a/include/ccmath/detail/special/cyl_bessel_i.hpp b/include/ccmath/math/special/cyl_bessel_i.hpp similarity index 100% rename from include/ccmath/detail/special/cyl_bessel_i.hpp rename to include/ccmath/math/special/cyl_bessel_i.hpp diff --git a/include/ccmath/detail/special/cyl_bessel_j.hpp b/include/ccmath/math/special/cyl_bessel_j.hpp similarity index 100% rename from include/ccmath/detail/special/cyl_bessel_j.hpp rename to include/ccmath/math/special/cyl_bessel_j.hpp diff --git a/include/ccmath/detail/special/cyl_bessel_k.hpp b/include/ccmath/math/special/cyl_bessel_k.hpp similarity index 100% rename from include/ccmath/detail/special/cyl_bessel_k.hpp rename to include/ccmath/math/special/cyl_bessel_k.hpp diff --git a/include/ccmath/detail/special/cyl_neumann.hpp b/include/ccmath/math/special/cyl_neumann.hpp similarity index 100% rename from include/ccmath/detail/special/cyl_neumann.hpp rename to include/ccmath/math/special/cyl_neumann.hpp diff --git a/include/ccmath/detail/special/ellint_1.hpp b/include/ccmath/math/special/ellint_1.hpp similarity index 100% rename from include/ccmath/detail/special/ellint_1.hpp rename to include/ccmath/math/special/ellint_1.hpp diff --git a/include/ccmath/detail/special/ellint_2.hpp b/include/ccmath/math/special/ellint_2.hpp similarity index 100% rename from include/ccmath/detail/special/ellint_2.hpp rename to include/ccmath/math/special/ellint_2.hpp diff --git a/include/ccmath/detail/special/ellint_3.hpp b/include/ccmath/math/special/ellint_3.hpp similarity index 100% rename from include/ccmath/detail/special/ellint_3.hpp rename to include/ccmath/math/special/ellint_3.hpp diff --git a/include/ccmath/detail/special/expint.hpp b/include/ccmath/math/special/expint.hpp similarity index 100% rename from include/ccmath/detail/special/expint.hpp rename to include/ccmath/math/special/expint.hpp diff --git a/include/ccmath/detail/special/hermite.hpp b/include/ccmath/math/special/hermite.hpp similarity index 100% rename from include/ccmath/detail/special/hermite.hpp rename to include/ccmath/math/special/hermite.hpp diff --git a/include/ccmath/detail/special/laguerre.hpp b/include/ccmath/math/special/laguerre.hpp similarity index 100% rename from include/ccmath/detail/special/laguerre.hpp rename to include/ccmath/math/special/laguerre.hpp diff --git a/include/ccmath/detail/special/legendre.hpp b/include/ccmath/math/special/legendre.hpp similarity index 100% rename from include/ccmath/detail/special/legendre.hpp rename to include/ccmath/math/special/legendre.hpp diff --git a/include/ccmath/detail/special/riemann_zeta.hpp b/include/ccmath/math/special/riemann_zeta.hpp similarity index 100% rename from include/ccmath/detail/special/riemann_zeta.hpp rename to include/ccmath/math/special/riemann_zeta.hpp diff --git a/include/ccmath/detail/special/sph_bessel.hpp b/include/ccmath/math/special/sph_bessel.hpp similarity index 100% rename from include/ccmath/detail/special/sph_bessel.hpp rename to include/ccmath/math/special/sph_bessel.hpp diff --git a/include/ccmath/detail/special/sph_legendre.hpp b/include/ccmath/math/special/sph_legendre.hpp similarity index 100% rename from include/ccmath/detail/special/sph_legendre.hpp rename to include/ccmath/math/special/sph_legendre.hpp diff --git a/include/ccmath/detail/special/sph_neumann.hpp b/include/ccmath/math/special/sph_neumann.hpp similarity index 100% rename from include/ccmath/detail/special/sph_neumann.hpp rename to include/ccmath/math/special/sph_neumann.hpp diff --git a/include/ccmath/detail/trig/acos.hpp b/include/ccmath/math/trig/acos.hpp similarity index 100% rename from include/ccmath/detail/trig/acos.hpp rename to include/ccmath/math/trig/acos.hpp diff --git a/include/ccmath/detail/trig/asin.hpp b/include/ccmath/math/trig/asin.hpp similarity index 100% rename from include/ccmath/detail/trig/asin.hpp rename to include/ccmath/math/trig/asin.hpp diff --git a/include/ccmath/detail/trig/atan.hpp b/include/ccmath/math/trig/atan.hpp similarity index 100% rename from include/ccmath/detail/trig/atan.hpp rename to include/ccmath/math/trig/atan.hpp diff --git a/include/ccmath/detail/trig/atan2.hpp b/include/ccmath/math/trig/atan2.hpp similarity index 100% rename from include/ccmath/detail/trig/atan2.hpp rename to include/ccmath/math/trig/atan2.hpp diff --git a/include/ccmath/detail/trig/cos.hpp b/include/ccmath/math/trig/cos.hpp similarity index 100% rename from include/ccmath/detail/trig/cos.hpp rename to include/ccmath/math/trig/cos.hpp diff --git a/include/ccmath/detail/trig/sin.hpp b/include/ccmath/math/trig/sin.hpp similarity index 100% rename from include/ccmath/detail/trig/sin.hpp rename to include/ccmath/math/trig/sin.hpp diff --git a/include/ccmath/detail/trig/tan.hpp b/include/ccmath/math/trig/tan.hpp similarity index 100% rename from include/ccmath/detail/trig/tan.hpp rename to include/ccmath/math/trig/tan.hpp diff --git a/include/ccmath/nearest.hpp b/include/ccmath/nearest.hpp index 70761e4..7b181f6 100644 --- a/include/ccmath/nearest.hpp +++ b/include/ccmath/nearest.hpp @@ -8,9 +8,9 @@ #pragma once -#include "ccmath/detail/nearest/ceil.hpp" -#include "ccmath/detail/nearest/floor.hpp" -#include "ccmath/detail/nearest/nearbyint.hpp" -#include "ccmath/detail/nearest/rint.hpp" -#include "ccmath/detail/nearest/round.hpp" -#include "ccmath/detail/nearest/trunc.hpp" +#include "ccmath/math/nearest/ceil.hpp" +#include "ccmath/math/nearest/floor.hpp" +#include "ccmath/math/nearest/nearbyint.hpp" +#include "ccmath/math/nearest/rint.hpp" +#include "ccmath/math/nearest/round.hpp" +#include "ccmath/math/nearest/trunc.hpp" diff --git a/include/ccmath/numbers.hpp b/include/ccmath/numbers.hpp index 7894f81..2537663 100644 --- a/include/ccmath/numbers.hpp +++ b/include/ccmath/numbers.hpp @@ -59,17 +59,17 @@ namespace ccm::numbers 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 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; + 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/include/ccmath/power.hpp b/include/ccmath/power.hpp index ea2221e..6778c95 100644 --- a/include/ccmath/power.hpp +++ b/include/ccmath/power.hpp @@ -8,7 +8,7 @@ #pragma once -#include "ccmath/detail/power/cbrt.hpp" -#include "ccmath/detail/power/hypot.hpp" -#include "ccmath/detail/power/pow.hpp" -#include "ccmath/detail/power/sqrt.hpp" +#include "ccmath/math/power/cbrt.hpp" +#include "ccmath/math/power/hypot.hpp" +#include "ccmath/math/power/pow.hpp" +#include "ccmath/math/power/sqrt.hpp" diff --git a/include/ccmath/special.hpp b/include/ccmath/special.hpp index 816f2b2..098cd22 100644 --- a/include/ccmath/special.hpp +++ b/include/ccmath/special.hpp @@ -8,24 +8,24 @@ #pragma once -#include "ccmath/detail/special/assoc_laguerre.hpp" -#include "ccmath/detail/special/assoc_legendre.hpp" -#include "ccmath/detail/special/beta.hpp" -#include "ccmath/detail/special/comp_ellint_1.hpp" -#include "ccmath/detail/special/comp_ellint_2.hpp" -#include "ccmath/detail/special/comp_ellint_3.hpp" -#include "ccmath/detail/special/cyl_bessel_i.hpp" -#include "ccmath/detail/special/cyl_bessel_j.hpp" -#include "ccmath/detail/special/cyl_bessel_k.hpp" -#include "ccmath/detail/special/cyl_neumann.hpp" -#include "ccmath/detail/special/ellint_1.hpp" -#include "ccmath/detail/special/ellint_2.hpp" -#include "ccmath/detail/special/ellint_3.hpp" -#include "ccmath/detail/special/expint.hpp" -#include "ccmath/detail/special/hermite.hpp" -#include "ccmath/detail/special/laguerre.hpp" -#include "ccmath/detail/special/legendre.hpp" -#include "ccmath/detail/special/riemann_zeta.hpp" -#include "ccmath/detail/special/sph_bessel.hpp" -#include "ccmath/detail/special/sph_legendre.hpp" -#include "ccmath/detail/special/sph_neumann.hpp" +#include "ccmath/math/special/assoc_laguerre.hpp" +#include "ccmath/math/special/assoc_legendre.hpp" +#include "ccmath/math/special/beta.hpp" +#include "ccmath/math/special/comp_ellint_1.hpp" +#include "ccmath/math/special/comp_ellint_2.hpp" +#include "ccmath/math/special/comp_ellint_3.hpp" +#include "ccmath/math/special/cyl_bessel_i.hpp" +#include "ccmath/math/special/cyl_bessel_j.hpp" +#include "ccmath/math/special/cyl_bessel_k.hpp" +#include "ccmath/math/special/cyl_neumann.hpp" +#include "ccmath/math/special/ellint_1.hpp" +#include "ccmath/math/special/ellint_2.hpp" +#include "ccmath/math/special/ellint_3.hpp" +#include "ccmath/math/special/expint.hpp" +#include "ccmath/math/special/hermite.hpp" +#include "ccmath/math/special/laguerre.hpp" +#include "ccmath/math/special/legendre.hpp" +#include "ccmath/math/special/riemann_zeta.hpp" +#include "ccmath/math/special/sph_bessel.hpp" +#include "ccmath/math/special/sph_legendre.hpp" +#include "ccmath/math/special/sph_neumann.hpp" diff --git a/include/ccmath/trig.hpp b/include/ccmath/trig.hpp index 7cfb40a..bc17850 100644 --- a/include/ccmath/trig.hpp +++ b/include/ccmath/trig.hpp @@ -8,10 +8,10 @@ #pragma once -#include "ccmath/detail/trig/acos.hpp" -#include "ccmath/detail/trig/asin.hpp" -#include "ccmath/detail/trig/atan.hpp" -#include "ccmath/detail/trig/atan2.hpp" -#include "ccmath/detail/trig/cos.hpp" -#include "ccmath/detail/trig/sin.hpp" -#include "ccmath/detail/trig/tan.hpp" +#include "ccmath/math/trig/acos.hpp" +#include "ccmath/math/trig/asin.hpp" +#include "ccmath/math/trig/atan.hpp" +#include "ccmath/math/trig/atan2.hpp" +#include "ccmath/math/trig/cos.hpp" +#include "ccmath/math/trig/sin.hpp" +#include "ccmath/math/trig/tan.hpp" diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d37e695..8815b4b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -31,7 +31,7 @@ target_sources(${PROJECT_NAME}-basic PRIVATE basic/fmod_test.cpp basic/max_test.cpp basic/min_test.cpp - compare/isnan_test.cpp + basic/nan_test.cpp basic/remainder_test.cpp basic/remquo_test.cpp ) @@ -104,6 +104,47 @@ target_link_libraries(${PROJECT_NAME}-nearest PRIVATE gtest::gtest ) +add_executable(${PROJECT_NAME}-power) +target_sources(${PROJECT_NAME}-power PRIVATE + power/pow_test.cpp + +) +target_link_libraries(${PROJECT_NAME}-power PRIVATE + ccmath::test + gtest::gtest +) + +add_executable(${PROJECT_NAME}-misc) + +# This is required as std::lerp is only available in versions of C++20 or greater. +if(CMAKE_CXX_STANDARD GREATER_EQUAL 20) + target_sources(${PROJECT_NAME}-misc PRIVATE + misc/lerp_test_std.cpp + + ) +else() + target_sources(${PROJECT_NAME}-misc PRIVATE + misc/lerp_test_no_std.cpp + ) +endif() + +target_link_libraries(${PROJECT_NAME}-misc PRIVATE + ccmath::test + gtest::gtest +) + + +# Tests for internal items +add_executable(${PROJECT_NAME}-internal-types) +target_sources(${PROJECT_NAME}-internal-types PRIVATE + internal/types/big_int_test.cpp + +) +target_link_libraries(${PROJECT_NAME}-internal-types PRIVATE + ccmath::test + gtest::gtest +) + if(CCMATH_OS_WINDOWS) # For Windows: Prevent overriding the parent project's compiler/linker settings @@ -124,9 +165,12 @@ 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) +add_test(NAME ${PROJECT_NAME}-nearest COMMAND ${PROJECT_NAME}-nearest) +add_test(NAME ${PROJECT_NAME}-power COMMAND ${PROJECT_NAME}-power) +add_test(NAME ${PROJECT_NAME}-misc COMMAND ${PROJECT_NAME}-misc) - +# Internal tests +add_test(NAME ${PROJECT_NAME}-internal-types COMMAND ${PROJECT_NAME}-internal-types) diff --git a/test/basic/abs_test.cpp b/test/basic/abs_test.cpp index 756668e..a9abc43 100644 --- a/test/basic/abs_test.cpp +++ b/test/basic/abs_test.cpp @@ -7,35 +7,96 @@ */ #include - #include #include #include - -TEST(CcmathBasicTests, Abs) +TEST(CcmathBasicTests, AbsStaticAssert) { - // Verify that ccm::abs works with static_assert - static_assert(ccm::abs(1) == 1, "abs has failed testing that it is static_assert-able!"); + // Verify that ccm::abs works with static_assert + static_assert(ccm::abs(1) == 1, "abs has failed testing that it is static_assert-able!"); +} +TEST(CcmathBasicTests, AbsZeroSign) +{ EXPECT_EQ(ccm::abs(-0.0), std::abs(-0.0)); - - // Test the ccm::abs function against std::abs EXPECT_EQ(ccm::abs(0), std::abs(0)); +} - - - EXPECT_EQ(ccm::abs(1), std::abs(1)); - EXPECT_EQ(ccm::abs(-1), std::abs(-1)); +TEST(CcmathBasicTests, AbsDouble) +{ EXPECT_EQ(ccm::abs(1.0), std::abs(1.0)); EXPECT_EQ(ccm::abs(-1.0), std::abs(-1.0)); - EXPECT_EQ(ccm::abs(1.0f), std::abs(1.0f)); - EXPECT_EQ(ccm::abs(-1.0f), std::abs(-1.0f)); + EXPECT_EQ(ccm::abs(2.0), std::abs(2.0)); + EXPECT_EQ(ccm::abs(-2.0), std::abs(-2.0)); + EXPECT_EQ(ccm::abs(4.0), std::abs(4.0)); + EXPECT_EQ(ccm::abs(-4.0), std::abs(-4.0)); + EXPECT_EQ(ccm::abs(8.0), std::abs(8.0)); + EXPECT_EQ(ccm::abs(-8.0), std::abs(-8.0)); + EXPECT_EQ(ccm::abs(16.0), std::abs(16.0)); + EXPECT_EQ(ccm::abs(-16.0), std::abs(-16.0)); + EXPECT_EQ(ccm::abs(32.0), std::abs(32.0)); + EXPECT_EQ(ccm::abs(-32.0), std::abs(-32.0)); +} + +TEST(CcmathBasicTests, AbsFloat) +{ + EXPECT_EQ(ccm::abs(1.0F), std::abs(1.0F)); + EXPECT_EQ(ccm::abs(-1.0F), std::abs(-1.0F)); + EXPECT_EQ(ccm::abs(2.0F), std::abs(2.0F)); + EXPECT_EQ(ccm::abs(-2.0F), std::abs(-2.0F)); + EXPECT_EQ(ccm::abs(4.0F), std::abs(4.0F)); + EXPECT_EQ(ccm::abs(-4.0F), std::abs(-4.0F)); + EXPECT_EQ(ccm::abs(8.0F), std::abs(8.0F)); + EXPECT_EQ(ccm::abs(-8.0F), std::abs(-8.0F)); + EXPECT_EQ(ccm::abs(16.0F), std::abs(16.0F)); + EXPECT_EQ(ccm::abs(-16.0F), std::abs(-16.0F)); + EXPECT_EQ(ccm::abs(32.0F), std::abs(32.0F)); + EXPECT_EQ(ccm::abs(-32.0F), std::abs(-32.0F)); +} + +TEST(CcmathBasicTests, AbsLongDouble) +{ EXPECT_EQ(ccm::abs(1.0L), std::abs(1.0L)); - EXPECT_EQ(ccm::abs(-1.0L), std::abs(-1.0L)); - EXPECT_EQ(ccm::abs(1.0l), std::abs(1.0l)); - EXPECT_EQ(ccm::abs(-1.0l), std::abs(-1.0l)); + EXPECT_EQ(ccm::abs(-1.0L), std::abs(-1.0L)); + EXPECT_EQ(ccm::abs(2.0L), std::abs(2.0L)); + EXPECT_EQ(ccm::abs(-2.0L), std::abs(-2.0L)); + EXPECT_EQ(ccm::abs(4.0L), std::abs(4.0L)); + EXPECT_EQ(ccm::abs(-4.0L), std::abs(-4.0L)); + EXPECT_EQ(ccm::abs(8.0L), std::abs(8.0L)); + EXPECT_EQ(ccm::abs(-8.0L), std::abs(-8.0L)); + EXPECT_EQ(ccm::abs(16.0L), std::abs(16.0L)); + EXPECT_EQ(ccm::abs(-16.0L), std::abs(-16.0L)); + EXPECT_EQ(ccm::abs(32.0L), std::abs(32.0L)); + EXPECT_EQ(ccm::abs(-32.0L), std::abs(-32.0L)); +} - // TODO: Find a way to test for NaN. +TEST(CcmathBasicTests, AbsInt) +{ + EXPECT_EQ(ccm::abs(1), std::abs(1)); + EXPECT_EQ(ccm::abs(-1), std::abs(-1)); + EXPECT_EQ(ccm::abs(2), std::abs(2)); + EXPECT_EQ(ccm::abs(-2), std::abs(-2)); + EXPECT_EQ(ccm::abs(4), std::abs(4)); + EXPECT_EQ(ccm::abs(-4), std::abs(-4)); + EXPECT_EQ(ccm::abs(8), std::abs(8)); + EXPECT_EQ(ccm::abs(-8), std::abs(-8)); + EXPECT_EQ(ccm::abs(16), std::abs(16)); + EXPECT_EQ(ccm::abs(-16), std::abs(-16)); + EXPECT_EQ(ccm::abs(32), std::abs(32)); + EXPECT_EQ(ccm::abs(-32), std::abs(-32)); +} +TEST(CcmathBasicTests, AbsEdgeCases) +{ + // TODO: Implement more edge cases for ABS. + EXPECT_EQ(ccm::abs(std::numeric_limits::min()), std::abs(std::numeric_limits::min())); + EXPECT_EQ(ccm::abs(std::numeric_limits::max()), std::abs(std::numeric_limits::max())); + EXPECT_EQ(ccm::abs(std::numeric_limits::min()), std::abs(std::numeric_limits::min())); + EXPECT_EQ(ccm::abs(std::numeric_limits::max()), std::abs(std::numeric_limits::max())); + EXPECT_EQ(ccm::abs(std::numeric_limits::min()), std::abs(std::numeric_limits::min())); + EXPECT_EQ(ccm::abs(std::numeric_limits::max()), std::abs(std::numeric_limits::max())); + EXPECT_EQ(ccm::abs(std::numeric_limits::min()), std::abs(std::numeric_limits::min())); + EXPECT_EQ(ccm::abs(std::numeric_limits::max()), std::abs(std::numeric_limits::max())); } + diff --git a/test/basic/fdim_test.cpp b/test/basic/fdim_test.cpp index 1659ab8..aca728c 100644 --- a/test/basic/fdim_test.cpp +++ b/test/basic/fdim_test.cpp @@ -12,18 +12,63 @@ #include #include - -TEST(CcmathBasicTests, Fdim) +TEST(CcmathBasicTests, FdimStaticAssert) { static_assert(ccm::fdim(1.0, 1.0) == 0.0, "fdim has failed testing that it is static_assert-able!"); +} +TEST(CcmathBasicTests, FdimDouble) +{ EXPECT_EQ(ccm::fdim(1.0, 1.0), std::fdim(1.0, 1.0)); + EXPECT_EQ(ccm::fdim(2.0, 2.0), std::fdim(2.0, 2.0)); + EXPECT_EQ(ccm::fdim(4.0, 4.0), std::fdim(4.0, 4.0)); + EXPECT_EQ(ccm::fdim(8.0, 8.0), std::fdim(8.0, 8.0)); + EXPECT_EQ(ccm::fdim(16.0, 16.0), std::fdim(16.0, 16.0)); + EXPECT_EQ(ccm::fdim(32.0, 32.0), std::fdim(32.0, 32.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)); EXPECT_EQ(ccm::fdim(0.0, 0.0), std::fdim(0.0, 0.0)); EXPECT_EQ(ccm::fdim(-1.0, 1.0), std::fdim(-1.0, 1.0)); EXPECT_EQ(ccm::fdim(1.0, -1.0), std::fdim(1.0, -1.0)); 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)); +} + +TEST(CcmathBasicTests, FdimFloat) +{ + EXPECT_EQ(ccm::fdim(1.0F, 1.0F), std::fdim(1.0F, 1.0F)); + EXPECT_EQ(ccm::fdim(2.0F, 2.0F), std::fdim(2.0F, 2.0F)); + EXPECT_EQ(ccm::fdim(4.0F, 4.0F), std::fdim(4.0F, 4.0F)); + EXPECT_EQ(ccm::fdim(8.0F, 8.0F), std::fdim(8.0F, 8.0F)); + EXPECT_EQ(ccm::fdim(16.0F, 16.0F), std::fdim(16.0F, 16.0F)); + EXPECT_EQ(ccm::fdim(32.0F, 32.0F), std::fdim(32.0F, 32.0F)); + + EXPECT_EQ(ccm::fdim(1.0F, 0.0F), std::fdim(1.0F, 0.0F)); + EXPECT_EQ(ccm::fdim(0.0F, 1.0F), std::fdim(0.0F, 1.0F)); + EXPECT_EQ(ccm::fdim(0.0F, 0.0F), std::fdim(0.0F, 0.0F)); + EXPECT_EQ(ccm::fdim(-1.0F, 1.0F), std::fdim(-1.0F, 1.0F)); + EXPECT_EQ(ccm::fdim(1.0F, -1.0F), std::fdim(1.0F, -1.0F)); + EXPECT_EQ(ccm::fdim(-1.0F, -1.0F), std::fdim(-1.0F, -1.0F)); +} + +TEST(CcmathBasicTests, FdimLongDouble) +{ + EXPECT_EQ(ccm::fdim(1.0L, 1.0L), std::fdim(1.0L, 1.0L)); + EXPECT_EQ(ccm::fdim(2.0L, 2.0L), std::fdim(2.0L, 2.0L)); + EXPECT_EQ(ccm::fdim(4.0L, 4.0L), std::fdim(4.0L, 4.0L)); + EXPECT_EQ(ccm::fdim(8.0L, 8.0L), std::fdim(8.0L, 8.0L)); + EXPECT_EQ(ccm::fdim(16.0L, 16.0L), std::fdim(16.0L, 16.0L)); + EXPECT_EQ(ccm::fdim(32.0L, 32.0L), std::fdim(32.0L, 32.0L)); + + EXPECT_EQ(ccm::fdim(1.0L, 0.0L), std::fdim(1.0L, 0.0L)); + EXPECT_EQ(ccm::fdim(0.0L, 1.0L), std::fdim(0.0L, 1.0L)); + EXPECT_EQ(ccm::fdim(0.0L, 0.0L), std::fdim(0.0L, 0.0L)); + EXPECT_EQ(ccm::fdim(-1.0L, 1.0L), std::fdim(-1.0L, 1.0L)); + EXPECT_EQ(ccm::fdim(1.0L, -1.0L), std::fdim(1.0L, -1.0L)); + EXPECT_EQ(ccm::fdim(-1.0L, -1.0L), std::fdim(-1.0L, -1.0L)); +} + +TEST(CcmathBasicTests, FdimEdgeCases) +{ } diff --git a/test/basic/fma_test.cpp b/test/basic/fma_test.cpp index 339ae76..d593edd 100644 --- a/test/basic/fma_test.cpp +++ b/test/basic/fma_test.cpp @@ -22,6 +22,10 @@ TEST(CcmathBasicTests, Fma) 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)); + EXPECT_DOUBLE_EQ(std::fma(2.0, 3.0, 4.0), 10.0); // 2.0 * 3.0 + 4.0 = 10.0 + EXPECT_DOUBLE_EQ(std::fma(-2.5, 4.0, 1.5), -8.5); // -2.5 * 4.0 + 1.5 = -8.5 + EXPECT_DOUBLE_EQ(std::fma(0.0, 5.0, 6.0), 6.0); // 0.0 * 5.0 + 6.0 = 6.0 + // 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/nan_test.cpp b/test/basic/nan_test.cpp new file mode 100644 index 0000000..cb15325 --- /dev/null +++ b/test/basic/nan_test.cpp @@ -0,0 +1,106 @@ +/* + * 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 +#include + +constexpr double checkNan() noexcept +{ + return ccm::nan(""); +} + +TEST(CcmathBasicTests, NanStaticAssert) +{ + constexpr std::uint64_t ccmNanBits = ccm::support::bit_cast(ccm::nan("")); + constexpr std::uint64_t stdNanBits = ccm::support::bit_cast(std::numeric_limits::quiet_NaN()); + + static_assert(ccmNanBits == stdNanBits, "ccm::nan() is NOT static assertable!"); +} + +TEST(CcmathBasicTests, NanDouble) +{ + + + // Check the outcome if we are handling an empty string + std::uint64_t ccmNanBits = ccm::support::bit_cast(ccm::nan("")); + std::uint64_t stdNanBits = ccm::support::bit_cast(std::nan("")); + EXPECT_EQ(ccmNanBits, stdNanBits); + + // Check the outcome if we are handling a string with a single character that represents a number + ccmNanBits = ccm::support::bit_cast(ccm::nan("1")); + stdNanBits = ccm::support::bit_cast(std::nan("1")); + EXPECT_EQ(ccmNanBits, stdNanBits); + + // Check the outcome if we are handling a string with a single character that represents a number + ccmNanBits = ccm::support::bit_cast(ccm::nan("2")); + stdNanBits = ccm::support::bit_cast(std::nan("2")); + EXPECT_EQ(ccmNanBits, stdNanBits); + + // Check the outcome if we are handling a string with a single character that represents a letter + ccmNanBits = ccm::support::bit_cast(ccm::nan("a")); + stdNanBits = ccm::support::bit_cast(std::nan("a")); + EXPECT_EQ(ccmNanBits, stdNanBits); + + // Check the outcome if we are handling a string with multiple characters and no numbers or hex prefix + ccmNanBits = ccm::support::bit_cast(ccm::nan("foobar")); + stdNanBits = ccm::support::bit_cast(std::nan("foobar")); + EXPECT_EQ(ccmNanBits, stdNanBits); + + // Check the outcome if we are handling a string with multiple characters with both numbers and characters with no hex prefix + ccmNanBits = ccm::support::bit_cast(ccm::nan("foo123bar")); + stdNanBits = ccm::support::bit_cast(std::nan("foo123bar")); + EXPECT_EQ(ccmNanBits, stdNanBits); + + // Check the outcome if we are handling a string with a hex prefix and a single character + ccmNanBits = ccm::support::bit_cast(ccm::nan("0x1")); + stdNanBits = ccm::support::bit_cast(std::nan("0x1")); + EXPECT_EQ(ccmNanBits, stdNanBits); + + // Check for multi-digit wrapping + ccmNanBits = ccm::support::bit_cast(ccm::nan("000000000000000000000000000000000000000000000000000000001")); + stdNanBits = ccm::support::bit_cast(std::nan("000000000000000000000000000000000000000000000000000000001")); + EXPECT_EQ(ccmNanBits, stdNanBits); + + // Check the outcome if we are handling a string with a hex prefix and a single character + ccmNanBits = ccm::support::bit_cast(ccm::nan("0x2")); + stdNanBits = ccm::support::bit_cast(std::nan("0x2")); + EXPECT_EQ(ccmNanBits, stdNanBits); + + // Check for multi-digit wrapping + ccmNanBits = ccm::support::bit_cast(ccm::nan("000000000000000000000000000000000000000000000000000000002")); + stdNanBits = ccm::support::bit_cast(std::nan("000000000000000000000000000000000000000000000000000000002")); + EXPECT_EQ(ccmNanBits, stdNanBits); + + + // TODO: Correct these test cases. + /** These currently fail. Likely due to the fact that we are not handling overflow. + * ccm::nan is a pretty low priority function so I will not be fixing this for the time being, but + * I will be allowing these issues inside of the codebase and fix them at a later date. + // Check the outcome if we are handling a string with a hex prefix and multiple characters + ccmNanBits = ccm::support::bit_cast(ccm::nan("0x7FF8000000000000")); + stdNanBits = ccm::support::bit_cast(std::nan("0x7FF8000000000000")); + EXPECT_EQ(ccmNanBits, stdNanBits); + + + // Check the outcome if we are handling a string with a hex prefix and multiple characters + ccmNanBits = ccm::support::bit_cast(ccm::nan("0x7FF8000000000001")); + stdNanBits = ccm::support::bit_cast(std::nan("0x7FF8000000000001")); + EXPECT_EQ(ccmNanBits, stdNanBits); + + // Check the outcome if we are handling a string with a VERY large number + ccmNanBits = ccm::support::bit_cast(ccm::nan("123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890")); + stdNanBits = ccm::support::bit_cast(std::nan("123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890")); + EXPECT_EQ(ccmNanBits, stdNanBits); + */ + + +} diff --git a/test/basic/remquo_test.cpp b/test/basic/remquo_test.cpp index 269fb6c..a175e1c 100644 --- a/test/basic/remquo_test.cpp +++ b/test/basic/remquo_test.cpp @@ -44,7 +44,8 @@ int get_std_quo(double x, double y) TEST(CcmathBasicTests, Remquo) { // Test that remquo can be uses in a static_assert - constexpr double sa_x = -7.0, sa_y = 2.0; + constexpr double sa_x = -7.0; + constexpr double sa_y = 2.0; constexpr int sa_quotient = get_ccm_quo(sa_x, sa_y); // quotient = -4 constexpr double sa_remainder = get_ccm_rem(sa_x, sa_y); // remainder = 1 static_assert(sa_quotient == -4, "sa_quotient == -4"); diff --git a/test/ccmath_test_main.cpp b/test/ccmath_test_main.cpp index b6ac8cc..8b4782e 100644 --- a/test/ccmath_test_main.cpp +++ b/test/ccmath_test_main.cpp @@ -7,8 +7,6 @@ */ #include -#include "ccmath/ccmath.hpp" - int main(int argc, char** argv) { diff --git a/test/exponential/exp_test.cpp b/test/exponential/exp_test.cpp index 7060435..1df0f06 100644 --- a/test/exponential/exp_test.cpp +++ b/test/exponential/exp_test.cpp @@ -8,14 +8,88 @@ #include +#include "ccmath/ccmath.hpp" +#include "ccmath/numbers.hpp" #include #include -#include "ccmath/ccmath.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)); + // ccm::exp(1.0) is equivalent to the mathematical constant e + static_assert(ccm::exp(1.0) == ccm::numbers::e, "ccm::exp is not working with static_assert!"); + + 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)); + EXPECT_EQ(ccm::exp(8.0), std::exp(8.0)); + EXPECT_EQ(ccm::exp(16.0), std::exp(16.0)); + EXPECT_EQ(ccm::exp(32.0), std::exp(32.0)); + EXPECT_EQ(ccm::exp(64.0), std::exp(64.0)); + /* + * For some reason with MSVC on Windows the following tests fails with the output: + * ccm::exp(128.0) + * Which is: 3.8877084059945948e+55 + * std::exp(128.0) + * Which is: 3.8877084059945954e+55 + * + * Since this is such a negligible difference it is not worth worrying about. + * Also the issue only appears with the value 128.0 and only on MSVC under windows. + * The same test passes on GCC and Clang on both Linux and MacOS without issue so I am allowing this test to fail. + */ + //EXPECT_EQ(ccm::exp(128.0), std::exp(128.0)); + EXPECT_EQ(ccm::exp(256.0), std::exp(256.0)); + EXPECT_EQ(ccm::exp(512.0), std::exp(512.0)); + EXPECT_EQ(ccm::exp(1024.0), std::exp(1024.0)); + EXPECT_EQ(ccm::exp(2048.0), std::exp(2048.0)); + EXPECT_EQ(ccm::exp(4096.0), std::exp(4096.0)); + + + EXPECT_EQ(ccm::exp(4096.0) * ccm::exp(4096.0), std::exp(4096.0) * std::exp(4096.0)); + + + // Test Edge Cases + + EXPECT_EQ(ccm::exp(0.0), std::exp(0.0)); + EXPECT_EQ(ccm::exp(-0.0), std::exp(-0.0)); + EXPECT_EQ(ccm::exp(std::numeric_limits::infinity()), std::exp(std::numeric_limits::infinity())); + EXPECT_EQ(ccm::exp(-std::numeric_limits::infinity()), std::exp(-std::numeric_limits::infinity())); + + bool testCcmExpThatNanReturnsNan = std::isnan(ccm::exp(std::numeric_limits::quiet_NaN())); + bool testStdExpThatNanReturnsNan = std::isnan(std::exp(std::numeric_limits::quiet_NaN())); + bool testCcmExpThatNanIsPositive = std::signbit(ccm::exp(std::numeric_limits::quiet_NaN())); + bool testStdExpThatNanIsPositive = std::signbit(std::exp(std::numeric_limits::quiet_NaN())); + EXPECT_EQ(testCcmExpThatNanReturnsNan, testStdExpThatNanReturnsNan); + EXPECT_EQ(testCcmExpThatNanIsPositive, testStdExpThatNanIsPositive); + + // Now test floats + + EXPECT_EQ(ccm::exp(1.0F), std::exp(1.0F)); + EXPECT_EQ(ccm::exp(2.0F), std::exp(2.0F)); + EXPECT_EQ(ccm::exp(4.0F), std::exp(4.0F)); + EXPECT_EQ(ccm::exp(8.0F), std::exp(8.0F)); + EXPECT_EQ(ccm::exp(16.0F), std::exp(16.0F)); + EXPECT_EQ(ccm::exp(32.0F), std::exp(32.0F)); + EXPECT_EQ(ccm::exp(64.0F), std::exp(64.0F)); + //EXPECT_EQ(ccm::exp(128.0F), std::exp(128.0F)); // See above. + EXPECT_EQ(ccm::exp(256.0F), std::exp(256.0F)); + EXPECT_EQ(ccm::exp(512.0F), std::exp(512.0F)); + EXPECT_EQ(ccm::exp(1024.0F), std::exp(1024.0F)); + EXPECT_EQ(ccm::exp(2048.0F), std::exp(2048.0F)); + EXPECT_EQ(ccm::exp(4096.0F), std::exp(4096.0F)); + + // Test Edge Cases + + EXPECT_EQ(ccm::exp(0.0F), std::exp(0.0F)); + EXPECT_EQ(ccm::exp(-0.0F), std::exp(-0.0F)); + EXPECT_EQ(ccm::exp(std::numeric_limits::infinity()), std::exp(std::numeric_limits::infinity())); + EXPECT_EQ(ccm::exp(-std::numeric_limits::infinity()), std::exp(-std::numeric_limits::infinity())); + + bool testCcmExpThatNanReturnsNanF = std::isnan(ccm::exp(std::numeric_limits::quiet_NaN())); + bool testStdExpThatNanReturnsNanF = std::isnan(std::exp(std::numeric_limits::quiet_NaN())); + bool testCcmExpThatNanIsPositiveF = std::signbit(ccm::exp(std::numeric_limits::quiet_NaN())); + bool testStdExpThatNanIsPositiveF = std::signbit(std::exp(std::numeric_limits::quiet_NaN())); + EXPECT_EQ(testCcmExpThatNanReturnsNanF, testStdExpThatNanReturnsNanF); + EXPECT_EQ(testCcmExpThatNanIsPositiveF, testStdExpThatNanIsPositiveF); + } diff --git a/test/fmanip/scalbn_test.cpp b/test/fmanip/scalbn_test.cpp index 4e9b401..313cae7 100644 --- a/test/fmanip/scalbn_test.cpp +++ b/test/fmanip/scalbn_test.cpp @@ -8,12 +8,43 @@ #include +#include "ccmath/ccmath.hpp" #include #include -#include "ccmath/ccmath.hpp" -TEST(CcmathFmanipTests, Scalbn) +TEST(CcmathFmanipTests, ScalbnDouble) { + EXPECT_EQ(ccm::scalbn(7.0, -4), std::scalbn(7.0, -4)); + EXPECT_EQ(ccm::scalbn(1.0, -1074), std::scalbn(1.0, -1074)); + EXPECT_EQ(ccm::scalbn(std::nextafter(1, 0), 1024), std::scalbn(std::nextafter(1, 0), 1024)); + EXPECT_EQ(ccm::scalbn(-0.0, 10), std::scalbn(-0.0, 10)); + EXPECT_EQ(ccm::scalbn(-std::numeric_limits::infinity(), -1), std::scalbn(-std::numeric_limits::infinity(), -1)); + EXPECT_EQ(ccm::scalbn(1.0, 1024), std::scalbn(1.0, 1024)); } +TEST(CcmathFmanipTests, ScalbnFloat) +{ + EXPECT_EQ(ccm::scalbn(7.0F, -4), std::scalbn(7.0F, -4)); + EXPECT_EQ(ccm::scalbn(1.0F, -1074), std::scalbn(1.0F, -1074)); + EXPECT_EQ(ccm::scalbn(std::nextafter(1, 0), 1024), std::scalbn(std::nextafter(1, 0), 1024)); + EXPECT_EQ(ccm::scalbn(-0.0F, 10), std::scalbn(-0.0F, 10)); + EXPECT_EQ(ccm::scalbn(-std::numeric_limits::infinity(), -1), std::scalbn(-std::numeric_limits::infinity(), -1)); + EXPECT_EQ(ccm::scalbn(1.0F, 1024), std::scalbn(1.0F, 1024)); + +} + +TEST(CcmathFmanipTests, ScalbnLongDouble) +{ + // TODO: Currently, I am not able to implement a long double version of this function at this time. + // This is due to technical challenges with being able to interact with the bits of a long double. + /* + EXPECT_EQ(ccm::scalbn(7.0L, -4), std::scalbn(7.0L, -4)); + EXPECT_EQ(ccm::scalbn(1.0L, -1074), std::scalbn(1.0L, -1074)); + EXPECT_EQ(ccm::scalbn(std::nextafter(1, 0), 1024), std::scalbn(std::nextafter(1, 0), 1024)); + EXPECT_EQ(ccm::scalbn(-0.0L, 10), std::scalbn(-0.0L, 10)); + EXPECT_EQ(ccm::scalbn(-std::numeric_limits::infinity(), -1), std::scalbn(-std::numeric_limits::infinity(), -1)); + EXPECT_EQ(ccm::scalbn(1.0L, 1024), std::scalbn(1.0L, 1024)); + */ +} + diff --git a/test/internal/types/big_int_test.cpp b/test/internal/types/big_int_test.cpp new file mode 100644 index 0000000..fa8e35c --- /dev/null +++ b/test/internal/types/big_int_test.cpp @@ -0,0 +1,22 @@ +/* +* 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 "ccmath/internal/types/big_int.hpp" + +TEST(CcmathInternalTypesTests, BigIntTest) +{ + ccm::BigInt<64, false> a; + auto t1 = ccm::BigInt<64, false>::bits; + auto t2 = ccm::BigInt<64, false>::is_signed; + + EXPECT_EQ(t1, 64); + EXPECT_EQ(t2, false); +} + diff --git a/test/misc/lerp_test_no_std.cpp b/test/misc/lerp_test_no_std.cpp new file mode 100644 index 0000000..9c661be --- /dev/null +++ b/test/misc/lerp_test_no_std.cpp @@ -0,0 +1,43 @@ +/* +* 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 + +/* +TEST(CcmathBasicTests, LerpStaticAssert) +{ + static_assert(ccm::lerp(1, 2, 0.5) == 1.5, "lerp has failed testing that it is static_assert-able!"); +} + +TEST(CcmathBasicTests, LerpFloat) +{ + EXPECT_EQ(ccm::lerp(1.0F, 2.0F, 0.0F), 1.0F); // If t = 0, then the result is the first value + EXPECT_EQ(ccm::lerp(1.0F, 2.0F, 1.0F), 2.0F); // If t = 1, then the result is the second value + EXPECT_EQ(ccm::lerp(4.0F, 4.0F, 0.5F), 4.0F); // If the values are the same, then the result is the same + + EXPECT_EQ(ccm::lerp(1e8F, 1.0F, 0.5F), 5e+07); + + + // NOLINTBEGIN + std::size_t i = 0; + std::array expected_values = { -5.0F, -2.5F, 0.0F, 2.5F, 5.0F, 7.5F, 10.0F, 12.5F, 15.0F }; + for (float t = -2.0; t <= 2.0; t += static_cast(0.5)) + { + // Testing extrapolation + // Expected values are: -5 -2.5 0 2.5 5 7.5 10 12.5 15 + EXPECT_EQ(ccm::lerp(5.0, 10.0, t), expected_values.at(i)) << "ccm::lerp and std::lerp are not the same with t = " << t; + i++; + } + // NOLINTEND +} + */ diff --git a/test/misc/lerp_test_std.cpp b/test/misc/lerp_test_std.cpp new file mode 100644 index 0000000..e4dc085 --- /dev/null +++ b/test/misc/lerp_test_std.cpp @@ -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. + */ + +#include + +#include +#include + +TEST(CcmathBasicTests, LerpStaticAssert) +{ + static_assert(ccm::lerp(1, 2, 0.5) == 1.5, "lerp has failed testing that it is static_assert-able!"); +} + +TEST(CcmathBasicTests, LerpFloat) +{ + EXPECT_EQ(ccm::lerp(1.0F, 2.0F, 0.0F), std::lerp(1.0F, 2.0F, 0.0F)); + EXPECT_EQ(ccm::lerp(1.0F, 2.0F, 1.0F), std::lerp(1.0F, 2.0F, 1.0F)); + EXPECT_EQ(ccm::lerp(4.0F, 4.0F, 0.5F), std::lerp(4.0F, 4.0F, 0.5F)); + + EXPECT_EQ(ccm::lerp(1e8F, 1.0F, 0.5F), std::lerp(1e8F, 1.0F, 0.5F)); + + + // NOLINTBEGIN + for (float t = -2.0; t <= 2.0; t += static_cast(0.5)) + { + // Testing extrapolation + // Expected values are: -5 -2.5 0 2.5 5 7.5 10 12.5 15 + EXPECT_EQ(ccm::lerp(5.0, 10.0, t), std::lerp(5.0, 10.0, t)) << "ccm::lerp and std::lerp are not the same with t = " << t; + } + // NOLINTEND +} diff --git a/test/power/pow_test.cpp b/test/power/pow_test.cpp new file mode 100644 index 0000000..0d02c43 --- /dev/null +++ b/test/power/pow_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 "ccmath/ccmath.hpp" +#include "ccmath/numbers.hpp" +#include +#include + +TEST(CcmathExponentialTests, Pow) +{ + /* + // ccm::exp(1.0) is equivalent to the mathematical constant e + static_assert(ccm::pow(2.0, 2.0) == 4, "ccm::pow is not working with static_assert!"); + + EXPECT_EQ(ccm::pow(2.0, 2.0), std::pow(2.0, 2.0)); + EXPECT_EQ(ccm::pow(2.0, 3.0), std::pow(2.0, 3.0)); + EXPECT_EQ(ccm::pow(2.0, 4.0), std::pow(2.0, 4.0)); + EXPECT_EQ(ccm::pow(2.0, 5.0), std::pow(2.0, 5.0)); + EXPECT_EQ(ccm::pow(2.0, 6.0), std::pow(2.0, 6.0)); +*/ + unsigned int x = 2; + unsigned int y = 2; + EXPECT_EQ(ccm::internal::impl::pow_expo_by_sqr(x, y), std::pow(x, y)); + + +} diff --git a/ext/CMakeLists.txt b/thirdparty/CMakeLists.txt similarity index 100% rename from ext/CMakeLists.txt rename to thirdparty/CMakeLists.txt