| /* |
| * Copyright 2006 The Android Open Source Project |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #ifndef SkFloatingPoint_DEFINED |
| #define SkFloatingPoint_DEFINED |
| |
| #include "include/private/base/SkAttributes.h" |
| #include "include/private/base/SkMath.h" |
| |
| #include <cmath> |
| #include <cstdint> |
| #include <limits> |
| #include <type_traits> |
| |
| inline constexpr float SK_FloatSqrt2 = 1.41421356f; |
| inline constexpr float SK_FloatPI = 3.14159265f; |
| inline constexpr double SK_DoublePI = 3.14159265358979323846264338327950288; |
| |
| static constexpr int sk_float_sgn(float x) { |
| return (0.0f < x) - (x < 0.0f); |
| } |
| |
| static constexpr float sk_float_degrees_to_radians(float degrees) { |
| return degrees * (SK_FloatPI / 180); |
| } |
| |
| static constexpr float sk_float_radians_to_degrees(float radians) { |
| return radians * (180 / SK_FloatPI); |
| } |
| |
| // floor(double+0.5) vs. floorf(float+0.5f) give comparable performance, but upcasting to double |
| // means tricky values like 0.49999997 and 2^24 get rounded correctly. If these were rounded |
| // as floatf(x + .5f), they would be 1 higher than expected. |
| #define sk_float_round(x) (float)sk_double_round((double)(x)) |
| |
| template <typename T, std::enable_if_t<std::is_floating_point_v<T>, bool> = true> |
| static inline bool SkIsNaN(T x) { |
| return x != x; |
| } |
| |
| // Subtracting a value from itself will result in zero, except for NAN or ±Inf, which make NAN. |
| // Multiplying a group of values against zero will result in zero for each product, except for |
| // NAN or ±Inf, which will result in NAN and continue resulting in NAN for the rest of the elements. |
| // This generates better code than `std::isfinite` when building with clang-cl (April 2024). |
| template <typename T, typename... Pack, std::enable_if_t<std::is_floating_point_v<T>, bool> = true> |
| static inline bool SkIsFinite(T x, Pack... values) { |
| T prod = x - x; |
| prod = (prod * ... * values); |
| // At this point, `prod` will either be NaN or 0. |
| return prod == prod; |
| } |
| |
| template <typename T, std::enable_if_t<std::is_floating_point_v<T>, bool> = true> |
| static inline bool SkIsFinite(const T array[], int count) { |
| T x = array[0]; |
| T prod = x - x; |
| for (int i = 1; i < count; ++i) { |
| prod *= array[i]; |
| } |
| // At this point, `prod` will either be NaN or 0. |
| return prod == prod; |
| } |
| |
| inline constexpr int SK_MaxS32FitsInFloat = 2147483520; |
| inline constexpr int SK_MinS32FitsInFloat = -SK_MaxS32FitsInFloat; |
| |
| // 0x7fffff8000000000 |
| inline constexpr int64_t SK_MaxS64FitsInFloat = SK_MaxS64 >> (63-24) << (63-24); |
| inline constexpr int64_t SK_MinS64FitsInFloat = -SK_MaxS64FitsInFloat; |
| |
| /** |
| * Return the closest int for the given float. Returns SK_MaxS32FitsInFloat for NaN. |
| */ |
| static constexpr int sk_float_saturate2int(float x) { |
| x = x < SK_MaxS32FitsInFloat ? x : SK_MaxS32FitsInFloat; |
| x = x > SK_MinS32FitsInFloat ? x : SK_MinS32FitsInFloat; |
| return (int)x; |
| } |
| |
| /** |
| * Return the closest int for the given double. Returns SK_MaxS32 for NaN. |
| */ |
| static constexpr int sk_double_saturate2int(double x) { |
| x = x < SK_MaxS32 ? x : SK_MaxS32; |
| x = x > SK_MinS32 ? x : SK_MinS32; |
| return (int)x; |
| } |
| |
| /** |
| * Return the closest int64_t for the given float. Returns SK_MaxS64FitsInFloat for NaN. |
| */ |
| static constexpr int64_t sk_float_saturate2int64(float x) { |
| x = x < SK_MaxS64FitsInFloat ? x : SK_MaxS64FitsInFloat; |
| x = x > SK_MinS64FitsInFloat ? x : SK_MinS64FitsInFloat; |
| return (int64_t)x; |
| } |
| |
| #define sk_float_floor2int(x) sk_float_saturate2int(std::floor(x)) |
| #define sk_float_round2int(x) sk_float_saturate2int(sk_float_round(x)) |
| #define sk_float_ceil2int(x) sk_float_saturate2int(std::ceil(x)) |
| |
| #define sk_float_floor2int_no_saturate(x) ((int)std::floor(x)) |
| #define sk_float_round2int_no_saturate(x) ((int)sk_float_round(x)) |
| #define sk_float_ceil2int_no_saturate(x) ((int)std::ceil(x)) |
| |
| #define sk_double_round(x) (std::floor((x) + 0.5)) |
| #define sk_double_floor2int(x) ((int)std::floor(x)) |
| #define sk_double_round2int(x) ((int)std::round(x)) |
| #define sk_double_ceil2int(x) ((int)std::ceil(x)) |
| |
| // Cast double to float, ignoring any warning about too-large finite values being cast to float. |
| // Clang thinks this is undefined, but it's actually implementation defined to return either |
| // the largest float or infinity (one of the two bracketing representable floats). Good enough! |
| SK_NO_SANITIZE("float-cast-overflow") |
| static constexpr float sk_double_to_float(double x) { |
| return static_cast<float>(x); |
| } |
| |
| inline constexpr float SK_FloatNaN = std::numeric_limits<float>::quiet_NaN(); |
| inline constexpr float SK_FloatInfinity = std::numeric_limits<float>::infinity(); |
| inline constexpr float SK_FloatNegativeInfinity = -SK_FloatInfinity; |
| |
| inline constexpr double SK_DoubleNaN = std::numeric_limits<double>::quiet_NaN(); |
| |
| // Calculate the midpoint between a and b. Similar to std::midpoint in c++20. |
| static constexpr float sk_float_midpoint(float a, float b) { |
| // Use double math to avoid underflow and overflow. |
| return static_cast<float>(0.5 * (static_cast<double>(a) + b)); |
| } |
| |
| static inline float sk_float_rsqrt_portable(float x) { return 1.0f / std::sqrt(x); } |
| static inline float sk_float_rsqrt (float x) { return 1.0f / std::sqrt(x); } |
| |
| // IEEE defines how float divide behaves for non-finite values and zero-denoms, but C does not, |
| // so we have a helper that suppresses the possible undefined-behavior warnings. |
| #ifdef SK_BUILD_FOR_WIN |
| #pragma warning(push) |
| #pragma warning(disable : 4723) |
| #endif |
| SK_NO_SANITIZE("float-divide-by-zero") |
| static constexpr float sk_ieee_float_divide(float numer, float denom) { |
| return numer / denom; |
| } |
| |
| SK_NO_SANITIZE("float-divide-by-zero") |
| static constexpr double sk_ieee_double_divide(double numer, double denom) { |
| return numer / denom; |
| } |
| #ifdef SK_BUILD_FOR_WIN |
| #pragma warning( pop ) |
| #endif |
| |
| // Returns true iff the provided number is within a small epsilon of 0. |
| bool sk_double_nearly_zero(double a); |
| |
| // Compare two doubles and return true if they are within maxUlpsDiff of each other. |
| // * nan as a or b - returns false. |
| // * infinity, infinity or -infinity, -infinity - returns true. |
| // * infinity and any other number - returns false. |
| // |
| // ulp is an initialism for Units in the Last Place. |
| bool sk_doubles_nearly_equal_ulps(double a, double b, uint8_t maxUlpsDiff = 16); |
| |
| #endif |