Not Enough Math
Lightweight zero-dependency C++ math library
Loading...
Searching...
No Matches
power.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <bit>
4#include "config.hpp"
5#include "utils.hpp"
6
7namespace nem
8{
9 namespace fast
10 {
11
12 template <std::floating_point T = float>
13 constexpr T log2(T x)
14 {
15 uint32_t bits = std::bit_cast<uint32_t>(x);
16 return float(bits) * (1.0f / (1 << 23)) - 126.94269504f;
17 }
18
19 } // namespace fast
20
21 namespace accurate
22 {
23
24 template <std::floating_point T = float>
25 constexpr T log2(T x)
26 {
27 uint32_t bits = std::bit_cast<uint32_t>(x);
28
29 int exp = int((bits >> 23) & 0xFF) - 127;
30
31 bits = (bits & 0x007FFFFF) | 0x3F800000;
32 float m = std::bit_cast<float>(bits);
33
34 float y = m - 1.0f;
35 float poly = y * (1.4426950f
36 + y * (-0.7213475f
37 + y * 0.4809f));
38
39 return float(exp) + poly;
40 }
41
42 } // accurate namespace
43
44
48
49 template <std::floating_point T = float, nem::Precision Prec = nem::default_precision>
50 constexpr T log2(T x)
51 {
52 if constexpr (std::is_same_v<T, float>)
53 {
54 if constexpr (Prec == nem::accurate::mode)
55 {
56 return nem::accurate::log2<T>(x);
57 }
58 else if constexpr (Prec == nem::fast::mode)
59 {
60 return nem::fast::log2<T>(x);
61 }
62 }
63 else
64 {
65 // TODO: add implementation for double and long double
66 return nem::log2<float, Prec>(static_cast<float>(x));
67 // return nem::error::invalid_result<T>(nem::error::Kind::InvalidArgument, "Log2 doesn't support other floating-point types than float (yet)");
68 }
69 }
70
71 template <std::floating_point T, nem::Precision Prec = nem::default_precision>
72 constexpr T log(T base, T value)
73 {
74 if (nem::is_zero_or_neg(base) || nem::equal(base, (T)1.0))
75 {
77 }
78 if (nem::is_zero_or_neg(value))
79 {
81 }
82 return nem::log2<T, Prec>(value) / nem::log2<T, Prec>(base);
83 }
84
85 template <std::floating_point T, nem::Precision Prec = nem::default_precision>
86 constexpr T ln(T value)
87 {
88 if (nem::is_zero_or_neg(value))
89 {
91 }
92 return nem::log2<T, Prec>(value) / nem::LOG2E<T>;
93 }
94
95 template <std::integral T, nem::Precision Prec = nem::default_precision>
96 constexpr T log(T base, T value)
97 {
98 return (T)nem::round(log<double, Prec>(static_cast<double>(base), static_cast<double>(value)));
99 }
100
104
105 template <std::floating_point T, nem::Precision Prec = nem::default_precision>
106 constexpr T exp2(T x)
107 {
108 if constexpr (std::is_same_v<T, float>)
109 {
110 int i = static_cast<int>(x);
111 float frac = x - float(i);
112
113 float p = 1.0f
114 + frac * (0.6931472f
115 + frac * (0.2402265f
116 + frac * (0.0555041f
117 + frac * 0.0096181f)));
118
119 uint32_t bits = std::bit_cast<uint32_t>(p);
120 bits += uint32_t(i) << 23;
121 return std::bit_cast<float>(bits);
122 }
123 else
124 {
125 // TODO: implement for double and long double
126 return nem::exp2<float, Prec>(x);
127 }
128 }
129
130 template <nem::scalar_type T, std::integral E>
131 constexpr T pow(T base, E exp)
132 {
133 T result = exp2<T>(log2<T>(nem::abs(base)) * T(exp));
134 return (exp % 2 != 0) ? -result : result;
135 }
136
137 template <std::floating_point T, nem::Precision Prec = nem::default_precision>
138 constexpr T pow(T base, T exp, nem::unsafe_t)
139 {
140 return exp2<T, Prec>(log2<T, Prec>(base) * exp);
141 }
142
143 template <std::floating_point T, nem::Precision Prec = nem::default_precision>
144 constexpr T pow(T base, T exp, nem::safe_t = nem::safe)
145 {
146 // TODO: likely/unlikely paths
147
148 if (nem::is_zero(exp))
149 {
150 return (T)1.0;
151 }
152
153 if (nem::is_zero(base))
154 {
155 return (T)0.0;
156 }
157
158 // most common
159 if (base > 0.0)
160 {
161 return nem::pow(base, exp, nem::unsafe);
162 }
163
164 // base < 0
165 if (nem::is_whole(exp))
166 {
167 return nem::pow<T, long long>(base, (long long)(exp));
168 }
169 else
170 {
171 return nem::error::invalid_result<T>(nem::error::Kind::DivisionByZero, "When base < 0 exponent cannot be float");
172 }
173 }
174
178
179 template <typename T>
180 NEM_INLINE T sqrt(T value)
181 {
182 if (nem::is_zero_or_neg(value))
183 {
184 value = T{ 0 };
185 }
186 if constexpr (std::is_floating_point_v<T>)
187 {
188 return static_cast<T>(nem::intr::_nem_sqrt(value));
189 }
190 else if constexpr (std::is_integral_v<T>)
191 {
192 return static_cast<T>(nem::intr::_nem_sqrt(static_cast<double>(value)));
193 }
194 }
195
196 template <std::floating_point T, int Precision = 20>
197 constexpr T csqrt(T value)
198 {
199 if (nem::is_zero_or_neg(value))
200 {
201 return (T)0.0;
202 }
203
204 T result = value;
205 for (int i = 0; i < Precision; ++i)
206 {
207 result = 0.5 * (result + value / result);
208 }
209 return result;
210 }
211
212 template <std::integral T, int Precision = 20>
213 constexpr T csqrt(T value)
214 {
215 if (nem::is_zero_or_neg(value))
216 {
217 return (T)0.0;
218 }
219
220 nem::real result = value;
221 for (int i = 0; i < Precision; ++i)
222 {
223 result = (nem::real)0.5 * (result + static_cast<nem::real>(value) / result);
224 }
225 return static_cast<T>(result);
226 }
227
230 inline int isqrt(unsigned x)
231 {
232 unsigned a, b, m; // Limits and midpoint.
233 a = 1;
234 b = (x >> 5) + 8; // See text.
235 if (b > 65535) b = 65535;
236 do {
237 m = (a + b) >> 1;
238 if (m*m > x) b = m - 1;
239 else a = m + 1;
240 } while (b >= a);
241
242 return a - 1;
243 }
244
247 inline int icbrt(unsigned x)
248 {
249 int s;
250 unsigned y, b;
251 y = 0;
252 for (s = 30; s >= 0; s = s - 3)
253 {
254 y = 2*y;
255 b = (3*y*(y + 1) + 1) << s;
256 if (x >= b)
257 {
258 x = x - b;
259 y = y + 1;
260 }
261 }
262 return y;
263 }
264}
#define NEM_INLINE
Definition config.hpp:12
constexpr nem::Precision mode
Definition config.hpp:124
constexpr T log2(T x)
Definition power.hpp:25
T invalid_result(nem::error::Kind kind=Kind::RuntimeError, const char *const msg=nullptr)
Definition err.hpp:120
constexpr T log2(T x)
Definition power.hpp:13
constexpr nem::Precision mode
Definition config.hpp:101
Definition config.hpp:16
constexpr T LOG2E
log(2, e)
Definition consts.hpp:22
constexpr T pow(T base, E exp)
Definition power.hpp:131
T sqrt(T value)
Definition power.hpp:180
constexpr T exp2(T x)
Definition power.hpp:106
constexpr T abs(T value) noexcept
Definition utils.hpp:38
float real
Definition config.hpp:55
constexpr bool is_zero(T a) noexcept
Is a nearly 0? Given an Epsilon > 0, within each all numbers are considered indistinguisable from 0,...
Definition utils.hpp:48
constexpr bool is_zero_or_neg(T a) noexcept
Definition utils.hpp:51
constexpr T frac(T value) noexcept
Returns the fractional part of the floating-point number, with respect to sign 3.5 -> 0....
Definition utils.hpp:133
constexpr T ln(T value)
Definition power.hpp:86
constexpr T csqrt(T value)
Definition power.hpp:197
constexpr unsafe_t unsafe
Definition config.hpp:64
constexpr T round(T value) noexcept
Definition utils.hpp:144
int icbrt(unsigned x)
Definition power.hpp:247
Precision
Definition config.hpp:67
constexpr T log2(T x)
Definition power.hpp:50
constexpr safe_t safe
Definition config.hpp:63
int isqrt(unsigned x)
Definition power.hpp:230
constexpr bool equal(T a, T b) noexcept
Are a and b nearly equal? Given an Epsilon > 0, within each all numbers are considered indistinguisab...
Definition utils.hpp:56
constexpr T log(T base, T value)
Definition power.hpp:72
constexpr T is_whole(T value)
Is a floating point number whole (same as integer)
Definition utils.hpp:307
constexpr T E
Definition consts.hpp:14