Eigen  5.0.1
 
Loading...
Searching...
No Matches
Half.h
1// This file is part of Eigen, a lightweight C++ template library
2// for linear algebra.
3//
4// This Source Code Form is subject to the terms of the Mozilla
5// Public License v. 2.0. If a copy of the MPL was not distributed
6// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
7//
8// The conversion routines are Copyright (c) Fabian Giesen, 2016.
9// The original license follows:
10//
11// Copyright (c) Fabian Giesen, 2016
12// All rights reserved.
13// Redistribution and use in source and binary forms, with or without
14// modification, are permitted.
15// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26// SPDX-License-Identifier: MPL-2.0
27
28// Standard 16-bit float type, mostly useful for GPUs. Defines a new
29// type Eigen::half (inheriting either from CUDA's or HIP's __half struct) with
30// operator overloads such that it behaves basically as an arithmetic
31// type. It will be quite slow on CPUs (so it is recommended to stay
32// in fp32 for CPUs, except for simple parameter conversions, I/O
33// to disk and the likes), but fast on GPUs.
34
35#ifndef EIGEN_HALF_H
36#define EIGEN_HALF_H
37
38// IWYU pragma: private
39#include "../../InternalHeaderCheck.h"
40
41// When compiling with GPU support, the "__half_raw" base class as well as
42// some other routines are defined in the GPU compiler header files
43// (cuda_fp16.h, hip_fp16.h), and they are not tagged constexpr
44// As a consequence, we get compile failures when compiling Eigen with
45// GPU support. Hence the need to disable EIGEN_CONSTEXPR when building
46// Eigen with GPU support.
47// Any functions that require `numext::bit_cast` may also not be constexpr,
48// including any native types when setting via raw bit values.
49#if defined(EIGEN_HAS_GPU_FP16) || EIGEN_HAS_ARM64_FP16 || defined(EIGEN_HAS_BUILTIN_FLOAT16)
50#define _EIGEN_MAYBE_CONSTEXPR
51#else
52#define _EIGEN_MAYBE_CONSTEXPR constexpr
53#endif
54
55#define F16_PACKET_FUNCTION(PACKET_F, PACKET_F16, METHOD) \
56 template <> \
57 EIGEN_UNUSED EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC PACKET_F16 METHOD<PACKET_F16>(const PACKET_F16& _x) { \
58 return float2half(METHOD<PACKET_F>(half2float(_x))); \
59 }
60
61#define EIGEN_INSTANTIATE_GENERIC_MATH_FUNCS_F16(PACKET_F, PACKET_F16) \
62 F16_PACKET_FUNCTION(PACKET_F, PACKET_F16, pcos) \
63 F16_PACKET_FUNCTION(PACKET_F, PACKET_F16, psin) \
64 F16_PACKET_FUNCTION(PACKET_F, PACKET_F16, psinh) \
65 F16_PACKET_FUNCTION(PACKET_F, PACKET_F16, pcosh) \
66 F16_PACKET_FUNCTION(PACKET_F, PACKET_F16, pasinh) \
67 F16_PACKET_FUNCTION(PACKET_F, PACKET_F16, pacosh) \
68 F16_PACKET_FUNCTION(PACKET_F, PACKET_F16, pexp) \
69 F16_PACKET_FUNCTION(PACKET_F, PACKET_F16, pexp2) \
70 F16_PACKET_FUNCTION(PACKET_F, PACKET_F16, pexpm1) \
71 F16_PACKET_FUNCTION(PACKET_F, PACKET_F16, plog) \
72 F16_PACKET_FUNCTION(PACKET_F, PACKET_F16, plog1p) \
73 F16_PACKET_FUNCTION(PACKET_F, PACKET_F16, plog2) \
74 F16_PACKET_FUNCTION(PACKET_F, PACKET_F16, plog10) \
75 F16_PACKET_FUNCTION(PACKET_F, PACKET_F16, preciprocal) \
76 F16_PACKET_FUNCTION(PACKET_F, PACKET_F16, prsqrt) \
77 F16_PACKET_FUNCTION(PACKET_F, PACKET_F16, pcbrt) \
78 F16_PACKET_FUNCTION(PACKET_F, PACKET_F16, psqrt) \
79 F16_PACKET_FUNCTION(PACKET_F, PACKET_F16, ptanh)
80
81// F16 wrappers for contrib/SpecialFunctions.
82#define EIGEN_INSTANTIATE_SPECIAL_FUNCS_F16(PACKET_F, PACKET_F16) \
83 F16_PACKET_FUNCTION(PACKET_F, PACKET_F16, perf) \
84 F16_PACKET_FUNCTION(PACKET_F, PACKET_F16, pndtri)
85
86#define EIGEN_INSTANTIATE_BESSEL_FUNCS_F16(PACKET_F, PACKET_F16) \
87 F16_PACKET_FUNCTION(PACKET_F, PACKET_F16, pbessel_i0) \
88 F16_PACKET_FUNCTION(PACKET_F, PACKET_F16, pbessel_i0e) \
89 F16_PACKET_FUNCTION(PACKET_F, PACKET_F16, pbessel_i1) \
90 F16_PACKET_FUNCTION(PACKET_F, PACKET_F16, pbessel_i1e) \
91 F16_PACKET_FUNCTION(PACKET_F, PACKET_F16, pbessel_j0) \
92 F16_PACKET_FUNCTION(PACKET_F, PACKET_F16, pbessel_j1) \
93 F16_PACKET_FUNCTION(PACKET_F, PACKET_F16, pbessel_k0) \
94 F16_PACKET_FUNCTION(PACKET_F, PACKET_F16, pbessel_k0e) \
95 F16_PACKET_FUNCTION(PACKET_F, PACKET_F16, pbessel_k1) \
96 F16_PACKET_FUNCTION(PACKET_F, PACKET_F16, pbessel_k1e) \
97 F16_PACKET_FUNCTION(PACKET_F, PACKET_F16, pbessel_y0) \
98 F16_PACKET_FUNCTION(PACKET_F, PACKET_F16, pbessel_y1)
99
100namespace Eigen {
101
102struct half;
103
104namespace half_impl {
105
106// We want to use the __half_raw struct from the HIP header file only during the device compile phase.
107// This is required because of a quirk in the way TensorFlow GPU builds are done.
108// When compiling TensorFlow source code with GPU support, files that
109// * contain GPU kernels (i.e. *.cu.cc files) are compiled via hipcc
110// * do not contain GPU kernels ( i.e. *.cc files) are compiled via gcc (typically)
111//
112// Tensorflow uses the Eigen::half type as its FP16 type, and there are functions that
113// * are defined in a file that gets compiled via hipcc AND
114// * have Eigen::half as a pass-by-value argument AND
115// * are called in a file that gets compiled via gcc
116//
117// In the scenario described above the caller and callee will see different versions
118// of the Eigen::half base class __half_raw, and they will be compiled by different compilers
119//
120// There appears to be an ABI mismatch between gcc and clang (which is called by hipcc) that results in
121// the callee getting corrupted values for the Eigen::half argument.
122//
123// Making the host side compile phase of hipcc use the same Eigen::half impl, as the gcc compile, resolves
124// this error, and hence the following convoluted #if condition
125#if !defined(EIGEN_GPUCC) || !defined(EIGEN_GPU_COMPILE_PHASE)
126
127// Make our own __half_raw definition that is similar to CUDA's.
128struct __half_raw {
129 struct construct_from_rep_tag {};
130#if (defined(EIGEN_GPUCC) && !defined(EIGEN_GPU_COMPILE_PHASE))
131 // Eigen::half can be used as the datatype for shared memory declarations (in Eigen and TF)
132 // The element type for shared memory cannot have non-trivial constructors
133 // and hence the following special casing (which skips the zero-initialization).
134 // Note that this check gets done even in the host compilation phase, and
135 // hence the need for this
136 EIGEN_DEVICE_FUNC _EIGEN_MAYBE_CONSTEXPR __half_raw() {}
137#else
138 EIGEN_DEVICE_FUNC _EIGEN_MAYBE_CONSTEXPR __half_raw() : x(0) {}
139#endif
140
141#if EIGEN_HAS_ARM64_FP16
142 explicit EIGEN_DEVICE_FUNC __half_raw(numext::uint16_t raw) : x(numext::bit_cast<__fp16>(raw)) {}
143 EIGEN_DEVICE_FUNC constexpr __half_raw(construct_from_rep_tag, __fp16 rep) : x{rep} {}
144 __fp16 x;
145#elif defined(EIGEN_HAS_BUILTIN_FLOAT16)
146 explicit EIGEN_DEVICE_FUNC __half_raw(numext::uint16_t raw) : x(numext::bit_cast<_Float16>(raw)) {}
147 EIGEN_DEVICE_FUNC constexpr __half_raw(construct_from_rep_tag, _Float16 rep) : x{rep} {}
148 _Float16 x;
149#else
150 explicit EIGEN_DEVICE_FUNC constexpr __half_raw(numext::uint16_t raw) : x(raw) {}
151 EIGEN_DEVICE_FUNC constexpr __half_raw(construct_from_rep_tag, numext::uint16_t rep) : x{rep} {}
152 numext::uint16_t x;
153#endif
154};
155
156#elif defined(EIGEN_HIPCC)
157// HIP GPU compile phase: nothing to do here.
158// HIP fp16 header file has a definition for __half_raw
159#elif defined(EIGEN_CUDACC)
160
161// CUDA GPU compile phase.
162
163#elif defined(SYCL_DEVICE_ONLY)
164using __half_raw = cl::sycl::half;
165#endif
166
167EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC _EIGEN_MAYBE_CONSTEXPR __half_raw raw_uint16_to_half(numext::uint16_t x);
168EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC numext::uint16_t raw_half_as_uint16(const __half_raw& h);
169EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC __half_raw float_to_half_rtne(float ff);
170EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC float half_to_float(__half_raw h);
171
172struct half_base : public __half_raw {
173 EIGEN_DEVICE_FUNC _EIGEN_MAYBE_CONSTEXPR half_base() {}
174 EIGEN_DEVICE_FUNC _EIGEN_MAYBE_CONSTEXPR half_base(const __half_raw& h) : __half_raw(h) {}
175
176#if defined(EIGEN_GPUCC)
177#if defined(EIGEN_HIPCC)
178 // Delegate to raw_uint16_to_half, which reinterprets the raw bits for every storage type of
179 // __half_raw::x. In the host compile phase on platforms with a native fp16 type (e.g. __fp16 on
180 // arm64), a direct "x = __half_as_ushort(h)" would perform a numeric integer-to-float conversion.
181 // numext::bit_cast is used to extract the bits because hip_fp16.h defines __half_as_ushort for
182 // the device compile phase only (host references fail to link against ROCm 6.3).
183 EIGEN_DEVICE_FUNC _EIGEN_MAYBE_CONSTEXPR half_base(const __half& h)
184 : __half_raw(raw_uint16_to_half(numext::bit_cast<numext::uint16_t>(h))) {}
185#elif defined(EIGEN_CUDACC)
186 EIGEN_DEVICE_FUNC _EIGEN_MAYBE_CONSTEXPR half_base(const __half& h) : __half_raw(*(__half_raw*)&h) {}
187#endif
188#endif
189};
190
191} // namespace half_impl
192
193// Class definition.
194struct half : public half_impl::half_base {
195 // Writing this out as separate #if-else blocks to make the code easier to follow
196 // The same applies to most #if-else blocks in this file
197#if !defined(EIGEN_GPUCC) || !defined(EIGEN_GPU_COMPILE_PHASE)
198 // Use the same base class for the following two scenarios
199 // * when compiling without GPU support enabled
200 // * during host compile phase when compiling with GPU support enabled
201 using __half_raw = half_impl::__half_raw;
202#elif defined(EIGEN_HIPCC)
203 // Nothing to do here
204 // HIP fp16 header file has a definition for __half_raw
205#elif defined(EIGEN_CUDACC)
206 // Nothing to do here.
207#endif
208
209 EIGEN_DEVICE_FUNC _EIGEN_MAYBE_CONSTEXPR half() {}
210
211 EIGEN_DEVICE_FUNC _EIGEN_MAYBE_CONSTEXPR half(const __half_raw& h) : half_impl::half_base(h) {}
212
213#if defined(EIGEN_GPUCC)
214#if defined(EIGEN_HIPCC)
215 EIGEN_DEVICE_FUNC _EIGEN_MAYBE_CONSTEXPR half(const __half& h) : half_impl::half_base(h) {}
216#elif defined(EIGEN_CUDACC)
217 EIGEN_DEVICE_FUNC _EIGEN_MAYBE_CONSTEXPR half(const __half& h) : half_impl::half_base(h) {}
218#endif
219#endif
220
221// In the device compile phase __half_raw is the vendor type, which has no construct_from_rep_tag,
222// so these constructors are restricted to the host compile phase and non-GPU builds.
223#if EIGEN_HAS_ARM64_FP16 && !defined(EIGEN_GPU_COMPILE_PHASE)
224 explicit EIGEN_DEVICE_FUNC _EIGEN_MAYBE_CONSTEXPR half(__fp16 b)
225 : half(__half_raw(__half_raw::construct_from_rep_tag(), b)) {}
226#elif defined(EIGEN_HAS_BUILTIN_FLOAT16) && !defined(EIGEN_GPU_COMPILE_PHASE)
227 explicit EIGEN_DEVICE_FUNC _EIGEN_MAYBE_CONSTEXPR half(_Float16 b)
228 : half(__half_raw(__half_raw::construct_from_rep_tag(), b)) {}
229#endif
230
231 explicit EIGEN_DEVICE_FUNC _EIGEN_MAYBE_CONSTEXPR half(bool b)
232 : half_impl::half_base(half_impl::raw_uint16_to_half(b ? 0x3c00 : 0)) {}
233 template <class T>
234 explicit EIGEN_DEVICE_FUNC half(T val)
235 : half_impl::half_base(half_impl::float_to_half_rtne(static_cast<float>(val))) {}
236 explicit EIGEN_DEVICE_FUNC half(float f) : half_impl::half_base(half_impl::float_to_half_rtne(f)) {}
237
238 // Following the convention of numpy, converting between complex and
239 // float will lead to loss of imag value.
240 template <typename RealScalar>
241 explicit EIGEN_DEVICE_FUNC half(std::complex<RealScalar> c)
242 : half_impl::half_base(half_impl::float_to_half_rtne(static_cast<float>(c.real()))) {}
243
244 EIGEN_DEVICE_FUNC operator float() const { // NOLINT: Allow implicit conversion to float, because it is lossless.
245 return half_impl::half_to_float(*this);
246 }
247
248#if defined(EIGEN_HAS_GPU_FP16) && !defined(EIGEN_GPU_COMPILE_PHASE)
249 EIGEN_DEVICE_FUNC operator __half() const {
250 ::__half_raw hr;
251 // raw_half_as_uint16 reinterprets the raw bits for every storage type of __half_raw::x.
252 // A direct "hr.x = x" would perform a numeric float-to-integer conversion when x has a
253 // native fp16 type (e.g. __fp16 on arm64), since the vendor ::__half_raw::x is an integer.
254 hr.x = half_impl::raw_half_as_uint16(*this);
255 return __half(hr);
256 }
257#endif
258};
259
260// TODO(majnemer): Get rid of this once we can rely on C++17 inline variables do
261// solve the ODR issue.
262namespace half_impl {
263template <typename = void>
264struct numeric_limits_half_impl {
265 static constexpr const bool is_specialized = true;
266 static constexpr const bool is_signed = true;
267 static constexpr const bool is_integer = false;
268 static constexpr const bool is_exact = false;
269 static constexpr const bool has_infinity = true;
270 static constexpr const bool has_quiet_NaN = true;
271 static constexpr const bool has_signaling_NaN = true;
272 EIGEN_DIAGNOSTICS(push)
273 EIGEN_DISABLE_DEPRECATED_WARNING
274 static constexpr const std::float_denorm_style has_denorm = std::denorm_present;
275 static constexpr const bool has_denorm_loss = false;
276 EIGEN_DIAGNOSTICS(pop)
277 static constexpr const std::float_round_style round_style = std::round_to_nearest;
278 static constexpr const bool is_iec559 = true;
279 // The C++ standard defines this as "true if the set of values representable
280 // by the type is finite." Half has finite precision.
281 static constexpr const bool is_bounded = true;
282 static constexpr const bool is_modulo = false;
283 static constexpr const int digits = 11;
284 static constexpr const int digits10 =
285 3; // according to http://half.sourceforge.net/structstd_1_1numeric__limits_3_01half__float_1_1half_01_4.html
286 static constexpr const int max_digits10 =
287 5; // according to http://half.sourceforge.net/structstd_1_1numeric__limits_3_01half__float_1_1half_01_4.html
288 static constexpr const int radix = std::numeric_limits<float>::radix;
289 static constexpr const int min_exponent = -13;
290 static constexpr const int min_exponent10 = -4;
291 static constexpr const int max_exponent = 16;
292 static constexpr const int max_exponent10 = 4;
293 static constexpr const bool traps = std::numeric_limits<float>::traps;
294 // IEEE754: "The implementer shall choose how tininess is detected, but shall
295 // detect tininess in the same way for all operations in radix two"
296 static constexpr const bool tinyness_before = std::numeric_limits<float>::tinyness_before;
297
298 static _EIGEN_MAYBE_CONSTEXPR Eigen::half(min)() { return Eigen::half_impl::raw_uint16_to_half(0x0400); }
299 static _EIGEN_MAYBE_CONSTEXPR Eigen::half lowest() { return Eigen::half_impl::raw_uint16_to_half(0xfbff); }
300 static _EIGEN_MAYBE_CONSTEXPR Eigen::half(max)() { return Eigen::half_impl::raw_uint16_to_half(0x7bff); }
301 static _EIGEN_MAYBE_CONSTEXPR Eigen::half epsilon() { return Eigen::half_impl::raw_uint16_to_half(0x1400); }
302 static _EIGEN_MAYBE_CONSTEXPR Eigen::half round_error() { return Eigen::half_impl::raw_uint16_to_half(0x3800); }
303 static _EIGEN_MAYBE_CONSTEXPR Eigen::half infinity() { return Eigen::half_impl::raw_uint16_to_half(0x7c00); }
304 static _EIGEN_MAYBE_CONSTEXPR Eigen::half quiet_NaN() { return Eigen::half_impl::raw_uint16_to_half(0x7e00); }
305 static _EIGEN_MAYBE_CONSTEXPR Eigen::half signaling_NaN() { return Eigen::half_impl::raw_uint16_to_half(0x7d00); }
306 static _EIGEN_MAYBE_CONSTEXPR Eigen::half denorm_min() { return Eigen::half_impl::raw_uint16_to_half(0x0001); }
307};
308
309// Redundant out-of-class definitions are required pre-C++17 but deprecated since.
310#if EIGEN_COMP_CXXVER < 17
311template <typename T>
312constexpr const bool numeric_limits_half_impl<T>::is_specialized;
313template <typename T>
314constexpr const bool numeric_limits_half_impl<T>::is_signed;
315template <typename T>
316constexpr const bool numeric_limits_half_impl<T>::is_integer;
317template <typename T>
318constexpr const bool numeric_limits_half_impl<T>::is_exact;
319template <typename T>
320constexpr const bool numeric_limits_half_impl<T>::has_infinity;
321template <typename T>
322constexpr const bool numeric_limits_half_impl<T>::has_quiet_NaN;
323template <typename T>
324constexpr const bool numeric_limits_half_impl<T>::has_signaling_NaN;
325EIGEN_DIAGNOSTICS(push)
326EIGEN_DISABLE_DEPRECATED_WARNING
327template <typename T>
328constexpr const std::float_denorm_style numeric_limits_half_impl<T>::has_denorm;
329template <typename T>
330constexpr const bool numeric_limits_half_impl<T>::has_denorm_loss;
331EIGEN_DIAGNOSTICS(pop)
332template <typename T>
333constexpr const std::float_round_style numeric_limits_half_impl<T>::round_style;
334template <typename T>
335constexpr const bool numeric_limits_half_impl<T>::is_iec559;
336template <typename T>
337constexpr const bool numeric_limits_half_impl<T>::is_bounded;
338template <typename T>
339constexpr const bool numeric_limits_half_impl<T>::is_modulo;
340template <typename T>
341constexpr const int numeric_limits_half_impl<T>::digits;
342template <typename T>
343constexpr const int numeric_limits_half_impl<T>::digits10;
344template <typename T>
345constexpr const int numeric_limits_half_impl<T>::max_digits10;
346template <typename T>
347constexpr const int numeric_limits_half_impl<T>::radix;
348template <typename T>
349constexpr const int numeric_limits_half_impl<T>::min_exponent;
350template <typename T>
351constexpr const int numeric_limits_half_impl<T>::min_exponent10;
352template <typename T>
353constexpr const int numeric_limits_half_impl<T>::max_exponent;
354template <typename T>
355constexpr const int numeric_limits_half_impl<T>::max_exponent10;
356template <typename T>
357constexpr const bool numeric_limits_half_impl<T>::traps;
358template <typename T>
359constexpr const bool numeric_limits_half_impl<T>::tinyness_before;
360#endif
361} // end namespace half_impl
362} // end namespace Eigen
363
364namespace std {
365// If std::numeric_limits<T> is specialized, should also specialize
366// std::numeric_limits<const T>, std::numeric_limits<volatile T>, and
367// std::numeric_limits<const volatile T>
368// https://stackoverflow.com/a/16519653/
369template <>
370class numeric_limits<Eigen::half> : public Eigen::half_impl::numeric_limits_half_impl<> {};
371template <>
372class numeric_limits<const Eigen::half> : public numeric_limits<Eigen::half> {};
373template <>
374class numeric_limits<volatile Eigen::half> : public numeric_limits<Eigen::half> {};
375template <>
376class numeric_limits<const volatile Eigen::half> : public numeric_limits<Eigen::half> {};
377} // end namespace std
378
379namespace Eigen {
380
381namespace half_impl {
382
383#if defined(EIGEN_GPU_COMPILE_PHASE)
384// Note: We deliberately do *not* define this to 1 even if we have Arm's native
385// fp16 type since GPU half types are rather different from native CPU half types.
386#define EIGEN_HAS_NATIVE_GPU_FP16
387#endif
388
389// Intrinsics for native fp16 support. Note that on current hardware,
390// these are no faster than fp32 arithmetic (you need to use the half2
391// versions to get the ALU speed increased), but you do save the
392// conversion steps back and forth.
393
394#if defined(EIGEN_HAS_NATIVE_GPU_FP16)
395EIGEN_STRONG_INLINE __device__ half operator+(const half& a, const half& b) { return __hadd(::__half(a), ::__half(b)); }
396EIGEN_STRONG_INLINE __device__ half operator*(const half& a, const half& b) { return __hmul(a, b); }
397EIGEN_STRONG_INLINE __device__ half operator-(const half& a, const half& b) { return __hsub(a, b); }
398EIGEN_STRONG_INLINE __device__ half operator/(const half& a, const half& b) { return __hdiv(a, b); }
399EIGEN_STRONG_INLINE __device__ half operator-(const half& a) { return __hneg(a); }
400EIGEN_STRONG_INLINE __device__ half& operator+=(half& a, const half& b) {
401 a = a + b;
402 return a;
403}
404EIGEN_STRONG_INLINE __device__ half& operator*=(half& a, const half& b) {
405 a = a * b;
406 return a;
407}
408EIGEN_STRONG_INLINE __device__ half& operator-=(half& a, const half& b) {
409 a = a - b;
410 return a;
411}
412EIGEN_STRONG_INLINE __device__ half& operator/=(half& a, const half& b) {
413 a = a / b;
414 return a;
415}
416EIGEN_STRONG_INLINE __device__ bool operator==(const half& a, const half& b) { return __heq(a, b); }
417EIGEN_STRONG_INLINE __device__ bool operator!=(const half& a, const half& b) { return __hne(a, b); }
418EIGEN_STRONG_INLINE __device__ bool operator<(const half& a, const half& b) { return __hlt(a, b); }
419EIGEN_STRONG_INLINE __device__ bool operator<=(const half& a, const half& b) { return __hle(a, b); }
420EIGEN_STRONG_INLINE __device__ bool operator>(const half& a, const half& b) { return __hgt(a, b); }
421EIGEN_STRONG_INLINE __device__ bool operator>=(const half& a, const half& b) { return __hge(a, b); }
422
423#endif // EIGEN_HAS_NATIVE_GPU_FP16
424
425#if (EIGEN_HAS_ARM64_FP16 || defined(EIGEN_HAS_BUILTIN_FLOAT16)) && !defined(EIGEN_GPU_COMPILE_PHASE)
426// nvcc's EDG front end does not promote __fp16 arithmetic to float, and it ranks the mandatory
427// __fp16 -> float promotion as an exact match, so constructing a half from a native fp16 expression
428// is an ambiguous tie between half(__fp16) and half(float). Every nvcc arm64 translation unit that
429// includes Eigen then fails to compile. Building __half_raw names one constructor, and is what
430// half(__fp16) and half(_Float16) do internally, so the stored value is unchanged.
431EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half half_from_rep(decltype(__half_raw::x) rep) {
432 return half(__half_raw(__half_raw::construct_from_rep_tag(), rep));
433}
434#endif
435
436#if defined(EIGEN_HAS_ARM64_FP16_SCALAR_ARITHMETIC) && !defined(EIGEN_GPU_COMPILE_PHASE)
437EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half operator+(const half& a, const half& b) {
438 return half_from_rep(vaddh_f16(a.x, b.x));
439}
440EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half operator*(const half& a, const half& b) {
441 return half_from_rep(vmulh_f16(a.x, b.x));
442}
443EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half operator-(const half& a, const half& b) {
444 return half_from_rep(vsubh_f16(a.x, b.x));
445}
446EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half operator/(const half& a, const half& b) {
447 return half_from_rep(vdivh_f16(a.x, b.x));
448}
449EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half operator-(const half& a) { return half_from_rep(vnegh_f16(a.x)); }
450EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half& operator+=(half& a, const half& b) {
451 a = half_from_rep(vaddh_f16(a.x, b.x));
452 return a;
453}
454EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half& operator*=(half& a, const half& b) {
455 a = half_from_rep(vmulh_f16(a.x, b.x));
456 return a;
457}
458EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half& operator-=(half& a, const half& b) {
459 a = half_from_rep(vsubh_f16(a.x, b.x));
460 return a;
461}
462EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half& operator/=(half& a, const half& b) {
463 a = half_from_rep(vdivh_f16(a.x, b.x));
464 return a;
465}
466EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC bool operator==(const half& a, const half& b) { return vceqh_f16(a.x, b.x); }
467EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC bool operator!=(const half& a, const half& b) { return !vceqh_f16(a.x, b.x); }
468EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC bool operator<(const half& a, const half& b) { return vclth_f16(a.x, b.x); }
469EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC bool operator<=(const half& a, const half& b) { return vcleh_f16(a.x, b.x); }
470EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC bool operator>(const half& a, const half& b) { return vcgth_f16(a.x, b.x); }
471EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC bool operator>=(const half& a, const half& b) { return vcgeh_f16(a.x, b.x); }
472
473#elif (EIGEN_HAS_ARM64_FP16 || defined(EIGEN_HAS_BUILTIN_FLOAT16)) && !defined(EIGEN_GPU_COMPILE_PHASE)
474
475// NOTE: While GCC appears to only apply `-Wdouble-promotion` to single-to-double
476// conversions, Clang applies the warning to half-to-single implicit conversion also. For
477// the operations below, implicit conversion is intentional so that---depending on the
478// target features---the compiler can choose between using native half-precision
479// instructions or converting to single-precision.
480#if EIGEN_COMP_CLANG
481#pragma clang diagnostic push
482#pragma clang diagnostic ignored "-Wdouble-promotion"
483#endif
484
485EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half operator+(const half& a, const half& b) {
486 return half_from_rep(static_cast<decltype(a.x)>(a.x + b.x));
487}
488EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half operator*(const half& a, const half& b) {
489 return half_from_rep(static_cast<decltype(a.x)>(a.x * b.x));
490}
491EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half operator-(const half& a, const half& b) {
492 return half_from_rep(static_cast<decltype(a.x)>(a.x - b.x));
493}
494EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half operator/(const half& a, const half& b) {
495 return half_from_rep(static_cast<decltype(a.x)>(a.x / b.x));
496}
497EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half operator-(const half& a) {
498 return half_from_rep(static_cast<decltype(a.x)>(-a.x));
499}
500EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half& operator+=(half& a, const half& b) {
501 a = a + b;
502 return a;
503}
504EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half& operator*=(half& a, const half& b) {
505 a = a * b;
506 return a;
507}
508EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half& operator-=(half& a, const half& b) {
509 a = a - b;
510 return a;
511}
512EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half& operator/=(half& a, const half& b) {
513 a = a / b;
514 return a;
515}
516EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC bool operator==(const half& a, const half& b) { return a.x == b.x; }
517EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC bool operator!=(const half& a, const half& b) { return a.x != b.x; }
518EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC bool operator<(const half& a, const half& b) { return a.x < b.x; }
519EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC bool operator<=(const half& a, const half& b) { return a.x <= b.x; }
520EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC bool operator>(const half& a, const half& b) { return a.x > b.x; }
521EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC bool operator>=(const half& a, const half& b) { return a.x >= b.x; }
522
523#if EIGEN_COMP_CLANG
524#pragma clang diagnostic pop
525#endif
526
527// We need to distinguish ‘clang as the CUDA compiler’ from ‘clang as the host compiler,
528// invoked by NVCC’ (e.g. on MacOS). The former needs to see both host and device implementation
529// of the functions, while the latter can only deal with one of them.
530#elif !defined(EIGEN_HAS_NATIVE_GPU_FP16) || (EIGEN_COMP_CLANG && !EIGEN_COMP_NVCC) // Emulate support for half floats
531
532#if EIGEN_COMP_CLANG && defined(EIGEN_GPUCC)
533// We need to provide emulated *host-side* FP16 operators for clang.
534#pragma push_macro("EIGEN_DEVICE_FUNC")
535#undef EIGEN_DEVICE_FUNC
536#if defined(EIGEN_GPUCC) && defined(EIGEN_HAS_NATIVE_GPU_FP16)
537#define EIGEN_DEVICE_FUNC __host__
538#else // both host and device need emulated ops.
539#define EIGEN_DEVICE_FUNC __host__ __device__
540#endif
541#endif
542
543// Definitions for CPUs and older HIP+CUDA, mostly working through conversion
544// to/from fp32.
545EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half operator+(const half& a, const half& b) { return half(float(a) + float(b)); }
546EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half operator*(const half& a, const half& b) { return half(float(a) * float(b)); }
547EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half operator-(const half& a, const half& b) { return half(float(a) - float(b)); }
548EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half operator/(const half& a, const half& b) { return half(float(a) / float(b)); }
549EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half operator-(const half& a) {
550 return raw_uint16_to_half(static_cast<numext::uint16_t>(raw_half_as_uint16(a) ^ 0x8000));
551}
552EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half& operator+=(half& a, const half& b) {
553 a = half(float(a) + float(b));
554 return a;
555}
556EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half& operator*=(half& a, const half& b) {
557 a = half(float(a) * float(b));
558 return a;
559}
560EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half& operator-=(half& a, const half& b) {
561 a = half(float(a) - float(b));
562 return a;
563}
564EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half& operator/=(half& a, const half& b) {
565 a = half(float(a) / float(b));
566 return a;
567}
568
569// Non-negative floating point numbers have a monotonic mapping to non-negative integers.
570// This property allows floating point numbers to be reinterpreted as integers for comparisons, which is useful if there
571// is no native floating point comparison operator. Floating point signedness is handled by the sign-magnitude
572// representation, whereas integers typically use two's complement. Converting the bit pattern from sign-magnitude to
573// two's complement allows the transformed bit patterns be compared as signed integers. All edge cases (+/-0 and +/-
574// infinity) are handled automatically, except NaN.
575//
576// fp16 uses 1 sign bit, 5 exponent bits, and 10 mantissa bits. The bit pattern conveys NaN when all the exponent
577// bits (5) are set, and at least one mantissa bit is set. The sign bit is irrelevant for determining NaN. To check for
578// NaN, clear the sign bit and check if the integral representation is greater than 01111100000000. To test
579// for non-NaN, clear the sign bit and check if the integral representation is less than or equal to 01111100000000.
580
581// convert sign-magnitude representation to two's complement
582EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC int16_t mapToSigned(uint16_t a) {
583#if EIGEN_COMP_NVHPC
584 // NVHPC through at least 26.5 can miscompile repeated inlined comparisons of the transformed integer representations.
585 EIGEN_OPTIMIZATION_BARRIER(a)
586#endif
587 constexpr uint16_t kAbsMask = (1 << 15) - 1;
588 // If the sign bit is set, clear the sign bit and return the (integer) negation. Otherwise, return the input.
589 return (a >> 15) ? -(a & kAbsMask) : a;
590}
591EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC bool isOrdered(const half& a, const half& b) {
592 constexpr uint16_t kInf = ((1 << 5) - 1) << 10;
593 constexpr uint16_t kAbsMask = (1 << 15) - 1;
594 return numext::maxi(a.x & kAbsMask, b.x & kAbsMask) <= kInf;
595}
596EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC bool operator==(const half& a, const half& b) {
597 bool result = mapToSigned(a.x) == mapToSigned(b.x);
598 result &= isOrdered(a, b);
599 return result;
600}
601EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC bool operator!=(const half& a, const half& b) { return !(a == b); }
602EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC bool operator<(const half& a, const half& b) {
603 bool result = mapToSigned(a.x) < mapToSigned(b.x);
604 result &= isOrdered(a, b);
605 return result;
606}
607EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC bool operator<=(const half& a, const half& b) {
608 bool result = mapToSigned(a.x) <= mapToSigned(b.x);
609 result &= isOrdered(a, b);
610 return result;
611}
612EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC bool operator>(const half& a, const half& b) {
613 bool result = mapToSigned(a.x) > mapToSigned(b.x);
614 result &= isOrdered(a, b);
615 return result;
616}
617EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC bool operator>=(const half& a, const half& b) {
618 bool result = mapToSigned(a.x) >= mapToSigned(b.x);
619 result &= isOrdered(a, b);
620 return result;
621}
622
623#if EIGEN_COMP_CLANG && defined(EIGEN_GPUCC)
624#pragma pop_macro("EIGEN_DEVICE_FUNC")
625#endif
626
627#endif // Emulate support for half floats
628
629// Division by an index. Do it in full float precision to avoid accuracy
630// issues in converting the denominator to half.
631EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half operator/(const half& a, Index b) {
632 return half(static_cast<float>(a) / static_cast<float>(b));
633}
634
635EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half operator++(half& a) {
636 a += half(1);
637 return a;
638}
639
640EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half operator--(half& a) {
641 a -= half(1);
642 return a;
643}
644
645EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half operator++(half& a, int) {
646 half original_value = a;
647 ++a;
648 return original_value;
649}
650
651EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half operator--(half& a, int) {
652 half original_value = a;
653 --a;
654 return original_value;
655}
656
657// Conversion routines, including fallbacks for the host or older CUDA.
658// Note that newer Intel CPUs (Haswell or newer) have vectorized versions of
659// these in hardware. If we need more performance on older/other CPUs, they are
660// also possible to vectorize directly.
661
662EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC _EIGEN_MAYBE_CONSTEXPR __half_raw raw_uint16_to_half(numext::uint16_t x) {
663 // In the device compile phase of a GPU build we cannot simply do a "return __half_raw(x)",
664 // because there __half_raw is the vendor type (a union in the hip_fp16 header file) that has
665 // no uint16 constructor, and that will trigger a compile error. There "h.x = x" assigns the
666 // raw bits, since the vendor member x is an integer.
667 // In the host compile phase (and in non-GPU builds) Eigen's own __half_raw is in effect and its
668 // member x may be a native fp16 type (__fp16 on arm64, _Float16 with AVX512FP16 or riscv-zfh),
669 // so "h.x = x" would perform a numeric integer-to-float conversion that corrupts the raw bits;
670 // the explicit uint16 constructor reinterprets the bits for every storage type instead.
671 // Fortunately, since we need to disable EIGEN_CONSTEXPR for GPU anyway, we can get out
672 // of this catch22 by having separate bodies for the GPU device phase / everything else.
673#if defined(EIGEN_GPUCC) && defined(EIGEN_GPU_COMPILE_PHASE)
674 __half_raw h;
675 h.x = x;
676 return h;
677#else
678 return __half_raw(x);
679#endif
680}
681
682EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC numext::uint16_t raw_half_as_uint16(const __half_raw& h) {
683 // HIP/CUDA/Default have a member 'x' of type uint16_t.
684 // For ARM64 native half, the member 'x' is of type __fp16, so we need to bit-cast.
685 // For SYCL, cl::sycl::half is _Float16, so cast directly.
686#if EIGEN_HAS_ARM64_FP16
687 return numext::bit_cast<numext::uint16_t>(h.x);
688#elif defined(EIGEN_HAS_BUILTIN_FLOAT16)
689 return numext::bit_cast<numext::uint16_t>(h.x);
690#elif defined(SYCL_DEVICE_ONLY)
691 return numext::bit_cast<numext::uint16_t>(h);
692#else
693 return h.x;
694#endif
695}
696
697EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC __half_raw float_to_half_rtne(float ff) {
698#if defined(EIGEN_GPU_COMPILE_PHASE)
699 __half tmp_ff = __float2half(ff);
700 return *(__half_raw*)&tmp_ff;
701
702#elif EIGEN_HAS_ARM64_FP16
703 __half_raw h;
704 h.x = static_cast<__fp16>(ff);
705 return h;
706
707#elif defined(EIGEN_HAS_BUILTIN_FLOAT16)
708 __half_raw h;
709 h.x = static_cast<_Float16>(ff);
710 return h;
711
712#elif defined(EIGEN_HAS_FP16_C)
713 __half_raw h;
714 // Spell out the vector conversion to avoid Clang warning about the C99 compound literal used by _cvtss_sh.
715 h.x = static_cast<numext::uint16_t>(_mm_extract_epi16(_mm_cvtps_ph(_mm_set_ss(ff), 0), 0));
716 return h;
717
718#else
719 uint32_t f_bits = Eigen::numext::bit_cast<uint32_t>(ff);
720 const uint32_t f32infty_bits = {255 << 23};
721 const uint32_t f16max_bits = {(127 + 16) << 23};
722 const uint32_t denorm_magic_bits = {((127 - 15) + (23 - 10) + 1) << 23};
723 const uint32_t sign_mask = 0x80000000u;
724 __half_raw o;
725 o.x = static_cast<uint16_t>(0x0u);
726
727 const uint32_t sign = f_bits & sign_mask;
728 f_bits ^= sign;
729
730 // NOTE all the integer compares in this function can be safely
731 // compiled into signed compares since all operands are below
732 // 0x80000000. Important if you want fast straight SSE2 code
733 // (since there's no unsigned PCMPGTD).
734
735 if (f_bits >= f16max_bits) { // result is Inf or NaN (all exponent bits set)
736 o.x = (f_bits > f32infty_bits) ? 0x7e00 : 0x7c00; // NaN->qNaN and Inf->Inf
737 } else { // (De)normalized number or zero
738 if (f_bits < (113 << 23)) { // resulting FP16 is subnormal or zero
739 // use a magic value to align our 10 mantissa bits at the bottom of
740 // the float. as long as FP addition is round-to-nearest-even this
741 // just works.
742 f_bits = Eigen::numext::bit_cast<uint32_t>(Eigen::numext::bit_cast<float>(f_bits) +
743 Eigen::numext::bit_cast<float>(denorm_magic_bits));
744
745 // and one integer subtract of the bias later, we have our final float!
746 o.x = static_cast<numext::uint16_t>(f_bits - denorm_magic_bits);
747 } else {
748 const uint32_t mant_odd = (f_bits >> 13) & 1; // resulting mantissa is odd
749
750 // update exponent, rounding bias part 1
751 // Equivalent to `f.u += ((unsigned int)(15 - 127) << 23) + 0xfff`, but
752 // without arithmetic overflow.
753 f_bits += 0xc8000fffU;
754 // rounding bias part 2
755 f_bits += mant_odd;
756 // take the bits!
757 o.x = static_cast<numext::uint16_t>(f_bits >> 13);
758 }
759 }
760
761 o.x |= static_cast<numext::uint16_t>(sign >> 16);
762 return o;
763#endif
764}
765
766EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC float half_to_float(__half_raw h) {
767#if defined(EIGEN_GPU_COMPILE_PHASE)
768 return __half2float(h);
769#elif EIGEN_HAS_ARM64_FP16 || defined(EIGEN_HAS_BUILTIN_FLOAT16)
770 return static_cast<float>(h.x);
771#elif defined(EIGEN_HAS_FP16_C)
772#if EIGEN_COMP_MSVC
773 // MSVC does not have scalar instructions.
774 return _mm_cvtss_f32(_mm_cvtph_ps(_mm_set1_epi16(h.x)));
775#else
776 return _cvtsh_ss(h.x);
777#endif
778#else
779 const float magic = Eigen::numext::bit_cast<float>(static_cast<uint32_t>(113 << 23));
780 const uint32_t shifted_exp = 0x7c00 << 13; // exponent mask after shift
781 uint32_t o_bits = (h.x & 0x7fff) << 13; // exponent/mantissa bits
782 const uint32_t exp = shifted_exp & o_bits; // just the exponent
783 o_bits += (127 - 15) << 23; // exponent adjust
784
785 // handle exponent special cases
786 if (exp == shifted_exp) { // Inf/NaN?
787 o_bits += (128 - 16) << 23; // extra exp adjust
788 } else if (exp == 0) { // Zero/Denormal?
789 o_bits += 1 << 23; // extra exp adjust
790 // renormalize
791 o_bits = Eigen::numext::bit_cast<uint32_t>(Eigen::numext::bit_cast<float>(o_bits) - magic);
792 }
793
794 o_bits |= (h.x & 0x8000u) << 16; // sign bit
795 return Eigen::numext::bit_cast<float>(o_bits);
796#endif
797}
798
799// --- standard functions ---
800
801EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC bool(isinf)(const half& a) { return (raw_half_as_uint16(a) & 0x7fff) == 0x7c00; }
802EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC bool(isnan)(const half& a) {
803#if defined(EIGEN_GPU_COMPILE_PHASE)
804 return __hisnan(a);
805#else
806 return (raw_half_as_uint16(a) & 0x7fff) > 0x7c00;
807#endif
808}
809EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC bool(isfinite)(const half& a) {
810 return (raw_half_as_uint16(a) & 0x7fff) < 0x7c00;
811}
812
813EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half abs(const half& a) {
814#if defined(EIGEN_HAS_ARM64_FP16_SCALAR_ARITHMETIC)
815 return half_from_rep(vabsh_f16(a.x));
816#else
817 return raw_uint16_to_half(static_cast<numext::uint16_t>(raw_half_as_uint16(a) & 0x7FFF));
818#endif
819}
820EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half exp(const half& a) {
821#if defined(EIGEN_CUDA_ARCH) || defined(EIGEN_HIP_DEVICE_COMPILE)
822 return half(hexp(::__half(a)));
823#else
824 return half(::expf(float(a)));
825#endif
826}
827EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half exp2(const half& a) {
828#if defined(EIGEN_CUDA_ARCH) || defined(EIGEN_HIP_DEVICE_COMPILE)
829 return half(hexp2(::__half(a)));
830#else
831 return half(::exp2f(float(a)));
832#endif
833}
834EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half expm1(const half& a) { return half(numext::expm1(float(a))); }
835// float covers the half range, so the single conversion back rounds correctly.
836EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half ldexp(const half& a, int exponent) {
837 return half(numext::ldexp(float(a), exponent));
838}
839EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half log(const half& a) {
840#if defined(EIGEN_GPU_COMPILE_PHASE)
841 return half(hlog(::__half(a)));
842#else
843 return half(::logf(float(a)));
844#endif
845}
846EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half log1p(const half& a) { return half(numext::log1p(float(a))); }
847EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half log10(const half& a) { return half(::log10f(float(a))); }
848EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half log2(const half& a) {
849 return half(static_cast<float>(EIGEN_LOG2E) * ::logf(float(a)));
850}
851
852EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half sqrt(const half& a) {
853#if defined(EIGEN_CUDA_ARCH) || defined(EIGEN_HIP_DEVICE_COMPILE)
854 return half(hsqrt(::__half(a)));
855#else
856 return half(::sqrtf(float(a)));
857#endif
858}
859EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half pow(const half& a, const half& b) {
860 return half(::powf(float(a), float(b)));
861}
862EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half atan2(const half& a, const half& b) {
863 return half(::atan2f(float(a), float(b)));
864}
865EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half sin(const half& a) { return half(::sinf(float(a))); }
866EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half cos(const half& a) { return half(::cosf(float(a))); }
867EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half tan(const half& a) { return half(::tanf(float(a))); }
868EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half tanh(const half& a) { return half(::tanhf(float(a))); }
869EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half asin(const half& a) { return half(::asinf(float(a))); }
870EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half acos(const half& a) { return half(::acosf(float(a))); }
871EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half atan(const half& a) { return half(::atanf(float(a))); }
872EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half atanh(const half& a) { return half(::atanhf(float(a))); }
873EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half floor(const half& a) {
874#if (defined(EIGEN_CUDA_ARCH)) || defined(EIGEN_HIP_DEVICE_COMPILE)
875 return half(hfloor(::__half(a)));
876#else
877 return half(::floorf(float(a)));
878#endif
879}
880EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half ceil(const half& a) {
881#if (defined(EIGEN_CUDA_ARCH)) || defined(EIGEN_HIP_DEVICE_COMPILE)
882 return half(hceil(::__half(a)));
883#else
884 return half(::ceilf(float(a)));
885#endif
886}
887EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half rint(const half& a) { return half(::rintf(float(a))); }
888EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half round(const half& a) { return half(::roundf(float(a))); }
889EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half trunc(const half& a) { return half(::truncf(float(a))); }
890EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half fmod(const half& a, const half& b) {
891 return half(::fmodf(float(a), float(b)));
892}
893
894EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half(min)(const half& a, const half& b) { return b < a ? b : a; }
895
896EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half(max)(const half& a, const half& b) { return a < b ? b : a; }
897
898EIGEN_DEVICE_FUNC inline half fma(const half& a, const half& b, const half& c) {
899#if defined(EIGEN_HAS_ARM64_FP16_SCALAR_ARITHMETIC)
900 return half_from_rep(vfmah_f16(c.x, a.x, b.x));
901#elif defined(EIGEN_VECTORIZE_AVX512FP16)
902 // Reduces to vfmadd213sh.
903 return half(_mm_cvtsh_h(_mm_fmadd_ph(_mm_set_sh(a.x), _mm_set_sh(b.x), _mm_set_sh(c.x))));
904#else
905 // Emulate FMA via float.
906 return half(numext::fma(static_cast<float>(a), static_cast<float>(b), static_cast<float>(c)));
907#endif
908}
909
910#ifndef EIGEN_NO_IO
911EIGEN_ALWAYS_INLINE std::ostream& operator<<(std::ostream& os, const half& v) {
912 os << static_cast<float>(v);
913 return os;
914}
915#endif
916
917} // end namespace half_impl
918
919// import Eigen::half_impl::half into Eigen namespace
920// using half_impl::half;
921
922namespace internal {
923
924template <>
925struct is_arithmetic<half> : std::true_type {};
926
927template <>
928struct random_impl<half> {
929 enum : int { MantissaBits = 10 };
930 using Impl = random_impl<float>;
931 static EIGEN_DEVICE_FUNC inline half run(const half& x, const half& y) {
932 float result = Impl::run(x, y, MantissaBits);
933 return half(result);
934 }
935 static EIGEN_DEVICE_FUNC inline half run() {
936 float result = Impl::run(MantissaBits);
937 return half(result);
938 }
939};
940
941} // end namespace internal
942
943template <>
944struct NumTraits<Eigen::half> : GenericNumTraits<Eigen::half> {
945 enum { IsSigned = true, IsInteger = false, IsComplex = false, RequireInitialization = false };
946
947 EIGEN_DEVICE_FUNC _EIGEN_MAYBE_CONSTEXPR static EIGEN_STRONG_INLINE Eigen::half epsilon() {
948 // 0x1400 is 2^-10, the fp16 machine epsilon, matching std::numeric_limits<Eigen::half>::epsilon().
949 return half_impl::raw_uint16_to_half(0x1400);
950 }
951 EIGEN_DEVICE_FUNC _EIGEN_MAYBE_CONSTEXPR static EIGEN_STRONG_INLINE Eigen::half dummy_precision() {
952 return half_impl::raw_uint16_to_half(0x211f); // Eigen::half(1e-2f);
953 }
954 EIGEN_DEVICE_FUNC _EIGEN_MAYBE_CONSTEXPR static EIGEN_STRONG_INLINE Eigen::half highest() {
955 return half_impl::raw_uint16_to_half(0x7bff);
956 }
957 EIGEN_DEVICE_FUNC _EIGEN_MAYBE_CONSTEXPR static EIGEN_STRONG_INLINE Eigen::half lowest() {
958 return half_impl::raw_uint16_to_half(0xfbff);
959 }
960 EIGEN_DEVICE_FUNC _EIGEN_MAYBE_CONSTEXPR static EIGEN_STRONG_INLINE Eigen::half infinity() {
961 return half_impl::raw_uint16_to_half(0x7c00);
962 }
963 EIGEN_DEVICE_FUNC _EIGEN_MAYBE_CONSTEXPR static EIGEN_STRONG_INLINE Eigen::half quiet_NaN() {
964 return half_impl::raw_uint16_to_half(0x7e00);
965 }
966};
967
968} // end namespace Eigen
969
970#undef _EIGEN_MAYBE_CONSTEXPR
971
972namespace Eigen {
973namespace numext {
974
975#if defined(EIGEN_GPU_COMPILE_PHASE)
976
977template <>
978EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE bool(isnan)(const Eigen::half& h) {
979 return (half_impl::isnan)(h);
980}
981
982template <>
983EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE bool(isinf)(const Eigen::half& h) {
984 return (half_impl::isinf)(h);
985}
986
987template <>
988EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE bool(isfinite)(const Eigen::half& h) {
989 return (half_impl::isfinite)(h);
990}
991
992#endif
993
994template <>
995EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC Eigen::half bit_cast<Eigen::half, uint16_t>(const uint16_t& src) {
996 return Eigen::half(Eigen::half_impl::raw_uint16_to_half(src));
997}
998
999template <>
1000EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC uint16_t bit_cast<uint16_t, Eigen::half>(const Eigen::half& src) {
1001 return Eigen::half_impl::raw_half_as_uint16(src);
1002}
1003
1004EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC Eigen::half nextafter(const Eigen::half& from, const Eigen::half& to) {
1005 if (numext::isnan EIGEN_NOT_A_MACRO(from)) {
1006 return from;
1007 }
1008 if (numext::isnan EIGEN_NOT_A_MACRO(to)) {
1009 return to;
1010 }
1011 if (from == to) {
1012 return to;
1013 }
1014 uint16_t from_bits = numext::bit_cast<uint16_t>(from);
1015 bool from_sign = from_bits >> 15;
1016 if ((from_bits & 0x7fff) == 0) {
1017 // From ±0 toward a nonzero value: the neighbor is the smallest subnormal
1018 // carrying the sign of the direction (IEEE-754 nextUp/nextDown of zero).
1019 from_bits = (to > from) ? uint16_t(0x0001) : uint16_t(0x8001);
1020 } else if ((to > from) != from_sign) {
1021 // Toward the infinity with the same sign as from: increase the magnitude.
1022 ++from_bits;
1023 } else {
1024 --from_bits;
1025 }
1026 return numext::bit_cast<Eigen::half>(from_bits);
1027}
1028
1029// Specialize multiply-add to match packet operations and reduce conversions to/from float.
1030template <>
1031EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC Eigen::half madd<Eigen::half>(const Eigen::half& x, const Eigen::half& y,
1032 const Eigen::half& z) {
1033 return Eigen::half(static_cast<float>(x) * static_cast<float>(y) + static_cast<float>(z));
1034}
1035
1036} // namespace numext
1037} // namespace Eigen
1038
1039// Warp shuffle overloads for Eigen::half.
1040// CUDA uses __shfl_*_sync (with mask); HIP uses __shfl_* (no mask).
1041// Note that the following are __device__ - only functions.
1042#if defined(EIGEN_CUDACC) || defined(EIGEN_HIPCC)
1043
1044#if defined(EIGEN_CUDACC)
1045
1046__device__ EIGEN_STRONG_INLINE Eigen::half __shfl_sync(unsigned mask, Eigen::half var, int srcLane,
1047 int width = warpSize) {
1048 const __half h = var;
1049 return static_cast<Eigen::half>(__shfl_sync(mask, h, srcLane, width));
1050}
1051
1052__device__ EIGEN_STRONG_INLINE Eigen::half __shfl_up_sync(unsigned mask, Eigen::half var, unsigned int delta,
1053 int width = warpSize) {
1054 const __half h = var;
1055 return static_cast<Eigen::half>(__shfl_up_sync(mask, h, delta, width));
1056}
1057
1058__device__ EIGEN_STRONG_INLINE Eigen::half __shfl_down_sync(unsigned mask, Eigen::half var, unsigned int delta,
1059 int width = warpSize) {
1060 const __half h = var;
1061 return static_cast<Eigen::half>(__shfl_down_sync(mask, h, delta, width));
1062}
1063
1064__device__ EIGEN_STRONG_INLINE Eigen::half __shfl_xor_sync(unsigned mask, Eigen::half var, int laneMask,
1065 int width = warpSize) {
1066 const __half h = var;
1067 return static_cast<Eigen::half>(__shfl_xor_sync(mask, h, laneMask, width));
1068}
1069
1070#else // HIP
1071
1072__device__ EIGEN_STRONG_INLINE Eigen::half __shfl(Eigen::half var, int srcLane, int width = warpSize) {
1073 const int ivar = static_cast<int>(Eigen::numext::bit_cast<Eigen::numext::uint16_t>(var));
1074 return Eigen::numext::bit_cast<Eigen::half>(static_cast<Eigen::numext::uint16_t>(__shfl(ivar, srcLane, width)));
1075}
1076
1077__device__ EIGEN_STRONG_INLINE Eigen::half __shfl_up(Eigen::half var, unsigned int delta, int width = warpSize) {
1078 const int ivar = static_cast<int>(Eigen::numext::bit_cast<Eigen::numext::uint16_t>(var));
1079 return Eigen::numext::bit_cast<Eigen::half>(static_cast<Eigen::numext::uint16_t>(__shfl_up(ivar, delta, width)));
1080}
1081
1082__device__ EIGEN_STRONG_INLINE Eigen::half __shfl_down(Eigen::half var, unsigned int delta, int width = warpSize) {
1083 const int ivar = static_cast<int>(Eigen::numext::bit_cast<Eigen::numext::uint16_t>(var));
1084 return Eigen::numext::bit_cast<Eigen::half>(static_cast<Eigen::numext::uint16_t>(__shfl_down(ivar, delta, width)));
1085}
1086
1087__device__ EIGEN_STRONG_INLINE Eigen::half __shfl_xor(Eigen::half var, int laneMask, int width = warpSize) {
1088 const int ivar = static_cast<int>(Eigen::numext::bit_cast<Eigen::numext::uint16_t>(var));
1089 return Eigen::numext::bit_cast<Eigen::half>(static_cast<Eigen::numext::uint16_t>(__shfl_xor(ivar, laneMask, width)));
1090}
1091
1092#endif // HIP vs CUDA
1093#endif // __shfl*
1094
1095// ldg() has an overload for __half_raw, but we also need one for Eigen::half.
1096#if defined(EIGEN_CUDACC) || defined(EIGEN_HIPCC)
1097EIGEN_STRONG_INLINE __device__ Eigen::half __ldg(const Eigen::half* ptr) {
1098 return Eigen::half_impl::raw_uint16_to_half(__ldg(reinterpret_cast<const Eigen::numext::uint16_t*>(ptr)));
1099}
1100#endif // __ldg
1101
1102#if EIGEN_HAS_STD_HASH
1103namespace std {
1104template <>
1105struct hash<Eigen::half> {
1106 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE std::size_t operator()(const Eigen::half& a) const {
1107 return static_cast<std::size_t>(Eigen::numext::bit_cast<Eigen::numext::uint16_t>(a));
1108 }
1109};
1110} // end namespace std
1111#endif
1112
1113namespace Eigen {
1114namespace internal {
1115
1116template <>
1117struct cast_impl<float, half> {
1118 EIGEN_DEVICE_FUNC static inline half run(const float& a) {
1119#if defined(EIGEN_GPU_COMPILE_PHASE)
1120 return __float2half(a);
1121#else
1122 return half(a);
1123#endif
1124 }
1125};
1126
1127template <>
1128struct cast_impl<int, half> {
1129 EIGEN_DEVICE_FUNC static inline half run(const int& a) {
1130#if defined(EIGEN_GPU_COMPILE_PHASE)
1131 return __float2half(static_cast<float>(a));
1132#else
1133 return half(static_cast<float>(a));
1134#endif
1135 }
1136};
1137
1138template <>
1139struct cast_impl<half, float> {
1140 EIGEN_DEVICE_FUNC static inline float run(const half& a) {
1141#if defined(EIGEN_GPU_COMPILE_PHASE)
1142 return __half2float(a);
1143#else
1144 return static_cast<float>(a);
1145#endif
1146 }
1147};
1148
1149} // namespace internal
1150} // namespace Eigen
1151
1152#endif // EIGEN_HALF_H
Holds information about the various numeric (i.e. scalar) types allowed by Eigen.
Definition NumTraits.h:233