summaryrefslogtreecommitdiffstats
path: root/gfx/skia/skia/include/private/base/SkFloatingPoint.h
blob: 4b2eb4d8973b8dea17e15e12c0151df85c161b1a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
/*
 * 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/SkFloatBits.h"
#include "include/private/base/SkMath.h"

#include <cfloat>
#include <cmath>
#include <cstdint>
#include <cstring>

constexpr float SK_FloatSqrt2 = 1.41421356f;
constexpr float SK_FloatPI    = 3.14159265f;
constexpr double SK_DoublePI  = 3.14159265358979323846264338327950288;

// C++98 cmath std::pow seems to be the earliest portable way to get float pow.
// However, on Linux including cmath undefines isfinite.
// http://gcc.gnu.org/bugzilla/show_bug.cgi?id=14608
static inline float sk_float_pow(float base, float exp) {
    return powf(base, exp);
}

#define sk_float_sqrt(x)        sqrtf(x)
#define sk_float_sin(x)         sinf(x)
#define sk_float_cos(x)         cosf(x)
#define sk_float_tan(x)         tanf(x)
#define sk_float_floor(x)       floorf(x)
#define sk_float_ceil(x)        ceilf(x)
#define sk_float_trunc(x)       truncf(x)
#ifdef SK_BUILD_FOR_MAC
#    define sk_float_acos(x)    static_cast<float>(acos(x))
#    define sk_float_asin(x)    static_cast<float>(asin(x))
#else
#    define sk_float_acos(x)    acosf(x)
#    define sk_float_asin(x)    asinf(x)
#endif
#define sk_float_atan2(y,x)     atan2f(y,x)
#define sk_float_abs(x)         fabsf(x)
#define sk_float_copysign(x, y) copysignf(x, y)
#define sk_float_mod(x,y)       fmodf(x,y)
#define sk_float_exp(x)         expf(x)
#define sk_float_log(x)         logf(x)

constexpr float sk_float_degrees_to_radians(float degrees) {
    return degrees * (SK_FloatPI / 180);
}

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))

// can't find log2f on android, but maybe that just a tool bug?
#ifdef SK_BUILD_FOR_ANDROID
    static inline float sk_float_log2(float x) {
        const double inv_ln_2 = 1.44269504088896;
        return (float)(log(x) * inv_ln_2);
    }
#else
    #define sk_float_log2(x)        log2f(x)
#endif

static inline bool sk_float_isfinite(float x) {
    return SkFloatBits_IsFinite(SkFloat2Bits(x));
}

static inline bool sk_floats_are_finite(float a, float b) {
    return sk_float_isfinite(a) && sk_float_isfinite(b);
}

static inline bool sk_floats_are_finite(const float array[], int count) {
    float prod = 0;
    for (int i = 0; i < count; ++i) {
        prod *= array[i];
    }
    // At this point, prod will either be NaN or 0
    return prod == 0;   // if prod is NaN, this check will return false
}

static inline bool sk_float_isinf(float x) {
    return SkFloatBits_IsInf(SkFloat2Bits(x));
}

#ifdef SK_BUILD_FOR_WIN
    #define sk_float_isnan(x)       _isnan(x)
#elif defined(__clang__) || defined(__GNUC__)
    #define sk_float_isnan(x)       __builtin_isnan(x)
#else
    #define sk_float_isnan(x)       isnan(x)
#endif

#define sk_double_isnan(a)          sk_float_isnan(a)

#define SK_MaxS32FitsInFloat    2147483520
#define SK_MinS32FitsInFloat    -SK_MaxS32FitsInFloat

#define SK_MaxS64FitsInFloat    (SK_MaxS64 >> (63-24) << (63-24))   // 0x7fffff8000000000
#define SK_MinS64FitsInFloat    -SK_MaxS64FitsInFloat

/**
 *  Return the closest int for the given float. Returns SK_MaxS32FitsInFloat for NaN.
 */
static inline 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 inline 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 inline 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(sk_float_floor(x))
#define sk_float_round2int(x)   sk_float_saturate2int(sk_float_round(x))
#define sk_float_ceil2int(x)    sk_float_saturate2int(sk_float_ceil(x))

#define sk_float_floor2int_no_saturate(x)   (int)sk_float_floor(x)
#define sk_float_round2int_no_saturate(x)   (int)sk_float_round(x)
#define sk_float_ceil2int_no_saturate(x)    (int)sk_float_ceil(x)

#define sk_double_floor(x)          floor(x)
#define sk_double_round(x)          floor((x) + 0.5)
#define sk_double_ceil(x)           ceil(x)
#define sk_double_floor2int(x)      (int)sk_double_floor(x)
#define sk_double_round2int(x)      (int)sk_double_round(x)
#define sk_double_ceil2int(x)       (int)sk_double_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!
#ifdef __clang__
SK_NO_SANITIZE("float-cast-overflow")
#elif defined(__GNUC__)
SK_ATTRIBUTE(no_sanitize_undefined)
#endif
static inline float sk_double_to_float(double x) {
    return static_cast<float>(x);
}

#define SK_FloatNaN                 std::numeric_limits<float>::quiet_NaN()
#define SK_FloatInfinity            (+std::numeric_limits<float>::infinity())
#define SK_FloatNegativeInfinity    (-std::numeric_limits<float>::infinity())

#define SK_DoubleNaN                std::numeric_limits<double>::quiet_NaN()

// Returns false if any of the floats are outside of [0...1]
// Returns true if count is 0
bool sk_floats_are_unit(const float array[], size_t count);

static inline float sk_float_rsqrt_portable(float x) { return 1.0f / sk_float_sqrt(x); }
static inline float sk_float_rsqrt         (float x) { return 1.0f / sk_float_sqrt(x); }

// Returns the log2 of the provided value, were that value to be rounded up to the next power of 2.
// Returns 0 if value <= 0:
// Never returns a negative number, even if value is NaN.
//
//     sk_float_nextlog2((-inf..1]) -> 0
//     sk_float_nextlog2((1..2]) -> 1
//     sk_float_nextlog2((2..4]) -> 2
//     sk_float_nextlog2((4..8]) -> 3
//     ...
static inline int sk_float_nextlog2(float x) {
    uint32_t bits = (uint32_t)SkFloat2Bits(x);
    bits += (1u << 23) - 1u;  // Increment the exponent for non-powers-of-2.
    int exp = ((int32_t)bits >> 23) - 127;
    return exp & ~(exp >> 31);  // Return 0 for negative or denormalized floats, and exponents < 0.
}

// This is the number of significant digits we can print in a string such that when we read that
// string back we get the floating point number we expect.  The minimum value C requires is 6, but
// most compilers support 9
#ifdef FLT_DECIMAL_DIG
#define SK_FLT_DECIMAL_DIG FLT_DECIMAL_DIG
#else
#define SK_FLT_DECIMAL_DIG 9
#endif

// 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 __clang__
SK_NO_SANITIZE("float-divide-by-zero")
#elif defined(__GNUC__)
SK_ATTRIBUTE(no_sanitize_undefined)
#endif
static inline float sk_ieee_float_divide(float numer, float denom) {
    return numer / denom;
}

#ifdef __clang__
SK_NO_SANITIZE("float-divide-by-zero")
#elif defined(__GNUC__)
SK_ATTRIBUTE(no_sanitize_undefined)
#endif
static inline double sk_ieee_double_divide(double numer, double denom) {
    return numer / denom;
}

// While we clean up divide by zero, we'll replace places that do divide by zero with this TODO.
static inline float sk_ieee_float_divide_TODO_IS_DIVIDE_BY_ZERO_SAFE_HERE(float n, float d) {
    return sk_ieee_float_divide(n,d);
}

static inline float sk_fmaf(float f, float m, float a) {
#if defined(FP_FAST_FMA)
    return std::fmaf(f,m,a);
#else
    return f*m+a;
#endif
}

// Returns true iff the provided number is within a small epsilon of 0.
bool sk_double_nearly_zero(double a);

// Comparing floating point numbers is complicated. This helper only works if one or none
// of the two inputs is not very close to zero. It also does not work if both inputs could be NaN.
// The term "ulps" stands for "units of least precision". Read the following for more nuance:
//   https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
bool sk_doubles_nearly_equal_ulps(double a, double b, uint8_t max_ulps_diff=16);

#endif