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 // warning: comparing floating point with == or != is unsafe
46 // storing and comparing against same constants ok.
47 #if defined(__clang__)
48 #pragma clang diagnostic ignored "-Wfloat-equal"
49 #elif defined(__GNUC__)
50 #pragma GCC diagnostic ignored "-Wfloat-equal"
51 #endif
52 // Subtracting a value from itself will result in zero, except for NAN or ±Inf, which make NAN.
53 // Multiplying a group of values against zero will result in zero for each product, except for
54 // NAN or ±Inf, which will result in NAN and continue resulting in NAN for the rest of the elements.
55 // This generates better code than `std::isfinite` when building with clang-cl (April 2024).
56 template <typename T, typename... Pack, std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
SkIsFinite(T x,Pack...values)57 static inline bool SkIsFinite(T x, Pack... values) {
58 T prod = x - x;
59 prod = (prod * ... * values);
60 // At this point, `prod` will either be NaN or 0.
61 return prod == prod;
62 }
63
64 template <typename T, std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
SkIsFinite(const T array[],int count)65 static inline bool SkIsFinite(const T array[], int count) {
66 T x = array[0];
67 T prod = x - x;
68 for (int i = 1; i < count; ++i) {
69 prod *= array[i];
70 }
71 // At this point, `prod` will either be NaN or 0.
72 return prod == prod;
73 }
74
75 inline constexpr int SK_MaxS32FitsInFloat = 2147483520;
76 inline constexpr int SK_MinS32FitsInFloat = -SK_MaxS32FitsInFloat;
77
78 // 0x7fffff8000000000
79 inline constexpr int64_t SK_MaxS64FitsInFloat = SK_MaxS64 >> (63-24) << (63-24);
80 inline constexpr int64_t SK_MinS64FitsInFloat = -SK_MaxS64FitsInFloat;
81
82 // sk_[float|double]_saturate2int are written to return their maximum values when passed NaN.
83 // MSVC 19.38+ has a bug with this implementation, leading to incorrect results:
84 // https://developercommunity.visualstudio.com/t/Optimizer-incorrectly-handles-NaN-floati/10654403
85 //
86 // We inject an explicit NaN test on MSVC to work around the problem.
87 #if defined(_MSC_VER) && !defined(__clang__)
88 #define SK_CHECK_NAN(resultVal) if (SkIsNaN(x)) { return resultVal; }
89 #else
90 #define SK_CHECK_NAN(resultVal)
91 #endif
92
93 /**
94 * Return the closest int for the given float. Returns SK_MaxS32FitsInFloat for NaN.
95 */
sk_float_saturate2int(float x)96 static constexpr int sk_float_saturate2int(float x) {
97 SK_CHECK_NAN(SK_MaxS32FitsInFloat)
98 x = x < SK_MaxS32FitsInFloat ? x : SK_MaxS32FitsInFloat;
99 x = x > SK_MinS32FitsInFloat ? x : SK_MinS32FitsInFloat;
100 return (int)x;
101 }
102
103 /**
104 * Return the closest int for the given double. Returns SK_MaxS32 for NaN.
105 */
sk_double_saturate2int(double x)106 static constexpr int sk_double_saturate2int(double x) {
107 SK_CHECK_NAN(SK_MaxS32)
108 x = x < SK_MaxS32 ? x : SK_MaxS32;
109 x = x > SK_MinS32 ? x : SK_MinS32;
110 return (int)x;
111 }
112
113 /**
114 * Return the closest int64_t for the given float. Returns SK_MaxS64FitsInFloat for NaN.
115 */
sk_float_saturate2int64(float x)116 static constexpr int64_t sk_float_saturate2int64(float x) {
117 SK_CHECK_NAN(SK_MaxS64FitsInFloat)
118 x = x < SK_MaxS64FitsInFloat ? x : SK_MaxS64FitsInFloat;
119 x = x > SK_MinS64FitsInFloat ? x : SK_MinS64FitsInFloat;
120 return (int64_t)x;
121 }
122
123 #undef SK_CHECK_NAN
124
125 #define sk_float_floor2int(x) sk_float_saturate2int(std::floor(x))
126 #define sk_float_round2int(x) sk_float_saturate2int(sk_float_round(x))
127 #define sk_float_ceil2int(x) sk_float_saturate2int(std::ceil(x))
128
129 #define sk_float_floor2int_no_saturate(x) ((int)std::floor(x))
130 #define sk_float_round2int_no_saturate(x) ((int)sk_float_round(x))
131 #define sk_float_ceil2int_no_saturate(x) ((int)std::ceil(x))
132
133 #define sk_double_round(x) (std::floor((x) + 0.5))
134 #define sk_double_floor2int(x) ((int)std::floor(x))
135 #define sk_double_round2int(x) ((int)std::round(x))
136 #define sk_double_ceil2int(x) ((int)std::ceil(x))
137
138 // Cast double to float, ignoring any warning about too-large finite values being cast to float.
139 // Clang thinks this is undefined, but it's actually implementation defined to return either
140 // the largest float or infinity (one of the two bracketing representable floats). Good enough!
141 SK_NO_SANITIZE("float-cast-overflow")
sk_double_to_float(double x)142 static constexpr float sk_double_to_float(double x) {
143 return static_cast<float>(x);
144 }
145
146 inline constexpr float SK_FloatNaN = std::numeric_limits<float>::quiet_NaN();
147 inline constexpr float SK_FloatInfinity = std::numeric_limits<float>::infinity();
148 inline constexpr float SK_FloatNegativeInfinity = -SK_FloatInfinity;
149
150 inline constexpr double SK_DoubleNaN = std::numeric_limits<double>::quiet_NaN();
151
152 // Calculate the midpoint between a and b. Similar to std::midpoint in c++20.
sk_float_midpoint(float a,float b)153 static constexpr float sk_float_midpoint(float a, float b) {
154 // Use double math to avoid underflow and overflow.
155 return static_cast<float>(0.5 * (static_cast<double>(a) + b));
156 }
157
sk_float_rsqrt_portable(float x)158 static inline float sk_float_rsqrt_portable(float x) { return 1.0f / std::sqrt(x); }
sk_float_rsqrt(float x)159 static inline float sk_float_rsqrt (float x) { return 1.0f / std::sqrt(x); }
160
161 // IEEE defines how float divide behaves for non-finite values and zero-denoms, but C does not,
162 // so we have a helper that suppresses the possible undefined-behavior warnings.
163 #if defined(SK_BUILD_FOR_WIN) && defined(_MSC_VER)
164 #pragma warning(push)
165 #pragma warning(disable : 4723)
166 #endif
167 SK_NO_SANITIZE("float-divide-by-zero")
sk_ieee_float_divide(float numer,float denom)168 static constexpr float sk_ieee_float_divide(float numer, float denom) {
169 return numer / denom;
170 }
171
172 SK_NO_SANITIZE("float-divide-by-zero")
sk_ieee_double_divide(double numer,double denom)173 static constexpr double sk_ieee_double_divide(double numer, double denom) {
174 return numer / denom;
175 }
176 #if defined(SK_BUILD_FOR_WIN) && defined(_MSC_VER)
177 #pragma warning( pop )
178 #endif
179
180 // Returns true iff the provided number is within a small epsilon of 0.
181 bool sk_double_nearly_zero(double a);
182
183 // Compare two doubles and return true if they are within maxUlpsDiff of each other.
184 // * nan as a or b - returns false.
185 // * infinity, infinity or -infinity, -infinity - returns true.
186 // * infinity and any other number - returns false.
187 //
188 // ulp is an initialism for Units in the Last Place.
189 bool sk_doubles_nearly_equal_ulps(double a, double b, uint8_t maxUlpsDiff = 16);
190
191 #endif
192