/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include <cassert> #include <cstddef> #include <cstdint> #include <functional> #include <limits> #include <type_traits> #include <folly/Portability.h> #include <folly/lang/CheckedMath.h> #include <folly/portability/Constexpr.h> namespace folly { /// numbers /// /// mimic: std::numbers, C++20 (partial) namespace numbers { namespace detail { enable_if_floating_t; } /// e_v /// /// mimic: std::numbers::e_v, C++20 e_v; /// ln2_v /// /// mimic: std::numbers::ln2_v, C++20 ln2_v; /// e /// /// mimic: std::numbers::e, C++20 inline constexpr double e = …; /// ln2 /// /// mimic: std::numbers::ln2, C++20 inline constexpr double ln2 = …; } // namespace numbers /// floating_point_integral_constant /// /// Like std::integral_constant but for floating-point types holding integral /// values representable in an integral type. template <typename T, typename S, S Value> struct floating_point_integral_constant { … }; // ---- namespace detail { template <typename T> constexpr size_t constexpr_iterated_squares_desc_size_(T const base) { … } } // namespace detail /// constexpr_iterated_squares_desc_size_v /// /// Effectively calculates: floor(log(max_exponent)/log(base)) /// /// For use with constexpr_iterated_squares_desc below. constexpr_iterated_squares_desc_size_v; /// constexpr_iterated_squares_desc /// /// A constexpr scaling array of integer powers-of-powers-of-two, descending, /// with the associated powers-of-two. /// /// scaling = [..., {8, b^8}, {4, b^4}, {2, b^2}, {1, b^1}] for b = base /// /// Includes select constexpr scaling algorithms based on the scaling array. /// /// The scaling array and the scaling algorithms are general-purpose, if niche. /// They may be used by other constexpr math functions (floating-point) either /// to improve runtime performance or to improve numerical approximations. /// /// Some compilers fail to support passing some types as non-type template /// params. In particular, long double is not universally supported. Therefore, /// this utility takes its base as a type rather than as a value. For floating- /// point integral bases, that is, bases of floating-point type but of integral /// value, floating_point_integral_constant is the easiest parameterization. template <typename T, std::size_t Size> struct constexpr_iterated_squares_desc { … }; /// constexpr_iterated_squares_desc_v /// /// An instance of constexpr_iterated_squares_desc of max size with the given /// base. constexpr_iterated_squares_desc_v; /// constexpr_iterated_squares_desc_2_v /// /// An alias for constexpr_iterated_squares_desc_v with base 2, which is the /// most common base to use with iterated-squares. constexpr_iterated_squares_desc_2_v; // TLDR: Prefer using operator< for ordering. And when // a and b are equivalent objects, we return b to make // sorting stable. // See http://stepanovpapers.com/notes.pdf for details. template <typename T, typename... Ts> constexpr T constexpr_max(T a, Ts... ts) { … } // When a and b are equivalent objects, we return a to // make sorting stable. template <typename T, typename... Ts> constexpr T constexpr_min(T a, Ts... ts) { … } template <typename T, typename Less> constexpr T const& constexpr_clamp( T const& v, T const& lo, T const& hi, Less less) { … } template <typename T> constexpr T const& constexpr_clamp(T const& v, T const& lo, T const& hi) { … } template <typename T> constexpr bool constexpr_isnan(T const t) { … } namespace detail { template <typename T, typename = void> struct constexpr_abs_helper { … }; constexpr_abs_helper<T, typename std::enable_if<std::is_floating_point<T>::value>::type>; constexpr_abs_helper<T, typename std::enable_if<std::is_integral<T>::value && !std::is_same<T, bool>::value && std::is_unsigned<T>::value>::type>; constexpr_abs_helper<T, typename std::enable_if<std::is_integral<T>::value && !std::is_same<T, bool>::value && std::is_signed<T>::value>::type>; } // namespace detail template <typename T> constexpr auto constexpr_abs(T t) -> decltype(detail::constexpr_abs_helper<T>::go(t)) { … } namespace detail { template <typename T> constexpr T constexpr_log2_(T a, T e) { … } template <typename T> constexpr T constexpr_log2_ceil_(T l2, T t) { … } } // namespace detail template <typename T> constexpr T constexpr_log2(T t) { … } template <typename T> constexpr T constexpr_log2_ceil(T t) { … } /// constexpr_trunc /// /// mimic: std::trunc (C++23) template < typename T, std::enable_if_t<std::is_floating_point<T>::value, int> = 0> constexpr T constexpr_trunc(T const t) { … } template <typename T, std::enable_if_t<std::is_integral<T>::value, int> = 0> constexpr T constexpr_trunc(T const t) { … } /// constexpr_round /// /// mimic: std::round (C++23) template <typename T> constexpr T constexpr_round(T const t) { … } /// constexpr_floor /// /// mimic: std::floor (C++23) template <typename T> constexpr T constexpr_floor(T const t) { … } /// constexpr_ceil /// /// mimic: std::ceil (C++23) template <typename T> constexpr T constexpr_ceil(T const t) { … } /// constexpr_ceil /// /// The least integer at least t that round divides. template <typename T> constexpr T constexpr_ceil(T t, T round) { … } /// constexpr_mult /// /// Multiply two values, allowing for constexpr floating-pooint overflow to /// infinity. template <typename T> constexpr T constexpr_mult(T const a, T const b) { … } namespace detail { template < typename T, typename E, std::enable_if_t<std::is_signed<E>::value, int> = 1> constexpr T constexpr_ipow(T const base, E const exp) { … } template < typename T, typename E, std::enable_if_t<std::is_unsigned<E>::value, int> = 1> constexpr T constexpr_ipow(T const base, E const exp) { … } } // namespace detail /// constexpr_exp /// /// Calculates an approximation of the mathematical function exp(num). Usable in /// constant evaluations. Like std::exp, which becomes constexpr in C++26. /// /// The integer overload uses iterated squaring and multiplication. The /// floating-point overlaod naively evaluates the taylor series of exp(num) /// until approximate convergence. /// /// mimic: std::exp (C++23, C++26) template < typename T, typename N, std::enable_if_t< std::is_floating_point<T>::value && std::is_integral<N>::value && !std::is_same<N, bool>::value, int> = 0> constexpr T constexpr_exp(N const power) { … } template < typename N, std::enable_if_t< std::is_integral<N>::value && !std::is_same<N, bool>::value, int> = 0> constexpr double constexpr_exp(N const power) { … } template < typename T, std::enable_if_t<std::is_floating_point<T>::value, int> = 0> constexpr T constexpr_exp(T const power) { … } /// constexpr_log /// /// Calculates an approximation of the natural logarithm ln(num). /// /// The implementation uses a quickly-converging, high-precision iterative /// technique as described in: /// https://en.wikipedia.org/wiki/Natural_logarithm#High_precision /// /// The technique works best with numbers that are close enough to 1, so the /// implementation uses a quick shrink/growth technique as described in: /// https://en.wikipedia.org/wiki/Natural_logarithm#Efficient_computation template < typename T, std::enable_if_t<std::is_floating_point<T>::value, int> = 0> constexpr T constexpr_log(T const num) { … } /// constexpr_pow /// /// Calculates an approximation of the value of base raised to the exponent exp. /// /// The implementation uses iterated squaring and multiplication for the integer /// part of the exponent and uses the identity x^y = exp(y * log(x)) for the /// fractional part of the exponent. /// /// Notes: /// * Forbids base of +0 or -0 with finite non-positive exponent: in part since /// the plausible infinite result would be sensitive to the sign of the zero; /// and in part since std::pow would be required or permitted to raise error /// div-by-zero. /// * Forbids finite negative base with finite non-integer exponent: in part /// since std::pow would be required to raise error invalid. /// /// mimic: std::pow (C++26) template < typename T, typename E, std::enable_if_t< std::is_integral<E>::value && !std::is_same<E, bool>::value, int> = 0> constexpr T constexpr_pow(T const base, E const exp) { … } template < typename T, std::enable_if_t<std::is_floating_point<T>::value, int> = 0> constexpr T constexpr_pow(T const base, T const exp) { … } /// constexpr_find_last_set /// /// Return the 1-based index of the most significant bit which is set. /// For x > 0, constexpr_find_last_set(x) == 1 + floor(log2(x)). template <typename T> constexpr std::size_t constexpr_find_last_set(T const t) { … } namespace detail { template <typename U> constexpr std::size_t constexpr_find_first_set_( std::size_t s, std::size_t a, U const u) { … } } // namespace detail /// constexpr_find_first_set /// /// Return the 1-based index of the least significant bit which is set. /// For x > 0, the exponent in the largest power of two which does not divide x. template <typename T> constexpr std::size_t constexpr_find_first_set(T t) { … } template <typename T> constexpr T constexpr_add_overflow_clamped(T a, T b) { … } template <typename T> constexpr T constexpr_sub_overflow_clamped(T a, T b) { … } // clamp_cast<> provides sane numeric conversions from float point numbers to // integral numbers, and between different types of integral numbers. It helps // to avoid unexpected bugs introduced by bad conversion, and undefined behavior // like overflow when casting float point numbers to integral numbers. // // When doing clamp_cast<Dst>(value), if `value` is in valid range of Dst, // it will give correct result in Dst, equal to `value`. // // If `value` is outside the representable range of Dst, it will be clamped to // MAX or MIN in Dst, instead of being undefined behavior. // // Float NaNs are converted to 0 in integral type. // // Here's some comparison with static_cast<>: // (with FB-internal gcc-5-glibc-2.23 toolchain) // // static_cast<int32_t>(NaN) = 6 // clamp_cast<int32_t>(NaN) = 0 // // static_cast<int32_t>(9999999999.0f) = -348639895 // clamp_cast<int32_t>(9999999999.0f) = 2147483647 // // static_cast<int32_t>(2147483647.0f) = -348639895 // clamp_cast<int32_t>(2147483647.0f) = 2147483647 // // static_cast<uint32_t>(4294967295.0f) = 0 // clamp_cast<uint32_t>(4294967295.0f) = 4294967295 // // static_cast<uint32_t>(-1) = 4294967295 // clamp_cast<uint32_t>(-1) = 0 // // static_cast<int16_t>(32768u) = -32768 // clamp_cast<int16_t>(32768u) = 32767 template <typename Dst, typename Src> constexpr typename std::enable_if<std::is_integral<Src>::value, Dst>::type constexpr_clamp_cast(Src src) { … } namespace detail { // Upper/lower bound values that could be accurately represented in both // integral and float point types. constexpr double kClampCastLowerBoundDoubleToInt64F = …; constexpr double kClampCastUpperBoundDoubleToInt64F = …; constexpr double kClampCastUpperBoundDoubleToUInt64F = …; constexpr float kClampCastLowerBoundFloatToInt32F = …; constexpr float kClampCastUpperBoundFloatToInt32F = …; constexpr float kClampCastUpperBoundFloatToUInt32F = …; // This works the same as constexpr_clamp, but the comparison are done in Src // to prevent any implicit promotions. template <typename D, typename S> constexpr D constexpr_clamp_cast_helper(S src, S sl, S su, D dl, D du) { … } } // namespace detail template <typename Dst, typename Src> constexpr typename std::enable_if<std::is_floating_point<Src>::value, Dst>::type constexpr_clamp_cast(Src src) { … } } // namespace folly