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