• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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