deal.II version GIT relicensing-6842-g793a97d2aa 2026-10-02 14:00:01+00:00
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
fast_transcendental.h
Go to the documentation of this file.
1// -----------------------------------------------------------------------------
2//
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later
4// Copyright (C) 2025 by the deal.II authors
5//
6// This file is part of the deal.II library.
7//
8// Detailed license information governing the source code and contributions
9// can be found in LICENSE.md and CONTRIBUTING.md at the top level directory.
10//
11// -----------------------------------------------------------------------------
12
13
14#ifndef dealii_fast_transcendental_h
15#define dealii_fast_transcendental_h
16
17#include <deal.II/base/config.h>
18
22
23#include <array>
24#include <cstdint>
25#include <cstring>
26#include <type_traits>
27
28#ifdef _MSC_VER
29# include <intrin.h>
30#elif defined(__ARM_NEON)
31# include <arm_neon.h>
32#elif defined(__x86_64__)
33# include <x86intrin.h>
34#endif
35
37
39{
40 namespace internal
41 {
42 /*
43 * These arrays contain the polynomial coefficients used to approximate
44 * the correction function K(y_f) that appears in the vectorized exponential
45 * approximation. Each specialization provides the coefficients for a
46 * specific polynomial degree.
47 *
48 * The coefficients have been determined via a least-squares fit using 1e8
49 * uniformly spaced sample points over the interval [0, 1].
50 *
51 * The coefficients are ordered from the lowest to the highest polynomial
52 * term, i.e., exp_pol_coeff[i] corresponds to the coefficient of
53 * (y_f)^i.
54 *
55 * The primary template triggers a static assertion to ensure that an
56 * unsupported polynomial degree results in a clear, compile-time error.
57 */
58 template <int degree, typename Number>
59 constexpr std::array<Number, degree + 1> exp_pol_coeff =
60 []() -> std::array<Number, degree + 1> {
61 static_assert(degree > 2 && degree < 12,
62 "Unsupported polynomial degree for exp_pol_coeff.");
63 return std::array<Number, degree + 1>{};
64 }();
65
66 template <typename Number>
67 constexpr std::array<Number, 4> exp_pol_coeff<3, Number> = {
68 {1.8809206555322363e-04,
69 3.0316116404944521e-01,
70 -2.2412622972531135e-01,
71 -7.9019886953785187e-02}};
72
73 template <typename Number>
74 constexpr std::array<Number, 5> exp_pol_coeff<4, Number> = {
75 {-7.2868375805796964e-06,
76 3.0706874220004554e-01,
77 -2.4171033150558635e-01,
78 -5.1666839694436362e-02,
79 -1.3676523629674565e-02}};
80
81 template <typename Number>
82 constexpr std::array<Number, 6> exp_pol_coeff<5, Number> = {
83 {2.3053723876408911e-07,
84 3.0684322094757277e-01,
85 -2.4013168272249241e-01,
86 -5.5876569798473615e-02,
87 -8.9405772567122607e-03,
88 -1.8943785491846432e-03}};
89
90 template <typename Number>
91 constexpr std::array<Number, 7> exp_pol_coeff<6, Number> = {
92 {-6.1646222105268035e-09,
93 3.0685316242622318e-01,
94 -2.4023109751048086e-01,
95 -5.5478910644035311e-02,
96 -9.6861881733345725e-03,
97 -1.2382409419015978e-03,
98 -2.1871253576104385e-04}};
99
100 template <typename Number>
101 constexpr std::array<Number, 8> exp_pol_coeff<7, Number> = {
102 {1.4276280848886066e-10,
103 3.0685280921264796e-01,
104 -2.4022632912712596e-01,
105 -5.5505401662914296e-02,
106 -9.6133378710895074e-03,
107 -1.3431453773612783e-03,
108 -1.4294822119902167e-04,
109 -2.1646947017882258e-05}};
110
111 template <typename Number>
112 constexpr std::array<Number, 9> exp_pol_coeff<8, Number> = {
113 {-2.9146986289710858e-12,
114 3.0685281970141953e-01,
115 -2.4022651268062989e-01,
116 -5.5504055603868258e-02,
117 -9.6183855925465120e-03,
118 -1.3326461166925901e-03,
119 -1.5519735866800178e-04,
120 -1.4147475092332136e-05,
121 -1.8748679813608566e-06}};
122
123 template <typename Number>
124 constexpr std::array<Number, 10> exp_pol_coeff<9, Number> = {
125 {5.4337475594757437e-14,
126 3.0685281943421089e-01,
127 -2.4022650680203564e-01,
128 -5.5504110470759059e-02,
129 -9.6181181164443925e-03,
130 -1.3333950497836748e-03,
131 -1.5394913684739714e-04,
132 -1.5370223000068715e-05,
133 -1.2252831550509094e-06,
134 -1.4435218373241001e-07}};
135
136 template <typename Number>
137 constexpr std::array<Number, 11> exp_pol_coeff<10, Number> = {
138 {7.6072080297756739e-12,
139 3.0685281862960340e-01,
140 -2.4022648563631835e-01,
141 -5.5504349821691862e-02,
142 -9.6166785909533817e-03,
143 -1.3384970969245304e-03,
144 -1.4276337493295606e-04,
145 -3.0711077326624553e-05,
146 1.1584454591775664e-05,
147 -6.0984990187293866e-06,
148 1.1810109241254786e-06}};
149
150 template <typename Number>
151 constexpr std::array<Number, 12> exp_pol_coeff<11, Number> = {
152 {3.6083394569535254e-13,
153 3.0685281939395403e-01,
154 -2.4022650549710606e-01,
155 -5.5504128716495800e-02,
156 -9.6179813320517119e-03,
157 -1.3340079120703319e-03,
158 -1.5221190054222076e-04,
159 -1.8563222423500779e-05,
160 2.5694826992914108e-06,
161 -2.9577981133179920e-06,
162 1.1827521620524028e-06,
163 -2.1525063398151332e-07}};
164
168 template <typename Number>
169 constexpr Number exp_coeff_a = numbers::signaling_nan<Number>();
170 template <>
171 inline constexpr double exp_coeff_a<double> = 4503599627370496;
172 template <>
173 inline constexpr float exp_coeff_a<float> = 8388608;
174
178 template <typename Number>
179 inline constexpr Number exp_coeff_b = numbers::signaling_nan<Number>();
180 template <>
181 inline constexpr double exp_coeff_b<double> = 4.60718241880001741e+18;
182 template <>
183 inline constexpr float exp_coeff_b<float> = 1065353216;
184
190 template <typename Number>
191 inline constexpr Number max_exponent = numbers::signaling_nan<Number>();
192 template <>
193 inline constexpr double max_exponent<double> = 9.218868437227405312e+18;
194 template <>
195 inline constexpr float max_exponent<float> = 2139095040;
196
202 template <typename Number>
203 inline constexpr Number exp_max_abs_x = numbers::signaling_nan<Number>();
204 template <>
205 inline constexpr double exp_max_abs_x<double> = 709.196208642;
206 template <>
207 inline constexpr float exp_max_abs_x<float> = 87.49823353f;
208
224#if DEAL_II_VECTORIZATION_WIDTH_IN_BITS >= 512 && defined(__AVX512F__)
227 {
229 const __m512i integer = _mm512_cvtpd_epi64(in.data);
230 out.data = _mm512_castsi512_pd(integer);
231 return out;
232 }
233
236 {
238 const __m512i integer = _mm512_cvtps_epi32(in.data);
239 out.data = _mm512_castsi512_ps(integer);
240 return out;
241 }
242#endif
243
244#if DEAL_II_VECTORIZATION_WIDTH_IN_BITS >= 256 && defined(__AVX__)
247 {
249 int64_t int_values[4];
250 for (int i = 0; i < 4; i++)
251 {
252 int_values[i] = static_cast<int64_t>(in[i]);
253 }
254 out.data = _mm256_castsi256_pd(
255 _mm256_loadu_si256(reinterpret_cast<__m256i *>(int_values)));
256 return out;
257 }
258
261 {
263 int32_t int_values[8];
264 for (int i = 0; i < 8; i++)
265 {
266 int_values[i] = static_cast<int32_t>(in[i]);
267 }
268 out.data = _mm256_castsi256_ps(
269 _mm256_loadu_si256(reinterpret_cast<__m256i *>(int_values)));
270 return out;
271 }
272#endif
273
274#if DEAL_II_VECTORIZATION_WIDTH_IN_BITS >= 128 && defined(__SSE2__)
277 {
279 int64_t int_values[2];
280 for (int i = 0; i < 2; i++)
281 {
282 int_values[i] = static_cast<int64_t>(in[i]);
283 }
284 out.data = _mm_castsi128_pd(
285 _mm_loadu_si128(reinterpret_cast<__m128i *>(int_values)));
286 return out;
287 }
288
291 {
293 int32_t int_values[4];
294 for (int i = 0; i < 4; i++)
295 {
296 int_values[i] = static_cast<int32_t>(in[i]);
297 }
298 out.data = _mm_castsi128_ps(
299 _mm_loadu_si128(reinterpret_cast<__m128i *>(int_values)));
300 return out;
301 }
302#endif
303
304#if DEAL_II_VECTORIZATION_WIDTH_IN_BITS >= 128 && defined(__ARM_NEON)
307 {
309 const int64x2_t int_values = vcvtq_s64_f64(in.data);
310 out.data = vreinterpretq_f64_s64(int_values);
311 return out;
312 }
313
316 {
318 const int32x4_t int_values = vcvtq_s32_f32(in.data);
319 out.data = vreinterpretq_f32_s32(int_values);
320 return out;
321 }
322#endif
323
324 template <typename number>
327 {
329 const auto result = static_cast<
330 std::conditional_t<std::is_same_v<number, double>, int64_t, int32_t>>(
331 in.data);
332 std::memcpy(&out.data, &result, sizeof(out.data));
333 return out;
334 }
335
354 template <int degree, typename CoeffsType, typename ValueType>
355 DEAL_II_ALWAYS_INLINE inline ValueType
356 horner_scheme(const std::array<CoeffsType, degree + 1> &c,
357 const ValueType &x)
358 {
359 ValueType result = c.back();
360 for (int i = c.size() - 2; i >= 0; --i)
361 result = c[i] + x * result;
362
363 return result;
364 }
365
384 template <int degree, typename CoeffsType, typename ValueType>
385 DEAL_II_ALWAYS_INLINE inline ValueType
386 estrin_scheme(const std::array<CoeffsType, degree + 1> &c,
387 const ValueType &x)
388 {
389 constexpr int n_precomputed_x_powers = degree / 2;
390 std::array<ValueType, n_precomputed_x_powers> x_powers;
391 x_powers[0] = x * x;
392 for (unsigned i = 1; i < n_precomputed_x_powers; ++i)
393 x_powers[i] = x_powers[i - 1] * x_powers[0];
394
395 ValueType t1, t2;
396 t1 = (c[0] + c[1] * x);
397
398 for (unsigned i = 2; i < degree; i += 2)
399 {
400 t2 = (c[i] + c[i + 1] * x);
401 t1 += t2 * x_powers[(i - 2) / 2];
402 }
403
404 if constexpr (degree % 2 == 0)
405 t1 += c.back() * x_powers.back();
406 return t1;
407 }
408 } // namespace internal
409
414 enum class PolynomialEvalScheme : int
415 {
416 estrin,
417 horner
418 };
419
420
507 template <int degree,
508 typename Number,
509 bool ensure_correct_treatment_of_zero = true,
510 bool ensure_correct_treatment_of_infinity = true,
511 PolynomialEvalScheme polynomial_evaluation_scheme =
513 DEAL_II_ALWAYS_INLINE inline Number
514 exp(Number x)
515 {
516 if constexpr (std::is_floating_point_v<Number>)
517 {
518 auto r = exp<degree,
520 ensure_correct_treatment_of_zero,
521 ensure_correct_treatment_of_infinity,
522 polynomial_evaluation_scheme>(
524 return r.data;
525 }
526 else
527 {
528 using floating_type = typename Number::value_type;
529
530 static_assert(
531 std::is_same_v<floating_type, double> ||
532 std::is_same_v<floating_type, float>,
533 "The fast transcendental approximation of the exp() function only supports the fundamental types float and double.");
534
535 Number r = x * static_cast<floating_type>(numbers::LOG2E);
536
537 Number fractional_exponent = r - r.get_floor();
538
539 if constexpr (polynomial_evaluation_scheme ==
541 r -= internal::horner_scheme<degree>(
542 internal::exp_pol_coeff<degree, floating_type>,
543 fractional_exponent);
544 else if constexpr (polynomial_evaluation_scheme ==
546 r -= internal::estrin_scheme<degree>(
547 internal::exp_pol_coeff<degree, floating_type>,
548 fractional_exponent);
549 else
550 static_assert(
551 polynomial_evaluation_scheme == PolynomialEvalScheme::estrin ||
552 polynomial_evaluation_scheme == PolynomialEvalScheme::horner,
553 "The provided scheme for the evaluation of the correction function is not supported!");
554
555 r = internal::exp_coeff_a<floating_type> * r +
556 internal::exp_coeff_b<floating_type>;
557
558 // To ensure correct handling of zero and infinity, the input to the
559 // static_cast operation is adjusted so that values falling outside the
560 // valid range produce exactly zero or infinity.
561 if constexpr (ensure_correct_treatment_of_zero)
562 {
563 // Denormal values are excluded since the approximation method does
564 // not work for them.
565 r = compare_and_apply_mask<SIMDComparison::less_than>(
566 x,
567 Number(-internal::exp_max_abs_x<floating_type>),
568 Number(0.),
569 r);
570 }
571 if constexpr (ensure_correct_treatment_of_infinity)
572 {
573 r = compare_and_apply_mask<SIMDComparison::greater_than>(
574 x,
575 Number(internal::exp_max_abs_x<floating_type>),
576 Number(internal::max_exponent<floating_type>),
577 r);
578 }
579
580 return internal::type_cast(r);
581 }
582 }
583} // namespace fast_transcendental
584
586
587#endif
#define DEAL_II_ALWAYS_INLINE
Definition config.h:166
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
constexpr std::array< Number, 12 > exp_pol_coeff< 11, Number >
constexpr std::array< Number, 5 > exp_pol_coeff< 4, Number >
constexpr std::array< Number, 4 > exp_pol_coeff< 3, Number >
constexpr std::array< Number, 10 > exp_pol_coeff< 9, Number >
constexpr std::array< Number, 11 > exp_pol_coeff< 10, Number >
constexpr std::array< Number, 8 > exp_pol_coeff< 7, Number >
constexpr std::array< Number, 7 > exp_pol_coeff< 6, Number >
constexpr std::array< Number, 6 > exp_pol_coeff< 5, Number >
VectorizedArray< number, 1 > type_cast(const VectorizedArray< number, 1 > &in)
ValueType estrin_scheme(const std::array< CoeffsType, degree+1 > &c, const ValueType &x)
constexpr std::array< Number, 9 > exp_pol_coeff< 8, Number >
constexpr std::array< Number, degree+1 > exp_pol_coeff
ValueType horner_scheme(const std::array< CoeffsType, degree+1 > &c, const ValueType &x)
constexpr double LOG2E
Definition numbers.h:220