18#define IFAVX512(x,y) x
20#define IFAVX512(x,y) y
32template <
auto N,
auto ... candidates>
33concept one_of = ((N==candidates) || ... ||
false);
36template <
auto N,
auto ... candidates>
40template <
size_t N>
struct integer_traits {};
41template <>
struct integer_traits<8> {
using signed_t = int8_t;
using unsigned_t = uint8_t; };
42template <>
struct integer_traits<16> {
using signed_t = int16_t;
using unsigned_t = uint16_t; };
43template <>
struct integer_traits<32> {
using signed_t = int32_t;
using unsigned_t = uint32_t; };
44template <>
struct integer_traits<64> {
using signed_t = int64_t;
using unsigned_t = uint64_t; };
50requires one_of<
sizeof(T),1,2,4,8>
51using int_t =
typename integer_traits<
sizeof(T)*8>::signed_t;
56requires one_of<
sizeof(T),1,2,4,8>
57using uint_t =
typename integer_traits<
sizeof(T)*8>::unsigned_t;
62 static constexpr size_t value = N;
64 consteval operator size_t () const noexcept {
return N; }
90extern template bool cmp_unord(
float,
float)
noexcept;
91extern template bool cmp_unord(
double,
double)
noexcept;
107extern template bool cmp_ord(
float,
float)
noexcept;
108extern template bool cmp_ord(
double,
double)
noexcept;
112template <one_of_t<
float,
double> T>
119 constexpr int exponent_bias = std::is_same_v<T, float> ? 127 : 1023;
120 constexpr uint_type exponent_mask = std::is_same_v<T, float> ? uint_type(0x7F800000) : uint_type(0x7FF0000000000000ull);
121 constexpr uint_type mantissa_mask = std::is_same_v<T, float> ? uint_type(0x007FFFFF) : uint_type(0x000FFFFFFFFFFFFFull);
123 if (x == 0.0 ||
isnan(x) || std::isinf(x))
127 uint_type x_bits = std::bit_cast<uint_type>(x);
130 int exponent =
static_cast<int>(((x_bits & exponent_mask) >> (std::is_same_v<T, float> ? 23 : 52)) - exponent_bias);
131 uint_type mantissa = x_bits & mantissa_mask;
134 if (exponent != -exponent_bias)
135 mantissa |= std::is_same_v<T, float> ? (1u << 23) : (1ull << 52);
138 exponent +=
static_cast<int>(y);
141 if (exponent > std::numeric_limits<T>::max_exponent)
142 return std::numeric_limits<T>::infinity();
144 if (exponent < std::numeric_limits<T>::min_exponent)
145 return static_cast<T
>(0.0);
149 (x_bits & (std::is_same_v<T, float> ? 0x80000000u : 0x8000000000000000ull)) |
150 (
static_cast<uint_type
>(exponent + exponent_bias) << (std::is_same_v<T, float> ? 23 : 52)) |
151 (mantissa & mantissa_mask);
153 return std::bit_cast<T>(x_bits);
156 return std::scalbn(x,
static_cast<int>(y));
172template <CMPINT imm8,
typename T>
173requires (one_of_t<T,uint8_t,int8_t,uint16_t,int16_t,uint32_t,int32_t,uint64_t,int64_t> && (size_t(imm8) < 8uz))
175constexpr bool cmpint(T a, T b)
noexcept {
177 if constexpr (imm8 ==
TRUE)
return -1;
178 else if constexpr (imm8 ==
FALSE)
return 0;
179 else if constexpr (imm8 ==
LT)
return a < b;
180 else if constexpr (imm8 ==
NLT)
return a >= b;
181 else if constexpr (imm8 ==
LE)
return a <= b;
182 else if constexpr (imm8 ==
NLE)
return a > b;
183 else if constexpr (imm8 ==
EQ)
return a == b;
184 else if constexpr (imm8 ==
NE)
return a != b;
185 else static_assert(
false);
229template <CMP imm8,
typename T>
230requires (one_of_t<T,float,double> && (size_t(imm8) < 32uz))
232constexpr bool cmp(T a, T b)
noexcept {
234 if constexpr (imm8 ==
EQ_OQ)
return cmp_ord(a, b) && (a == b);
235 else if constexpr (imm8 ==
LT_OS)
return cmp_ord(a, b) && (a < b);
236 else if constexpr (imm8 ==
LE_OS)
return cmp_ord(a, b) && (a <= b);
240 else if constexpr (imm8 ==
NLE_US)
return cmp_unord(a, b) || !(a <= b);
242 else if constexpr (imm8 ==
EQ_UQ)
return cmp_unord(a, b) || (a == b);
243 else if constexpr (imm8 ==
NGE_US)
return cmp_unord(a, b) || !(a >= b);
245 else if constexpr (imm8 ==
FALSE_OQ)
return 0;
246 else if constexpr (imm8 ==
NEQ_OQ)
return cmp_ord(a, b) && (a != b);
247 else if constexpr (imm8 ==
GE_OS)
return cmp_ord(a, b) && (a >= b);
248 else if constexpr (imm8 ==
GT_OS)
return cmp_ord(a, b) && (a > b);
249 else if constexpr (imm8 ==
TRUE_UQ)
return -1;
250 else if constexpr (imm8 ==
EQ_OS)
return cmp_ord(a, b) && (a == b);
251 else if constexpr (imm8 ==
LT_OQ)
return cmp_ord(a, b) && (a < b);
252 else if constexpr (imm8 ==
LE_OQ)
return cmp_ord(a, b) && (a <= b);
256 else if constexpr (imm8 ==
NLE_UQ)
return cmp_unord(a, b) || !(a <= b);
258 else if constexpr (imm8 ==
EQ_US)
return cmp_unord(a, b) || (a == b);
259 else if constexpr (imm8 ==
NGE_UQ)
return cmp_unord(a, b) || !(a >= b);
261 else if constexpr (imm8 ==
FALSE_OS)
return 0;
262 else if constexpr (imm8 ==
NEQ_OS)
return cmp_ord(a, b) && (a != b);
263 else if constexpr (imm8 ==
GE_OQ)
return cmp_ord(a, b) && (a >= b);
264 else if constexpr (imm8 ==
GT_OQ)
return cmp_ord(a, b) && (a > b);
265 else if constexpr (imm8 ==
TRUE_US)
return -1;
266 else static_assert(
false);
278#if defined(EIN_TESTING) || defined(EIN_TESTING_NUMERICS)
279TEST_CASE(
"numerics",
"[numerics]") {
283 SECTION(
"Concepts: one_of and not_one_of") {
284 CHECK(one_of<1, 1, 2, 3>);
285 CHECK_FALSE(one_of<4, 1, 2, 3>);
286 CHECK(not_one_of<4, 1, 2, 3>);
287 CHECK_FALSE(not_one_of<1, 1, 2, 3>);
290 SECTION(
"integer_traits") {
291 CHECK(std::is_same_v<integer_traits<8>::signed_t, int8_t>);
292 CHECK(std::is_same_v<integer_traits<16>::unsigned_t, uint16_t>);
293 CHECK(std::is_same_v<integer_traits<32>::signed_t, int32_t>);
294 CHECK(std::is_same_v<integer_traits<64>::unsigned_t, uint64_t>);
297 SECTION(
"int_t and uint_t") {
304 SECTION(
"imm_t compile-time constant") {
305 constexpr auto imm4 = imm<4>;
306 CHECK(imm4.value == 4);
307 CHECK(
static_cast<size_t>(imm4) == 4);
310 SECTION(
"cmp_unord and cmp_ord") {
316 CHECK_FALSE(
cmp_ord(NAN, 1.0f));
317 CHECK_FALSE(
cmp_ord(1.0f, NAN));
321 constexpr float x = 2.0f;
322 constexpr float y = 3.0f;
323 CHECK(
scalef(x, y) == Approx(16.0f));
325 constexpr double a = 4.0;
326 constexpr double b = -2.0;
327 CHECK(
scalef(a, b) == Approx(1.0));
330 SECTION(
"CMPINT comparison") {
331 CHECK(cmpint<CMPINT::EQ>(5, 5));
332 CHECK(cmpint<CMPINT::NE>(5, 4));
333 CHECK_FALSE(cmpint<CMPINT::LT>(5, 4));
334 CHECK(cmpint<CMPINT::LE>(5, 5));
335 CHECK(cmpint<CMPINT::NLT>(5, 5));
339 SECTION(
"CMP floating-point comparison with AVX512") {
340 CHECK(cmp<CMP::EQ_OQ>(1.0f, 1.0f));
341 CHECK(cmp<CMP::LT_OS>(1.0f, 2.0f));
342 CHECK_FALSE(cmp<CMP::GT_OS>(1.0f, 2.0f));
N is not one of the candidates
N is one of the candidates
#define ein_artificial
[[artificial]].
#define ein_inline
inline [[always_inline]]
#define ein_nodiscard
C++17 [[nodiscard]].
static constexpr size_t value
constexpr size_t max_fp_comparison_predicate
AVX512 added many more floating point comparison types. Do we have them?
typename integer_traits< sizeof(T) *8 >::signed_t int_t
returns a signed integer type of the same size as T suitable for std::bitcast
constexpr bool cmpint(T a, T b) noexcept
constinit imm_t< N > imm
A compile time constant passed as an empty struct.
typename integer_traits< sizeof(T) *8 >::unsigned_t uint_t
returns an unsigned integer type of the same size as T suitable for std::bitcast
constexpr bool cmp(T a, T b) noexcept
perform an avx512 style floating point comparison for scalar values.
@ LE_OQ
Less-than-or-equal (ordered, nonsignaling) (AVX-512)
@ FALSE_OQ
False (ordered, nonsignaling) (AVX-512)
@ NEQ_OS
Not-equal (ordered, signaling) (AVX-512)
@ NLE_US
Not-less-than-or-equal (unordered, signaling)
@ TRUE_US
True (unordered, signaling) (AVX-512)
@ NGE_UQ
Not-greater-than-or-equal (unordered, nonsignaling) (AVX-512)
@ GE_OS
Greater-than-or-equal (ordered, signaling) (AVX-512)
@ NEQ_OQ
Not-equal (ordered, nonsignaling) (AVX-512)
@ ORD_Q
Ordered (nonsignaling)
@ LT_OS
Less-than (ordered, signaling)
@ GE_OQ
Greater-than-or-equal (ordered, nonsignaling) (AVX-512)
@ EQ_US
Equal (unordered, signaling) (AVX-512)
@ TRUE_UQ
True (unordered, nonsignaling) (AVX-512)
@ EQ_OS
Equal (ordered, signaling) (AVX-512)
@ FALSE_OS
False (ordered, signaling) (AVX-512)
@ GT_OS
Greater-than (ordered, signaling) (AVX-512)
@ ORD_S
Ordered (signaling) (AVX-512)
@ NLE_UQ
Not-less-than-or-equal (unordered, nonsignaling) (AVX-512)
@ GT_OQ
Greater-than (ordered, nonsignaling) (AVX-512)
@ UNORD_S
Unordered (signaling) (AVX-512)
@ LE_OS
Less-than-or-equal (ordered, signaling)
@ EQ_OQ
Equal (ordered, nonsignaling)
@ NEQ_UQ
Not-equal (unordered, nonsignaling)
@ EQ_UQ
Equal (unordered, nonsignaling) (AVX-512)
@ NLT_US
Not-less-than (unordered, signaling)
@ UNORD_Q
Unordered (nonsignaling)
@ LT_OQ
Less-than (ordered, nonsignaling) (AVX-512)
@ NLT_UQ
Not-less-than (unordered, nonsignaling) (AVX-512)
@ NGT_US
Not-greater-than (unordered, signaling) (AVX-512)
@ NGE_US
Not-greater-than-or-equal (unordered, signaling) (AVX-512)
@ NGT_UQ
Not-greater-than (unordered, nonsignaling) (AVX-512)
@ NEQ_US
Not-equal (unordered, signaling) (AVX-512)
A compile time constant passed as an empty struct.
#define ein_const
[[const]] is not const
template bool cmp_unord(float, float) noexcept
template bool cmp_ord(float, float) noexcept
X template float scalef(float, float) noexcept
cond xmacro
constexpr bool isnan(ein::bf16 x) noexcept