xref: /aosp_15_r20/external/skia/include/private/base/SkFloatingPoint.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2006 The Android Open Source Project
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #ifndef SkFloatingPoint_DEFINED
9 #define SkFloatingPoint_DEFINED
10 
11 #include "include/private/base/SkAttributes.h"
12 #include "include/private/base/SkMath.h"
13 
14 #include <cmath>
15 #include <cstdint>
16 #include <limits>
17 #include <type_traits>
18 
19 inline constexpr float SK_FloatSqrt2 = 1.41421356f;
20 inline constexpr float SK_FloatPI    = 3.14159265f;
21 inline constexpr double SK_DoublePI  = 3.14159265358979323846264338327950288;
22 
sk_float_sgn(float x)23 static constexpr int sk_float_sgn(float x) {
24     return (0.0f < x) - (x < 0.0f);
25 }
26 
sk_float_degrees_to_radians(float degrees)27 static constexpr float sk_float_degrees_to_radians(float degrees) {
28     return degrees * (SK_FloatPI / 180);
29 }
30 
sk_float_radians_to_degrees(float radians)31 static constexpr float sk_float_radians_to_degrees(float radians) {
32     return radians * (180 / SK_FloatPI);
33 }
34 
35 // floor(double+0.5) vs. floorf(float+0.5f) give comparable performance, but upcasting to double
36 // means tricky values like 0.49999997 and 2^24 get rounded correctly. If these were rounded
37 // as floatf(x + .5f), they would be 1 higher than expected.
38 #define sk_float_round(x) (float)sk_double_round((double)(x))
39 
40 template <typename T, std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
SkIsNaN(T x)41 static inline constexpr bool SkIsNaN(T x) {
42     return x != x;
43 }
44 
45 // Subtracting a value from itself will result in zero, except for NAN or ±Inf, which make NAN.
46 // Multiplying a group of values against zero will result in zero for each product, except for
47 // NAN or ±Inf, which will result in NAN and continue resulting in NAN for the rest of the elements.
48 // This generates better code than `std::isfinite` when building with clang-cl (April 2024).
49 template <typename T, typename... Pack, std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
SkIsFinite(T x,Pack...values)50 static inline bool SkIsFinite(T x, Pack... values) {
51     T prod = x - x;
52     prod = (prod * ... * values);
53     // At this point, `prod` will either be NaN or 0.
54     return prod == prod;
55 }
56 
57 template <typename T, std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
SkIsFinite(const T array[],int count)58 static inline bool SkIsFinite(const T array[], int count) {
59     T x = array[0];
60     T prod = x - x;
61     for (int i = 1; i < count; ++i) {
62         prod *= array[i];
63     }
64     // At this point, `prod` will either be NaN or 0.
65     return prod == prod;
66 }
67 
68 inline constexpr int SK_MaxS32FitsInFloat = 2147483520;
69 inline constexpr int SK_MinS32FitsInFloat = -SK_MaxS32FitsInFloat;
70 
71 // 0x7fffff8000000000
72 inline constexpr int64_t SK_MaxS64FitsInFloat = SK_MaxS64 >> (63-24) << (63-24);
73 inline constexpr int64_t SK_MinS64FitsInFloat = -SK_MaxS64FitsInFloat;
74 
75 // sk_[float|double]_saturate2int are written to return their maximum values when passed NaN.
76 // MSVC 19.38+ has a bug with this implementation, leading to incorrect results:
77 // https://developercommunity.visualstudio.com/t/Optimizer-incorrectly-handles-NaN-floati/10654403
78 //
79 // We inject an explicit NaN test on MSVC to work around the problem.
80 #if defined(_MSC_VER) && !defined(__clang__)
81     #define SK_CHECK_NAN(resultVal) if (SkIsNaN(x)) { return resultVal; }
82 #else
83     #define SK_CHECK_NAN(resultVal)
84 #endif
85 
86 /**
87  *  Return the closest int for the given float. Returns SK_MaxS32FitsInFloat for NaN.
88  */
sk_float_saturate2int(float x)89 static constexpr int sk_float_saturate2int(float x) {
90     SK_CHECK_NAN(SK_MaxS32FitsInFloat)
91     x = x < SK_MaxS32FitsInFloat ? x : SK_MaxS32FitsInFloat;
92     x = x > SK_MinS32FitsInFloat ? x : SK_MinS32FitsInFloat;
93     return (int)x;
94 }
95 
96 /**
97  *  Return the closest int for the given double. Returns SK_MaxS32 for NaN.
98  */
sk_double_saturate2int(double x)99 static constexpr int sk_double_saturate2int(double x) {
100     SK_CHECK_NAN(SK_MaxS32)
101     x = x < SK_MaxS32 ? x : SK_MaxS32;
102     x = x > SK_MinS32 ? x : SK_MinS32;
103     return (int)x;
104 }
105 
106 /**
107  *  Return the closest int64_t for the given float. Returns SK_MaxS64FitsInFloat for NaN.
108  */
sk_float_saturate2int64(float x)109 static constexpr int64_t sk_float_saturate2int64(float x) {
110     SK_CHECK_NAN(SK_MaxS64FitsInFloat)
111     x = x < SK_MaxS64FitsInFloat ? x : SK_MaxS64FitsInFloat;
112     x = x > SK_MinS64FitsInFloat ? x : SK_MinS64FitsInFloat;
113     return (int64_t)x;
114 }
115 
116 #undef SK_CHECK_NAN
117 
118 #define sk_float_floor2int(x)   sk_float_saturate2int(std::floor(x))
119 #define sk_float_round2int(x)   sk_float_saturate2int(sk_float_round(x))
120 #define sk_float_ceil2int(x)    sk_float_saturate2int(std::ceil(x))
121 
122 #define sk_float_floor2int_no_saturate(x)   ((int)std::floor(x))
123 #define sk_float_round2int_no_saturate(x)   ((int)sk_float_round(x))
124 #define sk_float_ceil2int_no_saturate(x)    ((int)std::ceil(x))
125 
126 #define sk_double_round(x)          (std::floor((x) + 0.5))
127 #define sk_double_floor2int(x)      ((int)std::floor(x))
128 #define sk_double_round2int(x)      ((int)std::round(x))
129 #define sk_double_ceil2int(x)       ((int)std::ceil(x))
130 
131 // Cast double to float, ignoring any warning about too-large finite values being cast to float.
132 // Clang thinks this is undefined, but it's actually implementation defined to return either
133 // the largest float or infinity (one of the two bracketing representable floats).  Good enough!
134 SK_NO_SANITIZE("float-cast-overflow")
sk_double_to_float(double x)135 static constexpr float sk_double_to_float(double x) {
136     return static_cast<float>(x);
137 }
138 
139 inline constexpr float SK_FloatNaN = std::numeric_limits<float>::quiet_NaN();
140 inline constexpr float SK_FloatInfinity = std::numeric_limits<float>::infinity();
141 inline constexpr float SK_FloatNegativeInfinity = -SK_FloatInfinity;
142 
143 inline constexpr double SK_DoubleNaN = std::numeric_limits<double>::quiet_NaN();
144 
145 // Calculate the midpoint between a and b. Similar to std::midpoint in c++20.
sk_float_midpoint(float a,float b)146 static constexpr float sk_float_midpoint(float a, float b) {
147     // Use double math to avoid underflow and overflow.
148     return static_cast<float>(0.5 * (static_cast<double>(a) + b));
149 }
150 
sk_float_rsqrt_portable(float x)151 static inline float sk_float_rsqrt_portable(float x) { return 1.0f / std::sqrt(x); }
sk_float_rsqrt(float x)152 static inline float sk_float_rsqrt         (float x) { return 1.0f / std::sqrt(x); }
153 
154 // IEEE defines how float divide behaves for non-finite values and zero-denoms, but C does not,
155 // so we have a helper that suppresses the possible undefined-behavior warnings.
156 #ifdef SK_BUILD_FOR_WIN
157 #pragma warning(push)
158 #pragma warning(disable : 4723)
159 #endif
160 SK_NO_SANITIZE("float-divide-by-zero")
sk_ieee_float_divide(float numer,float denom)161 static constexpr float sk_ieee_float_divide(float numer, float denom) {
162     return numer / denom;
163 }
164 
165 SK_NO_SANITIZE("float-divide-by-zero")
sk_ieee_double_divide(double numer,double denom)166 static constexpr double sk_ieee_double_divide(double numer, double denom) {
167     return numer / denom;
168 }
169 #ifdef SK_BUILD_FOR_WIN
170 #pragma warning( pop )
171 #endif
172 
173 // Returns true iff the provided number is within a small epsilon of 0.
174 bool sk_double_nearly_zero(double a);
175 
176 // Compare two doubles and return true if they are within maxUlpsDiff of each other.
177 // * nan as a or b - returns false.
178 // * infinity, infinity or -infinity, -infinity - returns true.
179 // * infinity and any other number - returns false.
180 //
181 // ulp is an initialism for Units in the Last Place.
182 bool sk_doubles_nearly_equal_ulps(double a, double b, uint8_t maxUlpsDiff = 16);
183 
184 #endif
185