ftz 0.0.1
Fast, reproducible floating-point arithmetic
Loading...
Searching...
No Matches
ftz.ccm
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2026 Edward Kmett <ekmett@gmail.com>
2// SPDX-License-Identifier: BSD-2-Clause OR Apache-2.0
10module;
11#include <native/attributes.h>
12#include <ftz/config.h>
13#include <algorithm>
14#include <array>
15#include <bit>
16#include <cmath>
17#include <concepts>
18#include <cstdint>
19#include <cstring>
20#include <type_traits>
21#include <utility>
22import native.scalar;
23import native.math;
24#include <ftz/ftz32_ops.h>
25export module ftz;
26export import ftz.controls;
27
28#if defined(__FAST_MATH__)
29#error Reproducible ftz32 requires ordinary expression contraction and fast math disabled
30#endif
31
32export namespace ftz {
63 template <bool Hardware>
64 struct [[nodiscard]] basic_ftz32 {
65 static constexpr bool hardware = Hardware;
67 constexpr native_inline basic_ftz32() noexcept = default;
69 constexpr native_inline basic_ftz32(float value) noexcept
70 : bits_(detail::ftz32_canonical(std::bit_cast<unsigned int>(value))) {}
71
72 [[nodiscard]] constexpr native_inline native_pure operator float() const noexcept { return to_float(); }
74 [[nodiscard]] static constexpr native_inline native_const basic_ftz32 from_float(float value) noexcept { return basic_ftz32(value); }
75 // Caller guarantees normal/signed zero/infinity/any NaN input.
78 [[nodiscard]] static constexpr native_inline native_const basic_ftz32 unsafe_from_float32(float value) noexcept {
79 return canonical(std::bit_cast<unsigned int>(value));
80 }
81
82 [[nodiscard]] static constexpr native_inline native_const basic_ftz32 from_bits(unsigned int bits) noexcept {
83 return canonical(detail::ftz32_canonical(bits));
84 }
85
86 [[nodiscard]] constexpr native_inline native_pure float to_float() const noexcept { return std::bit_cast<float>(bits_); }
88 [[nodiscard]] constexpr native_inline native_pure unsigned int to_bits() const noexcept { return bits_; }
91 [[nodiscard]] friend constexpr native_inline native_const basic_ftz32 operator+(basic_ftz32 a, basic_ftz32 b) noexcept {
92 return canonical(detail::ftz32_add<Hardware>(a.bits_, b.bits_));
93 }
94
96 [[nodiscard]] friend constexpr native_inline native_const basic_ftz32 operator-(basic_ftz32 a, basic_ftz32 b) noexcept {
97 return canonical(detail::ftz32_sub<Hardware>(a.bits_, b.bits_));
98 }
99
101 [[nodiscard]] friend constexpr native_inline native_const basic_ftz32 operator*(basic_ftz32 a, basic_ftz32 b) noexcept {
102 return canonical(detail::ftz32_mul(a.bits_, b.bits_));
103 }
104
106 [[nodiscard]] friend constexpr native_inline native_const basic_ftz32 operator/(basic_ftz32 a, basic_ftz32 b) noexcept {
107 return canonical(detail::ftz32_div<Hardware>(a.bits_, b.bits_));
108 }
109
110 [[nodiscard]] friend constexpr native_inline native_const basic_ftz32 operator-(basic_ftz32 a) noexcept { return canonical(detail::ftz32_neg(a.bits_)); }
112 [[nodiscard]] friend constexpr native_inline native_const basic_ftz32 operator+(basic_ftz32 a) noexcept { return a; }
114 [[nodiscard]] friend constexpr native_inline native_const bool operator==(basic_ftz32 a, basic_ftz32 b) noexcept { return detail::ftz32_equal(a.bits_, b.bits_); }
116 [[nodiscard]] friend constexpr native_inline native_const bool operator!=(basic_ftz32 a, basic_ftz32 b) noexcept { return !(a == b); }
118 [[nodiscard]] friend constexpr native_inline native_const bool operator<(basic_ftz32 a, basic_ftz32 b) noexcept { return detail::ftz32_less(a.bits_, b.bits_); }
120 [[nodiscard]] friend constexpr native_inline native_const bool operator>(basic_ftz32 a, basic_ftz32 b) noexcept { return b < a; }
122 [[nodiscard]] friend constexpr native_inline native_const bool operator<=(basic_ftz32 a, basic_ftz32 b) noexcept { return a < b || a == b; }
124 [[nodiscard]] friend constexpr native_inline native_const bool operator>=(basic_ftz32 a, basic_ftz32 b) noexcept { return b <= a; }
126 constexpr native_inline basic_ftz32 & operator+=(basic_ftz32 b) noexcept { return *this = *this + b; }
128 constexpr native_inline basic_ftz32 & operator-=(basic_ftz32 b) noexcept { return *this = *this - b; }
130 constexpr native_inline basic_ftz32 & operator*=(basic_ftz32 b) noexcept { return *this = *this * b; }
132 constexpr native_inline basic_ftz32 & operator/=(basic_ftz32 b) noexcept { return *this = *this / b; }
133 private:
134 unsigned int bits_ = 0u;
135 [[nodiscard]] static constexpr native_inline native_const basic_ftz32 canonical(unsigned int bits) noexcept {
136 basic_ftz32 result; result.bits_ = bits; return result;
137 }
138 };
139
149 static_assert(sizeof(m32)==sizeof(float) && sizeof(h32)==sizeof(float));
150 static_assert(std::is_trivially_copyable_v<m32> && std::is_trivially_copyable_v<h32>);
153 template <class T> inline constexpr bool is_ftz32 = false;
154 template <bool H> inline constexpr bool is_ftz32<basic_ftz32<H>> = true;
157 template <class T> concept ftz32_type = is_ftz32<std::remove_cvref_t<T>>;
158 namespace detail {
159 template <class T> struct policy_of { static constexpr int value = -1; };
160 template <bool H> struct policy_of<basic_ftz32<H>> { static constexpr int value = H; };
161 template <class T> requires requires { typename T::value_type; }
162 struct policy_of<T> : policy_of<typename T::value_type> {};
163 template <class A,class B> concept mixed_policy =
164 policy_of<std::remove_cvref_t<A>>::value >= 0 && policy_of<std::remove_cvref_t<B>>::value >= 0 &&
165 policy_of<std::remove_cvref_t<A>>::value != policy_of<std::remove_cvref_t<B>>::value;
166 }
168 // Implicit float/native conversions must never choose a policy for an
169 // expression containing both domains. Convert the operands deliberately.
171 template <class A,class B> requires detail::mixed_policy<A,B> void operator+(A,B) = delete;
173 template <class A,class B> requires detail::mixed_policy<A,B> void operator-(A,B) = delete;
175 template <class A,class B> requires detail::mixed_policy<A,B> void operator*(A,B) = delete;
177 template <class A,class B> requires detail::mixed_policy<A,B> void operator/(A,B) = delete;
179 template <class A,class B> requires detail::mixed_policy<A,B> void operator==(A,B) = delete;
181 template <class A,class B> requires detail::mixed_policy<A,B> void operator!=(A,B) = delete;
183 template <class A,class B> requires detail::mixed_policy<A,B> void operator<(A,B) = delete;
185 template <class A,class B> requires detail::mixed_policy<A,B> void operator>(A,B) = delete;
187 template <class A,class B> requires detail::mixed_policy<A,B> void operator<=(A,B) = delete;
189 template <class A,class B> requires detail::mixed_policy<A,B> void operator>=(A,B) = delete;
191 template <class A,class B> requires detail::mixed_policy<A,B> void operator<=>(A,B) = delete;
193 template <class A,class B> requires detail::mixed_policy<A,B> void operator+=(A &,B) = delete;
195 template <class A,class B> requires detail::mixed_policy<A,B> void operator-=(A &,B) = delete;
197 template <class A,class B> requires detail::mixed_policy<A,B> void operator*=(A &,B) = delete;
199 template <class A,class B> requires detail::mixed_policy<A,B> void operator/=(A &,B) = delete;
202 template <class A,class B> requires detail::mixed_policy<A,B> void atan2(A,B) = delete;
205 template <class A,class B> requires detail::mixed_policy<A,B> void copysign(A,B) = delete;
208 template <class A,class B> requires detail::mixed_policy<A,B> void min(A,B) = delete;
211 template <class A,class B> requires detail::mixed_policy<A,B> void max(A,B) = delete;
214 template <class A,class B> requires detail::mixed_policy<A,B> void scaleb(A,B) = delete;
217 template <class A,class B,class C> requires (detail::mixed_policy<A,B> || detail::mixed_policy<A,C> || detail::mixed_policy<B,C>)
218 void fma(A,B,C) = delete;
221 template <class M,class A,class B> requires detail::mixed_policy<A,B> void select(M,A,B) = delete;
224 template <class M,class A,class B,class E> requires (detail::mixed_policy<A,B> || detail::mixed_policy<A,E> || detail::mixed_policy<B,E>)
225 void masked_scaleb(M,A,B,E) = delete;
228 template <class M,class A,class E> requires detail::mixed_policy<A,E> void masked_scaleb_zero(M,A,E) = delete;
231 template <class T> concept ftz32_scalar = std::is_arithmetic_v<T>;
233 template <bool Hardware, ftz32_scalar T> [[nodiscard]] constexpr native_inline native_const basic_ftz32<Hardware> operator+(basic_ftz32<Hardware> a, T b) noexcept { return a + basic_ftz32<Hardware>(static_cast<float>(b)); }
235 template <bool Hardware, ftz32_scalar T> [[nodiscard]] constexpr native_inline native_const basic_ftz32<Hardware> operator+(T a, basic_ftz32<Hardware> b) noexcept { return basic_ftz32<Hardware>(static_cast<float>(a)) + b; }
237 template <bool Hardware, ftz32_scalar T> [[nodiscard]] constexpr native_inline native_const basic_ftz32<Hardware> operator-(basic_ftz32<Hardware> a, T b) noexcept { return a - basic_ftz32<Hardware>(static_cast<float>(b)); }
239 template <bool Hardware, ftz32_scalar T> [[nodiscard]] constexpr native_inline native_const basic_ftz32<Hardware> operator-(T a, basic_ftz32<Hardware> b) noexcept { return basic_ftz32<Hardware>(static_cast<float>(a)) - b; }
241 template <bool Hardware, ftz32_scalar T> [[nodiscard]] constexpr native_inline native_const basic_ftz32<Hardware> operator*(basic_ftz32<Hardware> a, T b) noexcept { return a * basic_ftz32<Hardware>(static_cast<float>(b)); }
243 template <bool Hardware, ftz32_scalar T> [[nodiscard]] constexpr native_inline native_const basic_ftz32<Hardware> operator*(T a, basic_ftz32<Hardware> b) noexcept { return basic_ftz32<Hardware>(static_cast<float>(a)) * b; }
246 template <bool Hardware, ftz32_scalar T> [[nodiscard]] constexpr native_inline native_const basic_ftz32<Hardware> operator/(basic_ftz32<Hardware> a, T b) noexcept { return a / basic_ftz32<Hardware>(static_cast<float>(b)); }
249 template <bool Hardware, ftz32_scalar T> [[nodiscard]] constexpr native_inline native_const basic_ftz32<Hardware> operator/(T a, basic_ftz32<Hardware> b) noexcept { return basic_ftz32<Hardware>(static_cast<float>(a)) / b; }
251 template <bool Hardware, ftz32_scalar T> [[nodiscard]] constexpr native_inline native_const bool operator==(basic_ftz32<Hardware> a, T b) noexcept { return a == basic_ftz32<Hardware>(static_cast<float>(b)); }
253 template <bool Hardware, ftz32_scalar T> [[nodiscard]] constexpr native_inline native_const bool operator==(T a, basic_ftz32<Hardware> b) noexcept { return basic_ftz32<Hardware>(static_cast<float>(a)) == b; }
255 template <bool Hardware, ftz32_scalar T> [[nodiscard]] constexpr native_inline native_const bool operator!=(basic_ftz32<Hardware> a, T b) noexcept { return a != basic_ftz32<Hardware>(static_cast<float>(b)); }
257 template <bool Hardware, ftz32_scalar T> [[nodiscard]] constexpr native_inline native_const bool operator!=(T a, basic_ftz32<Hardware> b) noexcept { return basic_ftz32<Hardware>(static_cast<float>(a)) != b; }
259 template <bool Hardware, ftz32_scalar T> [[nodiscard]] constexpr native_inline native_const bool operator<(basic_ftz32<Hardware> a, T b) noexcept { return a < basic_ftz32<Hardware>(static_cast<float>(b)); }
261 template <bool Hardware, ftz32_scalar T> [[nodiscard]] constexpr native_inline native_const bool operator<(T a, basic_ftz32<Hardware> b) noexcept { return basic_ftz32<Hardware>(static_cast<float>(a)) < b; }
263 template <bool Hardware, ftz32_scalar T> [[nodiscard]] constexpr native_inline native_const bool operator>(basic_ftz32<Hardware> a, T b) noexcept { return a > basic_ftz32<Hardware>(static_cast<float>(b)); }
265 template <bool Hardware, ftz32_scalar T> [[nodiscard]] constexpr native_inline native_const bool operator>(T a, basic_ftz32<Hardware> b) noexcept { return basic_ftz32<Hardware>(static_cast<float>(a)) > b; }
267 template <bool Hardware, ftz32_scalar T> [[nodiscard]] constexpr native_inline native_const bool operator<=(basic_ftz32<Hardware> a, T b) noexcept { return a <= basic_ftz32<Hardware>(static_cast<float>(b)); }
269 template <bool Hardware, ftz32_scalar T> [[nodiscard]] constexpr native_inline native_const bool operator<=(T a, basic_ftz32<Hardware> b) noexcept { return basic_ftz32<Hardware>(static_cast<float>(a)) <= b; }
271 template <bool Hardware, ftz32_scalar T> [[nodiscard]] constexpr native_inline native_const bool operator>=(basic_ftz32<Hardware> a, T b) noexcept { return a >= basic_ftz32<Hardware>(static_cast<float>(b)); }
273 template <bool Hardware, ftz32_scalar T> [[nodiscard]] constexpr native_inline native_const bool operator>=(T a, basic_ftz32<Hardware> b) noexcept { return basic_ftz32<Hardware>(static_cast<float>(a)) >= b; }
274
278 template <bool Hardware>
279 [[nodiscard]] constexpr native_inline native_const basic_ftz32<Hardware> fma(basic_ftz32<Hardware> a, basic_ftz32<Hardware> b, basic_ftz32<Hardware> c) noexcept {
280 return basic_ftz32<Hardware>::unsafe_from_float32(std::bit_cast<float>(detail::ftz32_fma(a.to_bits(), b.to_bits(), c.to_bits())));
281 }
282
286 template <bool Hardware>
287 [[nodiscard]] constexpr native_inline native_const basic_ftz32<Hardware> sin(basic_ftz32<Hardware> a) noexcept { return basic_ftz32<Hardware>::unsafe_from_float32(std::bit_cast<float>(detail::ftz32_sin<Hardware>(a.to_bits()))); }
292 template <bool Hardware>
293 [[nodiscard]] constexpr native_inline native_const basic_ftz32<Hardware> cos(basic_ftz32<Hardware> a) noexcept { return basic_ftz32<Hardware>::unsafe_from_float32(std::bit_cast<float>(detail::ftz32_cos<Hardware>(a.to_bits()))); }
298 template <bool Hardware>
299 [[nodiscard]] constexpr native_inline native_const basic_ftz32<Hardware> tanh(basic_ftz32<Hardware> a) noexcept { return basic_ftz32<Hardware>::unsafe_from_float32(std::bit_cast<float>(detail::ftz32_tanh<Hardware>(a.to_bits()))); }
304 template <bool Hardware>
305 [[nodiscard]] constexpr native_inline native_const basic_ftz32<Hardware> log(basic_ftz32<Hardware> a) noexcept { return basic_ftz32<Hardware>::unsafe_from_float32(std::bit_cast<float>(detail::ftz32_log<Hardware>(a.to_bits()))); }
310 template <bool Hardware>
311 [[nodiscard]] constexpr native_inline native_const basic_ftz32<Hardware> log1p(basic_ftz32<Hardware> a) noexcept { return basic_ftz32<Hardware>::unsafe_from_float32(std::bit_cast<float>(detail::ftz32_log1p<Hardware>(a.to_bits()))); }
317 template <unsigned int Degree = 6, bool Hardware> requires (Degree >= 1 && Degree <= 7)
318 [[nodiscard]] constexpr native_inline native_const basic_ftz32<Hardware> exp(basic_ftz32<Hardware> a) noexcept { return basic_ftz32<Hardware>::unsafe_from_float32(std::bit_cast<float>(detail::ftz32_exp<Degree>(a.to_bits()))); }
323 template <bool Hardware, bool Flush, unsigned int Degree> requires (!Flush && Degree >= 1 && Degree <= 7)
324 [[nodiscard]] constexpr native_inline native_const basic_ftz32<Hardware> exp(basic_ftz32<Hardware> a, std::bool_constant<Flush>, std::integral_constant<unsigned int, Degree>) noexcept { return exp<Degree>(a); }
328 template <bool Hardware>
329 [[nodiscard]] constexpr native_inline native_const basic_ftz32<Hardware> expm1(basic_ftz32<Hardware> a) noexcept { return basic_ftz32<Hardware>::unsafe_from_float32(std::bit_cast<float>(detail::ftz32_expm1<Hardware>(a.to_bits()))); }
334 template <bool Hardware>
335 [[nodiscard]] constexpr native_inline native_const basic_ftz32<Hardware> atan2(basic_ftz32<Hardware> y, basic_ftz32<Hardware> x) noexcept { return basic_ftz32<Hardware>::unsafe_from_float32(std::bit_cast<float>(detail::ftz32_atan2<Hardware>(y.to_bits(), x.to_bits()))); }
340 template <bool Hardware>
341 [[nodiscard]] constexpr native_inline native_const std::pair<basic_ftz32<Hardware>, basic_ftz32<Hardware>> sincos(basic_ftz32<Hardware> a) noexcept {
342 auto r=detail::ftz32_sincos<Hardware>(a.to_bits()); return {basic_ftz32<Hardware>::unsafe_from_float32(std::bit_cast<float>(r.sine)),basic_ftz32<Hardware>::unsafe_from_float32(std::bit_cast<float>(r.cosine))};
343 }
344
348 template <bool Hardware>
349 [[nodiscard]] constexpr native_inline native_const basic_ftz32<Hardware> sqrt(basic_ftz32<Hardware> a) noexcept { return basic_ftz32<Hardware>::unsafe_from_float32(std::bit_cast<float>(detail::ftz32_sqrt(a.to_bits()))); }
354 template <bool Hardware>
355 [[nodiscard]] constexpr native_inline basic_ftz32<Hardware> floor(basic_ftz32<Hardware> a) noexcept {
356 if consteval {
357 namespace fp = ::native::detail::constexpr_float;
358 return basic_ftz32<Hardware>::from_bits(fp::round_integral_bits<fp::binary32>(
359 a.to_bits(), fp::rounding::downward));
360 }
362 }
363
367 template <bool Hardware>
368 [[nodiscard]] constexpr native_inline basic_ftz32<Hardware> ceil(basic_ftz32<Hardware> a) noexcept {
369 if consteval {
370 namespace fp = ::native::detail::constexpr_float;
371 return basic_ftz32<Hardware>::from_bits(fp::round_integral_bits<fp::binary32>(
372 a.to_bits(), fp::rounding::upward));
373 }
375 }
376
380 template <bool Hardware>
381 [[nodiscard]] constexpr native_inline basic_ftz32<Hardware> trunc(basic_ftz32<Hardware> a) noexcept {
382 if consteval {
383 namespace fp = ::native::detail::constexpr_float;
384 return basic_ftz32<Hardware>::from_bits(fp::round_integral_bits<fp::binary32>(
385 a.to_bits(), fp::rounding::toward_zero));
386 }
388 }
389
392 template <bool Hardware>
393 [[nodiscard]] constexpr native_inline native_const basic_ftz32<Hardware> abs(basic_ftz32<Hardware> a) noexcept { return basic_ftz32<Hardware>::unsafe_from_float32(std::bit_cast<float>(detail::ftz32_abs(a.to_bits()))); }
397 template <bool Hardware>
398 [[nodiscard]] constexpr native_inline native_const basic_ftz32<Hardware> neg(basic_ftz32<Hardware> a) noexcept { return -a; }
402 template <bool Hardware>
403 [[nodiscard]] constexpr native_inline native_const bool isnan(basic_ftz32<Hardware> a) noexcept { return detail::ftz32_isnan(a.to_bits()); }
407 template <bool Hardware>
408 [[nodiscard]] constexpr native_inline native_const bool isinf(basic_ftz32<Hardware> a) noexcept { return (a.to_bits() & 0x7fffffffu) == detail::ftz32_infinity; }
412 template <bool Hardware>
413 [[nodiscard]] constexpr native_inline native_const bool isfinite(basic_ftz32<Hardware> a) noexcept { return (a.to_bits() & 0x7fffffffu) < detail::ftz32_infinity; }
417 template <bool Hardware>
418 [[nodiscard]] constexpr native_inline native_const bool signbit(basic_ftz32<Hardware> a) noexcept { return (a.to_bits() & detail::ftz32_sign) != 0u; }
423 template <bool Hardware>
424 [[nodiscard]] constexpr native_inline native_const basic_ftz32<Hardware> copysign(basic_ftz32<Hardware> a, basic_ftz32<Hardware> b) noexcept {
425 return basic_ftz32<Hardware>::from_bits((a.to_bits() & 0x7fffffffu) | (b.to_bits() & detail::ftz32_sign));
426 }
427 namespace detail {
428 // A conversion to exactly one policy preserves that policy. Built-in
429 // scalars retain the configured compatibility alias; a user conversion
430 // offering both domains is ambiguous and must choose explicitly.
431 template <class T> concept scalar_argument = !ftz32_type<T> &&
432 (std::is_arithmetic_v<std::remove_cvref_t<T>> ||
433 (std::convertible_to<T,m32> != std::convertible_to<T,h32>));
434 template <class T> using scalar_argument_t = std::conditional_t<
435 std::is_arithmetic_v<std::remove_cvref_t<T>>, ftz32,
436 std::conditional_t<std::convertible_to<T,m32>,m32,h32>>;
437 }
442 template <class T> requires detail::scalar_argument<T &&>
443 [[nodiscard]] constexpr native_inline auto sin(T && a)
444 noexcept(noexcept(sin(detail::scalar_argument_t<T &&>(std::forward<T>(a))))) {
445 return sin(detail::scalar_argument_t<T &&>(std::forward<T>(a)));
446 }
447
451 template <class T> requires detail::scalar_argument<T &&>
452 [[nodiscard]] constexpr native_inline auto cos(T && a)
453 noexcept(noexcept(cos(detail::scalar_argument_t<T &&>(std::forward<T>(a))))) {
454 return cos(detail::scalar_argument_t<T &&>(std::forward<T>(a)));
455 }
456
460 template <class T> requires detail::scalar_argument<T &&>
461 [[nodiscard]] constexpr native_inline auto sincos(T && a)
462 noexcept(noexcept(sincos(detail::scalar_argument_t<T &&>(std::forward<T>(a))))) {
463 return sincos(detail::scalar_argument_t<T &&>(std::forward<T>(a)));
464 }
465
469 template <class T> requires detail::scalar_argument<T &&>
470 [[nodiscard]] constexpr native_inline auto tanh(T && a)
471 noexcept(noexcept(tanh(detail::scalar_argument_t<T &&>(std::forward<T>(a))))) {
472 return tanh(detail::scalar_argument_t<T &&>(std::forward<T>(a)));
473 }
474
478 template <class T> requires detail::scalar_argument<T &&>
479 [[nodiscard]] constexpr native_inline auto log(T && a)
480 noexcept(noexcept(log(detail::scalar_argument_t<T &&>(std::forward<T>(a))))) {
481 return log(detail::scalar_argument_t<T &&>(std::forward<T>(a)));
482 }
483
487 template <class T> requires detail::scalar_argument<T &&>
488 [[nodiscard]] constexpr native_inline auto log1p(T && a)
489 noexcept(noexcept(log1p(detail::scalar_argument_t<T &&>(std::forward<T>(a))))) {
490 return log1p(detail::scalar_argument_t<T &&>(std::forward<T>(a)));
491 }
492
496 template <unsigned int Degree = 6, class T> requires (detail::scalar_argument<T &&> && Degree >= 1 && Degree <= 7)
497 [[nodiscard]] constexpr native_inline auto exp(T && a)
498 noexcept(noexcept(exp<Degree>(detail::scalar_argument_t<T &&>(std::forward<T>(a))))) {
499 return exp<Degree>(detail::scalar_argument_t<T &&>(std::forward<T>(a)));
500 }
501
504 template <class T> requires detail::scalar_argument<T &&>
505 [[nodiscard]] constexpr native_inline auto expm1(T && a)
506 noexcept(noexcept(expm1(detail::scalar_argument_t<T &&>(std::forward<T>(a))))) {
507 return expm1(detail::scalar_argument_t<T &&>(std::forward<T>(a)));
508 }
509
513 template <class T> requires detail::scalar_argument<T &&>
514 [[nodiscard]] constexpr native_inline auto sqrt(T && a)
515 noexcept(noexcept(sqrt(detail::scalar_argument_t<T &&>(std::forward<T>(a))))) {
516 return sqrt(detail::scalar_argument_t<T &&>(std::forward<T>(a)));
517 }
518
522 template <class T> requires detail::scalar_argument<T &&>
523 [[nodiscard]] constexpr native_inline auto floor(T && a)
524 noexcept(noexcept(floor(detail::scalar_argument_t<T &&>(std::forward<T>(a))))) {
525 return floor(detail::scalar_argument_t<T &&>(std::forward<T>(a)));
526 }
527
531 template <class T> requires detail::scalar_argument<T &&>
532 [[nodiscard]] constexpr native_inline auto ceil(T && a)
533 noexcept(noexcept(ceil(detail::scalar_argument_t<T &&>(std::forward<T>(a))))) {
534 return ceil(detail::scalar_argument_t<T &&>(std::forward<T>(a)));
535 }
536
540 template <class T> requires detail::scalar_argument<T &&>
541 [[nodiscard]] constexpr native_inline auto trunc(T && a)
542 noexcept(noexcept(trunc(detail::scalar_argument_t<T &&>(std::forward<T>(a))))) {
543 return trunc(detail::scalar_argument_t<T &&>(std::forward<T>(a)));
544 }
545
548 template <class T> requires detail::scalar_argument<T &&>
549 [[nodiscard]] constexpr native_inline auto abs(T && a)
550 noexcept(noexcept(abs(detail::scalar_argument_t<T &&>(std::forward<T>(a))))) {
551 return abs(detail::scalar_argument_t<T &&>(std::forward<T>(a)));
552 }
553
556 template <class T> requires detail::scalar_argument<T &&>
557 [[nodiscard]] constexpr native_inline auto neg(T && a)
558 noexcept(noexcept(neg(detail::scalar_argument_t<T &&>(std::forward<T>(a))))) {
559 return neg(detail::scalar_argument_t<T &&>(std::forward<T>(a)));
560 }
561
564 template <class T> requires detail::scalar_argument<T &&>
565 [[nodiscard]] constexpr native_inline auto isnan(T && a)
566 noexcept(noexcept(isnan(detail::scalar_argument_t<T &&>(std::forward<T>(a))))) {
567 return isnan(detail::scalar_argument_t<T &&>(std::forward<T>(a)));
568 }
569
572 template <class T> requires detail::scalar_argument<T &&>
573 [[nodiscard]] constexpr native_inline auto isinf(T && a)
574 noexcept(noexcept(isinf(detail::scalar_argument_t<T &&>(std::forward<T>(a))))) {
575 return isinf(detail::scalar_argument_t<T &&>(std::forward<T>(a)));
576 }
577
580 template <class T> requires detail::scalar_argument<T &&>
581 [[nodiscard]] constexpr native_inline auto isfinite(T && a)
582 noexcept(noexcept(isfinite(detail::scalar_argument_t<T &&>(std::forward<T>(a))))) {
583 return isfinite(detail::scalar_argument_t<T &&>(std::forward<T>(a)));
584 }
585
588 template <class T> requires detail::scalar_argument<T &&>
589 [[nodiscard]] constexpr native_inline auto signbit(T && a)
590 noexcept(noexcept(signbit(detail::scalar_argument_t<T &&>(std::forward<T>(a))))) {
591 return signbit(detail::scalar_argument_t<T &&>(std::forward<T>(a)));
592 }
593
594 namespace detail {
595 template <class T> inline constexpr int scalar_policy = [] {
596 if constexpr (ftz32_type<T>) return int(std::remove_cvref_t<T>::hardware);
597 else if constexpr (std::is_arithmetic_v<std::remove_cvref_t<T>>) return -1;
598 else if constexpr (std::convertible_to<T,m32> != std::convertible_to<T,h32>)
599 return int(std::convertible_to<T,h32>);
600 else return -2;
601 }();
602 template <class... T> concept scalar_arguments = ((scalar_policy<T> >= -1) && ...) &&
603 !(((scalar_policy<T> == 0) || ...) && ((scalar_policy<T> == 1) || ...));
604 template <class... T> using scalar_arguments_t = std::conditional_t<
605 ((scalar_policy<T> == 0) || ...), m32,
606 std::conditional_t<((scalar_policy<T> == 1) || ...),h32,ftz32>>;
607 }
611 template <class A,class B,class C> requires detail::scalar_arguments<A &&,B &&,C &&> &&
613 [[nodiscard]] constexpr native_inline auto fma(A && a,B && b,C && c)
614 noexcept(noexcept(fma(detail::scalar_arguments_t<A &&,B &&,C &&>(std::forward<A>(a)),
615 detail::scalar_arguments_t<A &&,B &&,C &&>(std::forward<B>(b)),
616 detail::scalar_arguments_t<A &&,B &&,C &&>(std::forward<C>(c))))) {
617 using F=detail::scalar_arguments_t<A &&,B &&,C &&>;
618 return fma(F(std::forward<A>(a)),F(std::forward<B>(b)),F(std::forward<C>(c)));
619 }
620
624 template <class A,class B> requires detail::scalar_arguments<A &&,B &&> &&
625 (!(ftz32_type<A> && ftz32_type<B>))
626 [[nodiscard]] constexpr native_inline auto atan2(A && a,B && b)
627 noexcept(noexcept(atan2(detail::scalar_arguments_t<A &&,B &&>(std::forward<A>(a)),
628 detail::scalar_arguments_t<A &&,B &&>(std::forward<B>(b))))) {
629 using F=detail::scalar_arguments_t<A &&,B &&>;
630 return atan2(F(std::forward<A>(a)),F(std::forward<B>(b)));
631 }
632
636 template <class A,class B> requires detail::scalar_arguments<A &&,B &&> &&
637 (!(ftz32_type<A> && ftz32_type<B>))
638 [[nodiscard]] constexpr native_inline auto copysign(A && a,B && b)
639 noexcept(noexcept(copysign(detail::scalar_arguments_t<A &&,B &&>(std::forward<A>(a)),
640 detail::scalar_arguments_t<A &&,B &&>(std::forward<B>(b))))) {
641 using F=detail::scalar_arguments_t<A &&,B &&>;
642 return copysign(F(std::forward<A>(a)),F(std::forward<B>(b)));
643 }
644
645}
646
647#include "ftz/simd.h"
648
649namespace ftz::detail {
650 using ::ftz::detail::ftz32_cpu_witness;
651 using ::ftz::detail::ftz32_cpu_path;
652 // Volatile input loads prevent compile-time evaluation. The noinline
653 // boundary is a probe property, never part of ordinary value arithmetic.
654 template <bool Hardware>
655#if defined(_MSC_VER)
656 __declspec(noinline)
657#elif defined(__GNUC__) || defined(__clang__)
658 __attribute__((noinline))
659#endif
660 inline unsigned int ftz32_cpu_evaluate(ftz32_cpu_witness row, ftz32_cpu_path path) {
661 volatile unsigned int a_word = row.a, b_word = row.b, c_word = row.c;
662 unsigned int a = a_word, b = b_word, c = c_word;
663 if (path != ftz32_cpu_path::raw) {
664 // Probe the explicit sibling's repaired addition independently of the
665 // compiled profile. Other finite boundary witnesses use the shared
666 // multiplication/FMA repair graph, which is unchanged by add policy.
667 auto add = [path](unsigned int x, unsigned int y) {
668 return path == ftz32_cpu_path::explicit_core ? ftz32_add_policy<false>(x,y) : ftz32_add<Hardware>(x,y);
669 };
670 switch (row.operation) {
671 case 0: return add(a,b);
672 case 1: return ftz32_mul(a,b);
673 case 2: return ftz32_fma(a,b,c);
674 case 3: return add(ftz32_mul(a,b),c);
675 default: return ftz32_canonical(a);
676 }
677 }
678 float x = std::bit_cast<float>(a), y = std::bit_cast<float>(b), z = std::bit_cast<float>(c);
679 switch (row.operation) {
680 case 0: return std::bit_cast<unsigned int>(x+y);
681 case 1: return std::bit_cast<unsigned int>(x*y);
682 case 2: return std::bit_cast<unsigned int>(std::fma(x,y,z));
683 default: {
684 float product = x*y;
685 return std::bit_cast<unsigned int>(product+z);
686 }
687 }
688 }
689}
690
691export namespace ftz {
692 // Explicit compiled-profile qualification, normally once at process startup
693 // under native_fp32_scope. Thread entry and arithmetic never call this bank.
694 // Like arithmetic, the probe may set masked exception status flags; controls
695 // stay untouched and the owning scope restores the caller's complete state.
705 template <ftz32_type T = ftz32>
706 [[nodiscard]] inline ftz32_cpu_admission probe_ftz32_cpu() {
707 return ::ftz::detail::ftz32_cpu_probe(detail::ftz32_cpu_evaluate<T::hardware>, T::hardware);
708 }
709}
Accepts built-in arithmetic operands for conversion into a chosen FTZ policy.
Definition ftz.ccm:231
Accepts either scalar policy after removing cv/ref qualifiers.
Definition ftz.ccm:157
Shared FTZ scalar operators and math, including special values and full-range trig reduction.
ftz32_cpu_admission probe_ftz32_cpu()
Observes whether this compiled scalar profile meets the requested policy.
Definition ftz.ccm:706
constexpr basic_ftz32< Hardware > neg(basic_ftz32< Hardware > a) noexcept
Flips the sign bit without floating-point arithmetic.
Definition ftz.ccm:398
constexpr basic_ftz32< Hardware > abs(basic_ftz32< Hardware > a) noexcept
Clears the sign bit without floating-point arithmetic.
Definition ftz.ccm:393
constexpr basic_ftz32< Hardware > sqrt(basic_ftz32< Hardware > a) noexcept
Returns the reciprocal-refined square-root approximation; signed zero and positive infinity are prese...
Definition ftz.ccm:349
constexpr std::array< R, N > log1p(std::array< R, N > const &input) noexcept
Computes log(1+x), preserving signed zero; -1 gives negative infinity and x < -1 gives NaN....
Definition simd.h:1286
constexpr std::array< R, N > add(std::array< R, N > const &a, std::array< R, N > const &b) noexcept
Adds matching register arrays; a non-array operand is converted once and broadcast,...
Definition simd.h:1112
constexpr std::array< R, N > log(std::array< R, N > const &input) noexcept
Computes natural logarithms; either zero gives negative infinity, negative nonzero values give NaN....
Definition simd.h:1259
constexpr std::array< R, N > atan2(std::array< R, N > const &y, std::array< R, N > const &x) noexcept
Computes atan2(y,x) in radians with the scalar FTZ signed-axis and infinity rules....
Definition simd.h:1314
constexpr std::array< R, N > tanh(std::array< R, N > const &input) noexcept
Computes hyperbolic tangent with the scalar FTZ graph, preserving signed zero and saturating infiniti...
Definition simd.h:1231
basic_ftz32< true > h32
Hardware-normalization value; arithmetic requires a qualified flush region.
Definition ftz.ccm:144
basic_ftz32< false > m32
Explicit-normalization value; arithmetic supports gradual and admitted flush regions.
Definition ftz.ccm:141
constexpr bool is_ftz32
Identifies the two unqualified FTZ scalar types. Use ftz32_type to ignore cv/ref.
Definition ftz.ccm:153
basic_ftz32< FTZ_FP32_HARDWARE_FTZ !=0 > ftz32
Compatibility alias selected by the installed FTZ_FP32_HARDWARE_FTZ package setting....
Definition ftz.ccm:148
constexpr R trunc(R value) noexcept
Rounds toward zero, independently of ambient rounding mode; signed zero and infinities survive....
Definition simd.h:1415
constexpr R::mask isfinite(R value) noexcept
Returns R::mask for finite lanes by inspecting words, without changing FP status.
Definition simd.h:1356
constexpr R::mask isinf(R value) noexcept
Returns R::mask for either infinity by inspecting lane words.
Definition simd.h:1349
constexpr auto operator!=(V a, basic_ftz32< Hardware > b) noexcept
Imports the raw register into the scalar operand's FTZ policy and returns the native comparison mask.
Definition simd.h:724
constexpr R floor(R value) noexcept
Rounds toward negative infinity, independently of ambient rounding mode; signed zero and infinities s...
Definition simd.h:1383
constexpr R cos(R input) noexcept
Computes cosine in radians with dedicated output reconstruction and the scalar FTZ special-value rule...
Definition simd.h:1020
constexpr V & operator+=(V &a, basic_ftz32< Hardware > b) noexcept
Evaluates FTZ arithmetic before assigning to the existing raw-register destination.
Definition simd.h:767
constexpr auto operator>=(V a, basic_ftz32< Hardware > b) noexcept
Imports the raw register into the scalar operand's FTZ policy and returns the native comparison mask.
Definition simd.h:752
constexpr R::mask signbit(R value) noexcept
Returns R::mask for set sign bits, including negative zero and signed NaNs.
Definition simd.h:1363
constexpr auto operator==(V a, basic_ftz32< Hardware > b) noexcept
Imports the raw register into the scalar operand's FTZ policy and returns the native comparison mask.
Definition simd.h:710
constexpr V & operator-=(V &a, basic_ftz32< Hardware > b) noexcept
Evaluates FTZ arithmetic before assigning to the existing raw-register destination.
Definition simd.h:781
constexpr auto operator>(V a, basic_ftz32< Hardware > b) noexcept
Imports the raw register into the scalar operand's FTZ policy and returns the native comparison mask.
Definition simd.h:696
constexpr auto operator/(V a, basic_ftz32< Hardware > b) noexcept
Imports the raw register into the scalar operand's FTZ policy and returns its FTZ vector rebind.
Definition simd.h:668
constexpr R expm1(R input) noexcept
Computes exp(x)-1 with the scalar FTZ graph, preserving signed zero. Returns R.
Definition simd.h:1039
constexpr auto operator*(V a, basic_ftz32< Hardware > b) noexcept
Imports the raw register into the scalar operand's FTZ policy and returns its FTZ vector rebind.
Definition simd.h:654
constexpr auto sincos(R input) noexcept
Computes sine and cosine in radians with the scalar special-value rules; returns a pair in that order...
Definition simd.h:1005
constexpr R exp(R input) noexcept
Computes the exponential with the scalar FTZ underflow, overflow and special-value rules....
Definition simd.h:1026
constexpr R ceil(R value) noexcept
Rounds toward positive infinity, independently of ambient rounding mode; signed zero and infinities s...
Definition simd.h:1399
constexpr V & operator*=(V &a, basic_ftz32< Hardware > b) noexcept
Evaluates FTZ arithmetic before assigning to the existing raw-register destination.
Definition simd.h:795
constexpr R::mask isnan(R value) noexcept
Returns R::mask for NaN lanes by inspecting words; no FP evaluation or NaN quieting occurs.
Definition simd.h:1342
constexpr auto fma(A a, B b, C c) noexcept(std::is_nothrow_constructible_v< detail::ftz32_simd< detail::raw_family_t< A, B, C >, detail::scalar_family_t< A, B, C > >, A & > &&std::is_nothrow_constructible_v< detail::ftz32_simd< detail::raw_family_t< A, B, C >, detail::scalar_family_t< A, B, C > >, B & > &&std::is_nothrow_constructible_v< detail::ftz32_simd< detail::raw_family_t< A, B, C >, detail::scalar_family_t< A, B, C > >, C & >)
Imports raw SIMD operands into the FTZ scalar operand's policy and evaluates fused a*b+c....
Definition simd.h:840
constexpr R sin(R input) noexcept
Computes sine in radians with dedicated output reconstruction and the scalar FTZ special-value rules....
Definition simd.h:1015
constexpr auto operator<(V a, basic_ftz32< Hardware > b) noexcept
Imports the raw register into the scalar operand's FTZ policy and returns the native comparison mask.
Definition simd.h:682
constexpr auto operator-(V a, basic_ftz32< Hardware > b) noexcept
Imports the raw register into the scalar operand's FTZ policy and returns its FTZ vector rebind.
Definition simd.h:640
constexpr auto operator<=(V a, basic_ftz32< Hardware > b) noexcept
Imports the raw register into the scalar operand's FTZ policy and returns the native comparison mask.
Definition simd.h:738
constexpr auto operator+(V a, basic_ftz32< Hardware > b) noexcept
Imports the raw register into the scalar operand's FTZ policy and returns its FTZ vector rebind.
Definition simd.h:626
constexpr V & operator/=(V &a, basic_ftz32< Hardware > b) noexcept
Evaluates FTZ arithmetic before assigning to the existing raw-register destination.
Definition simd.h:809
constexpr R copysign(R magnitude, R sign) noexcept
Returns magnitude with the sign bits of sign; all other words, including NaN payloads,...
Definition simd.h:1371
Binary32 value with a compile-time signed-FTZ policy.
Definition ftz.ccm:64
static constexpr basic_ftz32 from_float(float value) noexcept
Imports and normalizes a float, as the converting constructor does.
Definition ftz.ccm:74
constexpr basic_ftz32 & operator+=(basic_ftz32 b) noexcept
Assigns the corresponding FTZ result and returns this value by reference.
Definition ftz.ccm:126
friend constexpr bool operator<=(basic_ftz32 a, basic_ftz32 b) noexcept
Compares values; NaN is unordered (only != is true), and the two zero signs compare equal.
Definition ftz.ccm:122
friend constexpr bool operator>(basic_ftz32 a, basic_ftz32 b) noexcept
Compares values; NaN is unordered (only != is true), and the two zero signs compare equal.
Definition ftz.ccm:120
constexpr unsigned int to_bits() const noexcept
Exports the exact stored word, including zero sign and NaN payload.
Definition ftz.ccm:88
constexpr basic_ftz32() noexcept=default
Constructs positive zero.
friend constexpr bool operator==(basic_ftz32 a, basic_ftz32 b) noexcept
Compares values; NaN is unordered (only != is true), and the two zero signs compare equal.
Definition ftz.ccm:114
constexpr basic_ftz32 & operator-=(basic_ftz32 b) noexcept
Assigns the corresponding FTZ result and returns this value by reference.
Definition ftz.ccm:128
constexpr basic_ftz32 & operator/=(basic_ftz32 b) noexcept
Assigns the corresponding FTZ result and returns this value by reference.
Definition ftz.ccm:132
friend constexpr basic_ftz32 operator+(basic_ftz32 a) noexcept
Returns the value unchanged.
Definition ftz.ccm:112
friend constexpr bool operator!=(basic_ftz32 a, basic_ftz32 b) noexcept
Compares values; NaN is unordered (only != is true), and the two zero signs compare equal.
Definition ftz.ccm:116
friend constexpr bool operator>=(basic_ftz32 a, basic_ftz32 b) noexcept
Compares values; NaN is unordered (only != is true), and the two zero signs compare equal.
Definition ftz.ccm:124
static constexpr basic_ftz32 unsafe_from_float32(float value) noexcept
Wraps float bits without classification or normalization.
Definition ftz.ccm:78
constexpr float to_float() const noexcept
Definition ftz.ccm:86
friend constexpr basic_ftz32 operator-(basic_ftz32 a) noexcept
Flips only the sign bit, preserving signed zero and NaN payloads.
Definition ftz.ccm:110
friend constexpr basic_ftz32 operator/(basic_ftz32 a, basic_ftz32 b) noexcept
Divides using the reproducible reciprocal-refinement graph, with signed IEEE-like zero/infinity cases...
Definition ftz.ccm:106
constexpr basic_ftz32 & operator*=(basic_ftz32 b) noexcept
Assigns the corresponding FTZ result and returns this value by reference.
Definition ftz.ccm:130
friend constexpr basic_ftz32 operator-(basic_ftz32 a, basic_ftz32 b) noexcept
Applies the FTZ arithmetic/sign operation, preserving this policy and signed-zero semantics.
Definition ftz.ccm:96
static constexpr basic_ftz32 from_bits(unsigned int bits) noexcept
Imports binary32 bits, flushing only subnormal magnitudes to signed zero.
Definition ftz.ccm:82
friend constexpr basic_ftz32 operator+(basic_ftz32 a, basic_ftz32 b) noexcept
Applies the FTZ arithmetic/sign operation, preserving this policy and signed-zero semantics.
Definition ftz.ccm:91
friend constexpr bool operator<(basic_ftz32 a, basic_ftz32 b) noexcept
Compares values; NaN is unordered (only != is true), and the two zero signs compare equal.
Definition ftz.ccm:118
friend constexpr basic_ftz32 operator*(basic_ftz32 a, basic_ftz32 b) noexcept
Applies the FTZ arithmetic/sign operation, preserving this policy and signed-zero semantics.
Definition ftz.ccm:101
Results of finite startup witnesses for this scalar compilation and calling thread....