Plaquette
Loading...
Searching...
No Matches
pq_fastmath.h
1/*
2 * pq_fastmath.h
3 *
4 * Optimized mathematical functions.
5 *
6 * (c) 2022 Sofian Audry :: info(@)sofianaudry(.)com
7 *
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#ifndef PQ_FAST_MATH_H_
23#define PQ_FAST_MATH_H_
24
25#include "pq_globals.h"
26#include "pq_fixed32_trig.h"
27#include "pq_wrap.h"
28
29#include <stdint.h>
30
31namespace pq {
32
34// Source: https://www.gamedev.net/forums/topic/704525-3-quick-ways-to-calculate-the-square-root-in-c/
35inline float fastSqrt(const float& n)
36{
37 static union {int32_t i; float f;} u;
38 u.i = 0x2035AD0C + (*(int32_t*)&n >> 1);
39 return n / u.f + u.f * 0.25f;
40}
41
42inline float fastSin(float x) {
43#if defined(PQ_ARCH_32BITS)
44 x = wrap01(x/TWO_PI) * 4294967295ULL;
45 return sin32( (uint32_t)x ) / 2147483532.0f;
46#else
47 x = wrap01(x/TWO_PI) * 65535;
48 return sin16( (uint16_t)x ) / 32767.0f;
49#endif
50}
51
52inline float fastCos(float x) {
53 return fastSin(HALF_PI - x);
54}
55
56//Source: https://martin.ankerl.com/2007/10/04/optimized-pow-approximation-for-java-and-c-c/
57inline double fastPow(double a, double b) {
58 union {
59 double d;
60 int32_t x[2];
61 } u = { a };
62 u.x[1] = (int32_t)(b * (u.x[1] - 1072632447) + 1072632447);
63 u.x[0] = 0;
64 return u.d;
65}
66
67inline float fastPow(float a, float b) {
68 return (float)fastPow((double)a, (double)b);
69}
70
71// Computes 2^b using a bit-manipulation trick optimized for base 2.
72// Exact for integer exponents; ~6% max error for fractional exponents.
73inline double fastPow2(double b) {
74 union {
75 double d;
76 int32_t x[2];
77 } u;
78 u.x[0] = 0;
79 u.x[1] = (int32_t)(b * (1 << 20) + (1023 << 20));
80 return u.d;
81}
82
83inline float fastPow2(float b) {
84 return (float)fastPow2((double)b);
85}
86
87// Fast natural logarithm approximation using IEEE 754 bit manipulation.
88// Inverse of the Schraudolph fastExp trick below. Max relative error ~14%
89// (worst case at m = sqrt(2) within a binade). Suitable for non-critical uses
90// where a single multiply-and-shift is sufficient.
91inline float fastLog(float x) {
92 union { float f; uint32_t u; } pun = { x };
93 // log(x) ≈ (bits(x) / 2^23 - 127) * ln2
94 return (float)pun.u * (0.693147f / (float)(1 << 23)) - 88.029691f;
95 // 88.029691f = 127 * ln(2)
96}
97
98// More precise fast natural logarithm for x ∈ (0, 1].
99// Splits x = 2^k * m (m ∈ [1,2)), then approximates log(m) using the Euler
100// substitution s = (m-1)/(m+1) where s ∈ [0, 1/3), which converges rapidly:
101// log(m) = 2*(s + s³/3 + s⁵/5 + ...)
102// Two terms give max error ~0.003 (~0.4% relative), vs ~14% for the bit trick.
103// All float ops (div, mul, add) are already linked in any floating-point sketch.
104inline float fastLog01(float x) {
105 union { float f; uint32_t u; } pun = { x };
106
107 // Integer exponent: log(x) = k*ln2 + log(m).
108 int32_t k = (int32_t)(pun.u >> 23) - 127;
109
110 // Mantissa m ∈ [1, 2) as float (replace exponent field with 127).
111 pun.u = (pun.u & 0x007FFFFF) | 0x3F800000;
112
113 // log(m) via s = (m-1)/(m+1): two terms of 2*(s + s³/3).
114 float s = (pun.f - 1.0f) / (pun.f + 1.0f);
115 float s2 = s * s;
116 float logm = s * (2.0f + (2.0f / 3.0f) * s2);
117
118 return (float)k * 0.693147f + logm;
119}
120
121// Directly computes -log(r / 2^32) from a raw uint32 random value r,
122// skipping the fixed32ToFloat step and the max(u, FLT_MIN) guard.
123// Builds the IEEE 754 representation of x = r/2^32 directly from the integer
124// bits (exponent = 126 - clz(r), mantissa = top 23 bits below the leading 1),
125// then delegates to fastLog01 and negates. Same precision as fastLog01.
126// For r=0 (probability 2^-32), returns a large capped value.
127inline float fastNegLog32(uint32_t r) {
128 if (r == 0) return 32.0f * 0.693147f;
129
130 const uint32_t z = __builtin_clz(r);
131
132 // Construct IEEE 754 bits for x = r/2^32 ∈ (0, 1]:
133 // exponent field = 126 - z (biased exponent for the interval [2^(-1-z), 2^(-z)))
134 // mantissa field = top 23 bits of r below the leading 1
135 union { float f; uint32_t u; } pun;
136 pun.u = ((r << (z + 1)) >> 9) | ((126 - z) << 23);
137
138 return -fastLog01(pun.f);
139}
140
141// Source: https://gist.github.com/jrade/293a73f89dfef51da6522428c857802d
142// N. Schraudolph, “A Fast, Compact Approximation of the Exponential Function”,
143// Neural Computation 11, 853–862 (1999).
144// (available at https://nic.schraudolph.org/pubs/Schraudolph99.pdf)
145inline float fastExp(float x)
146{
147 constexpr float a = (1 << 23) / 0.69314718f;
148 constexpr float b = (1 << 23) * (127 - 0.043677448f);
149 x = a * x + b;
150
151 // Remove these lines if bounds checking is not needed
152 constexpr float c = (1 << 23);
153 constexpr float d = (1 << 23) * 255;
154 if (x < c || x > d)
155 x = (x < c) ? 0.0f : d;
156
157 union { float f; uint32_t u; } pun;
158 pun.u = static_cast<uint32_t>(x);
159 return pun.f;
160}
161
162// inline double fastExp(double x)
163// {
164// constexpr double a = (1ll << 52) / 0.6931471805599453;
165// constexpr double b = (1ll << 52) * (1023 - 0.04367744890362246);
166// x = a * x + b;
167
168// // Remove these lines if bounds checking is not needed
169// constexpr double c = (1ll << 52);
170// constexpr double d = (1ll << 52) * 2047;
171// if (x < c || x > d)
172// x = (x < c) ? 0.0 : d;
173
174// // With C++20 one can use std::bit_cast instead
175// uint64_t n = static_cast<uint64_t>(x);
176// memcpy(&x, &n, 8);
177// return x;
178// }
179
180} // namespace pq
181
182#endif