Coverage Report

Created: 2026-07-25 07:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/AK/Math/Exponentials.h
Line
Count
Source
1
/*
2
 * Copyright (c) 2021-2026, Leon Albrecht <leon2002.la@gmail.com>.
3
 *
4
 * SPDX-License-Identifier: BSD-2-Clause
5
 */
6
7
#pragma once
8
9
#include <AK/BuiltinWrappers.h>
10
#include <AK/Concepts.h>
11
#include <AK/FloatingPoint.h>
12
#include <AK/Math/Constants.h>
13
#include <AK/Math/Fabs.h>
14
#include <AK/NumericLimits.h>
15
16
#include <AK/Math/Macros.h>
17
18
namespace AK {
19
20
namespace Exponentials {
21
22
template<FloatingPoint T>
23
constexpr T log2(T x)
24
4.13M
{
25
4.13M
    CONSTEXPR_STATE(log2, x);
26
27
4.13M
#if ARCH(X86_64)
28
    if constexpr (IsSame<T, long double>) {
29
        T ret;
30
        asm(
31
            "fld1\n"
32
            "fxch %%st(1)\n"
33
            "fyl2x\n"
34
            : "=t"(ret)
35
            : "0"(x));
36
        return ret;
37
    }
38
4.13M
#endif
39
    // References:
40
    // Gist comparing some implementations
41
    // * https://gist.github.com/Hendiadyoin1/f58346d66637deb9156ef360aa158bf9
42
43
4.13M
    if (x == 0)
44
0
        return -Infinity<T>;
45
4.13M
    if (x <= 0 || __builtin_isnan(x))
46
0
        return NaN<T>;
47
4.13M
    if (__builtin_isinf(x))
48
0
        return Infinity<T>;
49
50
4.13M
    auto ext = FloatExtractor<T>::from_float(x);
51
52
4.13M
    if (ext.exponent == 0) {
53
        // Denormal. log2(mantissa * 2 ** (-exponent_bias)) == log2(mantissa) - exponent_bias.
54
        // Shift mantissa until we have an implicit leading 1. ext.mantissa is always != 0 here.
55
        // Mantissa is mantissa_bits long, count_leading_zeroes() counts in ComponentType, adjust:
56
0
        auto const always_zero_bits = sizeof(typename FloatExtractor<T>::ComponentType) * 8 - (FloatExtractor<T>::mantissa_bits);
57
0
        unsigned leading_mantissa_zeroes = count_leading_zeroes(ext.mantissa) - always_zero_bits;
58
59
0
        int exponent = -FloatExtractor<T>::exponent_bias - leading_mantissa_zeroes;
60
0
        ext.mantissa <<= leading_mantissa_zeroes + 1;
61
0
        ext.exponent = FloatExtractor<T>::exponent_bias;
62
0
        return exponent + log2(ext.to_float());
63
0
    }
64
65
    // When the mantissa shows 0b00 (implicitly 1.0) we are on a power of 2
66
4.13M
    T exponent = ext.exponent - FloatExtractor<T>::exponent_bias;
67
4.13M
    if (ext.mantissa == 0)
68
3.65M
        return exponent;
69
70
476k
    FloatExtractor<T> mantissa_ext {
71
476k
        .mantissa = ext.mantissa,
72
476k
        .exponent = FloatExtractor<T>::exponent_bias,
73
476k
        .sign = ext.sign
74
476k
    };
75
76
    // (1 <= mantissa < 2)
77
476k
    T m = mantissa_ext.to_float();
78
79
    // This is a reconstruction of one of Sun's algorithms
80
    // They use a transformation to lower the problem space,
81
    // while keeping the precision, and a 14th degree polynomial,
82
    // which is mirrored at sqrt(2)
83
    // TODO: Sun has some more algorithms for this and other math functions,
84
    //       leveraging LUTs, investigate those, if they are better in performance and/or precision.
85
    //       These seem to be related to crLibM's implementations, for which papers and references
86
    //       are available.
87
    // FIXME: Dynamically adjust the amount of precision between floats and doubles
88
    //        AKA floats might need less accuracy here, in comparison to doubles
89
90
476k
    bool inverted = false;
91
    // m > sqrt(2)
92
476k
    if (m > Sqrt2<T>) {
93
162k
        inverted = true;
94
162k
        m = 2 / m;
95
162k
    }
96
476k
    T s = (m - 1) / (m + 1);
97
    // ln((1 + s) / (1 - s)) == ln(m)
98
476k
    T s2 = s * s;
99
    // clang-format off
100
476k
    T high_approx = s2 * (static_cast<T>(0.6666666666666735130)
101
476k
                  + s2 * (static_cast<T>(0.3999999999940941908)
102
476k
                  + s2 * (static_cast<T>(0.2857142874366239149)
103
476k
                  + s2 * (static_cast<T>(0.2222219843214978396)
104
476k
                  + s2 * (static_cast<T>(0.1818357216161805012)
105
476k
                  + s2 * (static_cast<T>(0.1531383769920937332)
106
476k
                  + s2 *  static_cast<T>(0.1479819860511658591)))))));
107
    // clang-format on
108
109
    // ln(m) == 2 * s + s * high_approx
110
    // log2(m) == log2(e) * ln(m)
111
476k
    T log2_mantissa = L2_E<T> * (2 * s + s * high_approx);
112
476k
    if (inverted)
113
162k
        log2_mantissa = 1 - log2_mantissa;
114
476k
    return exponent + log2_mantissa;
115
4.13M
}
_ZN2AK12Exponentials4log2ITkNS_8Concepts13FloatingPointEfEET_S3_
Line
Count
Source
24
4.13M
{
25
4.13M
    CONSTEXPR_STATE(log2, x);
26
27
4.13M
#if ARCH(X86_64)
28
    if constexpr (IsSame<T, long double>) {
29
        T ret;
30
        asm(
31
            "fld1\n"
32
            "fxch %%st(1)\n"
33
            "fyl2x\n"
34
            : "=t"(ret)
35
            : "0"(x));
36
        return ret;
37
    }
38
4.13M
#endif
39
    // References:
40
    // Gist comparing some implementations
41
    // * https://gist.github.com/Hendiadyoin1/f58346d66637deb9156ef360aa158bf9
42
43
4.13M
    if (x == 0)
44
0
        return -Infinity<T>;
45
4.13M
    if (x <= 0 || __builtin_isnan(x))
46
0
        return NaN<T>;
47
4.13M
    if (__builtin_isinf(x))
48
0
        return Infinity<T>;
49
50
4.13M
    auto ext = FloatExtractor<T>::from_float(x);
51
52
4.13M
    if (ext.exponent == 0) {
53
        // Denormal. log2(mantissa * 2 ** (-exponent_bias)) == log2(mantissa) - exponent_bias.
54
        // Shift mantissa until we have an implicit leading 1. ext.mantissa is always != 0 here.
55
        // Mantissa is mantissa_bits long, count_leading_zeroes() counts in ComponentType, adjust:
56
0
        auto const always_zero_bits = sizeof(typename FloatExtractor<T>::ComponentType) * 8 - (FloatExtractor<T>::mantissa_bits);
57
0
        unsigned leading_mantissa_zeroes = count_leading_zeroes(ext.mantissa) - always_zero_bits;
58
59
0
        int exponent = -FloatExtractor<T>::exponent_bias - leading_mantissa_zeroes;
60
0
        ext.mantissa <<= leading_mantissa_zeroes + 1;
61
0
        ext.exponent = FloatExtractor<T>::exponent_bias;
62
0
        return exponent + log2(ext.to_float());
63
0
    }
64
65
    // When the mantissa shows 0b00 (implicitly 1.0) we are on a power of 2
66
4.13M
    T exponent = ext.exponent - FloatExtractor<T>::exponent_bias;
67
4.13M
    if (ext.mantissa == 0)
68
3.65M
        return exponent;
69
70
476k
    FloatExtractor<T> mantissa_ext {
71
476k
        .mantissa = ext.mantissa,
72
476k
        .exponent = FloatExtractor<T>::exponent_bias,
73
476k
        .sign = ext.sign
74
476k
    };
75
76
    // (1 <= mantissa < 2)
77
476k
    T m = mantissa_ext.to_float();
78
79
    // This is a reconstruction of one of Sun's algorithms
80
    // They use a transformation to lower the problem space,
81
    // while keeping the precision, and a 14th degree polynomial,
82
    // which is mirrored at sqrt(2)
83
    // TODO: Sun has some more algorithms for this and other math functions,
84
    //       leveraging LUTs, investigate those, if they are better in performance and/or precision.
85
    //       These seem to be related to crLibM's implementations, for which papers and references
86
    //       are available.
87
    // FIXME: Dynamically adjust the amount of precision between floats and doubles
88
    //        AKA floats might need less accuracy here, in comparison to doubles
89
90
476k
    bool inverted = false;
91
    // m > sqrt(2)
92
476k
    if (m > Sqrt2<T>) {
93
162k
        inverted = true;
94
162k
        m = 2 / m;
95
162k
    }
96
476k
    T s = (m - 1) / (m + 1);
97
    // ln((1 + s) / (1 - s)) == ln(m)
98
476k
    T s2 = s * s;
99
    // clang-format off
100
476k
    T high_approx = s2 * (static_cast<T>(0.6666666666666735130)
101
476k
                  + s2 * (static_cast<T>(0.3999999999940941908)
102
476k
                  + s2 * (static_cast<T>(0.2857142874366239149)
103
476k
                  + s2 * (static_cast<T>(0.2222219843214978396)
104
476k
                  + s2 * (static_cast<T>(0.1818357216161805012)
105
476k
                  + s2 * (static_cast<T>(0.1531383769920937332)
106
476k
                  + s2 *  static_cast<T>(0.1479819860511658591)))))));
107
    // clang-format on
108
109
    // ln(m) == 2 * s + s * high_approx
110
    // log2(m) == log2(e) * ln(m)
111
476k
    T log2_mantissa = L2_E<T> * (2 * s + s * high_approx);
112
476k
    if (inverted)
113
162k
        log2_mantissa = 1 - log2_mantissa;
114
476k
    return exponent + log2_mantissa;
115
4.13M
}
Unexecuted instantiation: _ZN2AK12Exponentials4log2ITkNS_8Concepts13FloatingPointEdEET_S3_
Unexecuted instantiation: _ZN2AK12Exponentials4log2ITkNS_8Concepts13FloatingPointEdEET_S3_
116
117
template<FloatingPoint T>
118
constexpr T log(T x)
119
48
{
120
48
    CONSTEXPR_STATE(log, x);
121
122
48
#if ARCH(X86_64)
123
48
    T ret;
124
48
    asm(
125
48
        "fldln2\n"
126
48
        "fxch %%st(1)\n"
127
48
        "fyl2x\n"
128
48
        : "=t"(ret)
129
48
        : "0"(x));
130
48
    return ret;
131
#elif defined(AK_OS_SERENITY)
132
    // FIXME: Adjust the polynomial and formula in log2 to fit this
133
    return log2<T>(x) / L2_E<T>;
134
#else
135
    CALL_BUILTIN(log, x);
136
#endif
137
48
}
138
139
template<FloatingPoint T>
140
constexpr T log10(T x)
141
{
142
    CONSTEXPR_STATE(log10, x);
143
144
#if ARCH(X86_64)
145
    T ret;
146
    asm(
147
        "fldlg2\n"
148
        "fxch %%st(1)\n"
149
        "fyl2x\n"
150
        : "=t"(ret)
151
        : "0"(x));
152
    return ret;
153
#elif defined(AK_OS_SERENITY)
154
    // FIXME: Adjust the polynomial and formula in log2 to fit this
155
    return log2<T>(x) / L2_10<T>;
156
#else
157
    CALL_BUILTIN(log10, x);
158
#endif
159
}
160
161
template<FloatingPoint T>
162
constexpr T exp2(T exponent)
163
4.13M
{
164
4.13M
    CONSTEXPR_STATE(exp2, exponent);
165
166
4.13M
#if ARCH(X86_64)
167
4.13M
    T res;
168
4.13M
    asm("fld1\n"
169
4.13M
        "fld %%st(1)\n"
170
4.13M
        "fprem\n"
171
4.13M
        "f2xm1\n"
172
4.13M
        "faddp\n"
173
4.13M
        "fscale\n"
174
4.13M
        "fstp %%st(1)"
175
4.13M
        : "=t"(res)
176
4.13M
        : "0"(exponent));
177
4.13M
    return res;
178
#else
179
    // TODO: Add better implementation of this function.
180
    // This is just fast exponentiation for the integer part and
181
    // the first couple terms of the taylor series for the fractional part.
182
183
    if (exponent < 0)
184
        return 1 / exp2(-exponent);
185
186
    if (exponent >= log2(NumericLimits<T>::max()))
187
        return Infinity<T>;
188
189
    // Integer exponentiation part.
190
    int int_exponent = static_cast<int>(exponent);
191
    T exponent_fraction = exponent - int_exponent;
192
193
    T int_result = 1;
194
    T base = 2;
195
    for (;;) {
196
        if (int_exponent & 1)
197
            int_result *= base;
198
        int_exponent >>= 1;
199
        if (!int_exponent)
200
            break;
201
        base *= base;
202
    }
203
204
    // Fractional part.
205
    // Uses:
206
    // exp(x) = sum(n, 0, \infty, x ** n / n!)
207
    // 2**x = exp(log2(e) * x)
208
    // FIXME: Pick better step size (and make it dependent on T).
209
    T result = 0;
210
    T power = 1;
211
    T factorial = 1;
212
    for (int i = 1; i < 16; ++i) {
213
        result += power / factorial;
214
        power *= exponent_fraction / L2_E<T>;
215
        factorial *= i;
216
    }
217
218
    return int_result * result;
219
#endif
220
4.13M
}
_ZN2AK12Exponentials4exp2ITkNS_8Concepts13FloatingPointEfEET_S3_
Line
Count
Source
163
4.13M
{
164
4.13M
    CONSTEXPR_STATE(exp2, exponent);
165
166
4.13M
#if ARCH(X86_64)
167
4.13M
    T res;
168
4.13M
    asm("fld1\n"
169
4.13M
        "fld %%st(1)\n"
170
4.13M
        "fprem\n"
171
4.13M
        "f2xm1\n"
172
4.13M
        "faddp\n"
173
4.13M
        "fscale\n"
174
4.13M
        "fstp %%st(1)"
175
4.13M
        : "=t"(res)
176
4.13M
        : "0"(exponent));
177
4.13M
    return res;
178
#else
179
    // TODO: Add better implementation of this function.
180
    // This is just fast exponentiation for the integer part and
181
    // the first couple terms of the taylor series for the fractional part.
182
183
    if (exponent < 0)
184
        return 1 / exp2(-exponent);
185
186
    if (exponent >= log2(NumericLimits<T>::max()))
187
        return Infinity<T>;
188
189
    // Integer exponentiation part.
190
    int int_exponent = static_cast<int>(exponent);
191
    T exponent_fraction = exponent - int_exponent;
192
193
    T int_result = 1;
194
    T base = 2;
195
    for (;;) {
196
        if (int_exponent & 1)
197
            int_result *= base;
198
        int_exponent >>= 1;
199
        if (!int_exponent)
200
            break;
201
        base *= base;
202
    }
203
204
    // Fractional part.
205
    // Uses:
206
    // exp(x) = sum(n, 0, \infty, x ** n / n!)
207
    // 2**x = exp(log2(e) * x)
208
    // FIXME: Pick better step size (and make it dependent on T).
209
    T result = 0;
210
    T power = 1;
211
    T factorial = 1;
212
    for (int i = 1; i < 16; ++i) {
213
        result += power / factorial;
214
        power *= exponent_fraction / L2_E<T>;
215
        factorial *= i;
216
    }
217
218
    return int_result * result;
219
#endif
220
4.13M
}
Unexecuted instantiation: _ZN2AK12Exponentials4exp2ITkNS_8Concepts13FloatingPointEdEET_S3_
Unexecuted instantiation: _ZN2AK12Exponentials4exp2ITkNS_8Concepts13FloatingPointEdEET_S3_
221
222
template<FloatingPoint T>
223
constexpr T exp(T exponent)
224
0
{
225
0
    CONSTEXPR_STATE(exp, exponent);
226
227
0
#if ARCH(X86_64)
228
0
    T res;
229
0
    asm("fldl2e\n"
230
0
        "fmulp\n"
231
0
        "fld1\n"
232
0
        "fld %%st(1)\n"
233
0
        "fprem\n"
234
0
        "f2xm1\n"
235
0
        "faddp\n"
236
0
        "fscale\n"
237
0
        "fstp %%st(1)"
238
0
        : "=t"(res)
239
0
        : "0"(exponent));
240
0
    return res;
241
#else
242
    // TODO: Add better implementation of this function.
243
    return exp2(exponent * L2_E<T>);
244
#endif
245
0
}
Unexecuted instantiation: _ZN2AK12Exponentials3expITkNS_8Concepts13FloatingPointEfEET_S3_
Unexecuted instantiation: _ZN2AK12Exponentials3expITkNS_8Concepts13FloatingPointEfEET_S3_
246
247
}
248
249
using Exponentials::exp;
250
using Exponentials::exp2;
251
using Exponentials::log;
252
using Exponentials::log10;
253
using Exponentials::log2;
254
255
// Calculate x^y with fast exponentiation when the power is a natural number.
256
template<FloatingPoint F, UnsignedIntegral U>
257
constexpr F pow_int(F x, U y)
258
126k
{
259
126k
    auto result = static_cast<F>(1);
260
760k
    while (y > 0) {
261
633k
        if (y % 2 == 1) {
262
403k
            result *= x;
263
403k
        }
264
633k
        x = x * x;
265
633k
        y >>= 1;
266
633k
    }
267
126k
    return result;
268
126k
}
_ZN2AK7pow_intITkNS_8Concepts13FloatingPointEfTkNS1_16UnsignedIntegralEmEET_S2_T0_
Line
Count
Source
258
126k
{
259
126k
    auto result = static_cast<F>(1);
260
760k
    while (y > 0) {
261
633k
        if (y % 2 == 1) {
262
403k
            result *= x;
263
403k
        }
264
633k
        x = x * x;
265
633k
        y >>= 1;
266
633k
    }
267
126k
    return result;
268
126k
}
Unexecuted instantiation: _ZN2AK7pow_intITkNS_8Concepts13FloatingPointEdTkNS1_16UnsignedIntegralEmEET_S2_T0_
269
270
template<FloatingPoint T>
271
constexpr T pow(T x, T y)
272
16.7M
{
273
16.7M
    CONSTEXPR_STATE(pow, x, y);
274
16.7M
    if (__builtin_isnan(y))
275
0
        return y;
276
16.7M
    if (y == 0)
277
6.90k
        return 1;
278
16.7M
    if (x == 0)
279
12.5M
        return 0;
280
4.26M
    if (y == 1)
281
3.10k
        return x;
282
283
    // Take an integer fast path as long as the value fits within a 64-bit integer.
284
4.26M
    if (y >= static_cast<T>(NumericLimits<i64>::min()) && y < static_cast<T>(NumericLimits<i64>::max())) {
285
4.26M
        i64 y_as_int = static_cast<i64>(y);
286
4.26M
        if (y == static_cast<T>(y_as_int)) {
287
126k
            T result = pow_int(x, static_cast<u64>(fabs<T>(y)));
288
126k
            if (y < 0)
289
99.2k
                result = static_cast<T>(1.0l) / result;
290
126k
            return result;
291
126k
        }
292
4.26M
    }
293
294
    // FIXME: This formula suffers from error magnification.
295
4.13M
    return exp2<T>(y * log2<T>(x));
296
4.26M
}
_ZN2AK3powITkNS_8Concepts13FloatingPointEfEET_S2_S2_
Line
Count
Source
272
16.7M
{
273
16.7M
    CONSTEXPR_STATE(pow, x, y);
274
16.7M
    if (__builtin_isnan(y))
275
0
        return y;
276
16.7M
    if (y == 0)
277
6.90k
        return 1;
278
16.7M
    if (x == 0)
279
12.5M
        return 0;
280
4.26M
    if (y == 1)
281
3.10k
        return x;
282
283
    // Take an integer fast path as long as the value fits within a 64-bit integer.
284
4.26M
    if (y >= static_cast<T>(NumericLimits<i64>::min()) && y < static_cast<T>(NumericLimits<i64>::max())) {
285
4.26M
        i64 y_as_int = static_cast<i64>(y);
286
4.26M
        if (y == static_cast<T>(y_as_int)) {
287
126k
            T result = pow_int(x, static_cast<u64>(fabs<T>(y)));
288
126k
            if (y < 0)
289
99.2k
                result = static_cast<T>(1.0l) / result;
290
126k
            return result;
291
126k
        }
292
4.26M
    }
293
294
    // FIXME: This formula suffers from error magnification.
295
4.13M
    return exp2<T>(y * log2<T>(x));
296
4.26M
}
Unexecuted instantiation: _ZN2AK3powITkNS_8Concepts13FloatingPointEdEET_S2_S2_
Unexecuted instantiation: _ZN2AK3powITkNS_8Concepts13FloatingPointEdEET_S2_S2_
297
298
#include <AK/Math/UndefMacros.h>
299
300
}