HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
float16.h
Go to the documentation of this file.
1 // Copyright (c) Microsoft Corporation. All rights reserved.
2 // Licensed under the MIT License.
3 #pragma once
4 
5 #include <math.h>
6 
7 #include "endian.h"
8 #if defined(CUDA_VERSION) && CUDA_VERSION >= 11000
9 #include "cuda_bf16.h"
10 #endif
11 
12 #if !defined(__CUDACC__) && !defined(__HIPCC__)
13 #include "core/common/narrow.h"
14 #endif
15 
16 #include "core/common/common.h"
17 
19 
20 namespace onnxruntime {
21 
22 #if defined(__CUDACC__) || defined(__HIPCC__)
23 #define ORT_HOST_DEVICE __host__ __device__
24 #else
25 #define ORT_HOST_DEVICE
26 #endif
27 
28 // MLFloat16
30  private:
31  explicit constexpr MLFloat16(uint16_t x) noexcept { val = x; }
32 
33  public:
35 
36  MLFloat16() = default;
37 
38  constexpr static MLFloat16 FromBits(uint16_t x) noexcept { return MLFloat16(x); }
39 
40  // Using inherited implementation instead of math floatToHalf allows us to use this
41  // in other shared providers without having to implement the bridge
42  explicit MLFloat16(float v) noexcept { val = Base::ToUint16Impl(v); }
43 
44  static const MLFloat16 NaN;
45  static const MLFloat16 NegativeNaN;
46  static const MLFloat16 Infinity;
48  static const MLFloat16 MaxValue;
49  static const MLFloat16 Zero;
50  static const MLFloat16 One;
51  static const MLFloat16 MinusOne;
52 
53  // Using inherited implementation instead of math halfToFloat allows us to use this
54  // in other shared providers without having to implement the bridge
55  float ToFloat() const noexcept { return Base::ToFloatImpl(); }
56 
57  using Base::IsNegative;
58 
59  using Base::IsNaN;
60 
61  using Base::IsFinite;
62 
64 
66 
67  using Base::IsInfinity;
68 
69  using Base::IsNaNOrZero;
70 
71  using Base::IsNormal;
72 
73  using Base::IsSubnormal;
74 
75  using Base::Abs;
76 
77  using Base::Negate;
78 
79  operator float() const noexcept { return ToFloat(); }
80 
81  using Base::operator==;
82  using Base::operator!=;
83  using Base::operator<;
84 };
85 
86 // BFloat16
89 
90 #if defined(__HIP__)
91  ORT_HOST_DEVICE BFloat16() = default;
92 #else
93  BFloat16() = default;
94 #endif
95 
96  struct FromBitsT {};
97  static constexpr ORT_HOST_DEVICE FromBitsT FromBits() noexcept { return FromBitsT(); }
98  constexpr ORT_HOST_DEVICE BFloat16(unsigned short bits, FromBitsT) noexcept { val = bits; }
99 
100  static constexpr ORT_HOST_DEVICE BFloat16 FromBits(uint16_t bits) noexcept {
101  return BFloat16(bits, FromBits());
102  }
103 
104  inline ORT_HOST_DEVICE BFloat16(float v) noexcept {
105 #if defined(CUDA_VERSION) && CUDA_VERSION >= 11000 && defined(__CUDA_ARCH__) && __CUDA_ARCH__ >= 800
106  val = __bfloat16_as_ushort(__float2bfloat16(v));
107 #elif defined(__HIP__)
108  // We should be using memcpy in order to respect the strict aliasing rule but it fails in the HIP environment.
109  if (v != v) { // isnan
110  val = UINT16_C(0x7FC0);
111  } else {
112  union {
113  uint32_t U32;
114  float F32;
115  };
116 
117  F32 = v;
118  uint32_t rounding_bias = ((U32 >> 16) & 1) + UINT32_C(0x7FFF);
119  val = static_cast<uint16_t>((U32 + rounding_bias) >> 16);
120  }
121 #else
122 
123  // Use C isnan to work both in host and device
124  if (::isnan(v)) {
126  } else {
127  auto get_msb_half = [](float fl) {
128  uint16_t result;
129  if constexpr (onnxruntime_float16::detail::endian::native == onnxruntime_float16::detail::endian::little) {
130  std::memcpy(&result, reinterpret_cast<char*>(&fl) + sizeof(uint16_t), sizeof(uint16_t));
131  } else {
132  std::memcpy(&result, &fl, sizeof(uint16_t));
133  }
134  return result;
135  };
136 
137  uint16_t upper_bits = get_msb_half(v);
138  union {
139  uint32_t U32;
140  float F32;
141  };
142  F32 = v;
143  U32 += (upper_bits & 1) + kRoundToNearest;
144  val = get_msb_half(F32);
145  }
146 #endif
147  }
148 
149  inline ORT_HOST_DEVICE float ToFloat() const noexcept {
150 #if defined(CUDA_VERSION) && CUDA_VERSION >= 11000
151  return __bfloat162float(*reinterpret_cast<const __nv_bfloat16*>(&val));
152 #elif defined(__HIP__)
153  // We should be using memcpy in order to respect the strict aliasing rule but it fails in the HIP environment.
154  float result = 0;
155  uint32_t tmp = val;
156  tmp <<= 16;
157  float* tempRes = reinterpret_cast<float*>(&tmp);
158  result = *tempRes;
159  return result;
160 #else
161 
162  if (IsNaNHostDevice()) {
163  return std::numeric_limits<float>::quiet_NaN();
164  }
165 
166  float result = 0;
167  char* const first = reinterpret_cast<char*>(&result);
168  if constexpr (endian::native == endian::little) {
169  char* const second = first + sizeof(uint16_t);
170  std::memcpy(second, &val, sizeof(uint16_t));
171  } else {
172  std::memcpy(first, &val, sizeof(uint16_t));
173  }
174  return result;
175 #endif
176  }
177 
178  static const BFloat16 NaN;
179  static const BFloat16 NegativeNaN;
180  static const BFloat16 Infinity;
182  static const BFloat16 MaxValue;
183  static const BFloat16 Zero;
184  static const BFloat16 One;
185  static const BFloat16 MinusOne;
186 
187  using Base::IsNegative;
188 
189  using Base::IsNaN;
190 
191  using Base::IsFinite;
192 
194 
196 
197  using Base::IsInfinity;
198 
199  using Base::IsNaNOrZero;
200 
201  using Base::IsNormal;
202 
203  using Base::IsSubnormal;
204 
205  using Base::Abs;
206 
207  using Base::Negate;
208 
209  ORT_HOST_DEVICE operator float() const noexcept { return ToFloat(); }
210 
211 #if defined(CUDA_VERSION) && CUDA_VERSION >= 11000
212  ORT_HOST_DEVICE BFloat16(const __nv_bfloat16& value) { val = *reinterpret_cast<const unsigned short*>(&value); }
213  explicit ORT_HOST_DEVICE operator __nv_bfloat16() const { return *reinterpret_cast<const __nv_bfloat16*>(&val); }
214 #endif
215 
216  ORT_HOST_DEVICE bool operator==(const BFloat16& rhs) const noexcept {
217  if (IsNaNHostDevice() || rhs.IsNaNHostDevice()) {
218  // IEEE defines that NaN is not equal to anything, including itself.
219  return false;
220  }
221  return val == rhs.val;
222  }
223 
224  ORT_HOST_DEVICE bool operator!=(const BFloat16& rhs) const noexcept {
225  return !(*this == rhs);
226  }
227 
228  ORT_HOST_DEVICE bool operator<(const BFloat16& rhs) const noexcept {
229  if (IsNaNHostDevice() || rhs.IsNaNHostDevice()) {
230  // IEEE defines that NaN is unordered with respect to everything, including itself.
231  return false;
232  }
233 
234  const bool left_is_negative = IsNegativeHostDevice();
235  if (left_is_negative != rhs.IsNegativeHostDevice()) {
236  // When the signs of left and right differ, we know that left is less than right if it is
237  // the negative value. The exception to this is if both values are zero, in which case IEEE
238  // says they should be equal, even if the signs differ.
239  return left_is_negative && !AreZeroHostDevice(*this, rhs);
240  }
241  return (val != rhs.val) && ((val < rhs.val) ^ left_is_negative);
242  }
243 
244  ORT_HOST_DEVICE bool IsNegativeHostDevice() const noexcept {
245  return (val & kSignMask) != 0;
246  }
247 
248  ORT_HOST_DEVICE bool IsNaNHostDevice() const noexcept {
249  return static_cast<uint16_t>(val & ~kSignMask) > kPositiveInfinityBits;
250  }
251 
252  ORT_HOST_DEVICE static bool AreZeroHostDevice(const BFloat16Impl& lhs, const BFloat16Impl& rhs) noexcept {
253  // IEEE defines that positive and negative zero are equal, this gives us a quick equality check
254  // for two values by or'ing the private bits together and stripping the sign. They are both zero,
255  // and therefore equivalent, if the resulting value is still zero.
256  return static_cast<uint16_t>((lhs.val | rhs.val) & ~kSignMask) == 0;
257  }
258 };
259 
260 // User defined suffixes to make it easier to declare
261 // initializers with MLFloat16 and BFloat16 from unsigned short
262 // E.g 10_f16 or 10_b16
263 #if !defined(__CUDACC__) && !defined(__HIPCC__)
264 inline MLFloat16 operator"" _f16(unsigned long long int v) noexcept {
265  return MLFloat16::FromBits(narrow<uint16_t>(v));
266 }
267 
268 inline MLFloat16 operator"" _fp16(long double v) noexcept {
269  return MLFloat16(static_cast<float>(v));
270 }
271 
272 inline BFloat16 operator"" _b16(unsigned long long int v) noexcept {
273  return BFloat16::FromBits((narrow<uint16_t>(v)));
274 }
275 
276 inline BFloat16 operator"" _bfp16(long double v) noexcept {
277  return BFloat16(static_cast<float>(v));
278 }
279 #endif
280 
281 inline void BFloat16ToFloat(const BFloat16* blf, float* flt, size_t size) noexcept {
282  auto src = blf;
283  auto d = flt;
284  for (; size != 0; ++src, ++d, --size) {
285  *d = src->ToFloat();
286  }
287 }
288 
289 inline void FloatToBFloat16(const float* flt, BFloat16* blf, size_t size) {
290  auto src = flt;
291  auto d = blf;
292  for (; size != 0; ++src, ++d, --size) {
293  *d = BFloat16(*src);
294  }
295 }
296 
297 } // namespace onnxruntime
298 
299 namespace std {
300 
301 template <>
302 class numeric_limits<onnxruntime::MLFloat16> {
303  public:
304  static constexpr onnxruntime::MLFloat16 min() noexcept {
305  return onnxruntime::MLFloat16::FromBits(0x0400U); // Minimum positive normalized value: 0.00006103515625
306  }
307 
308  static constexpr onnxruntime::MLFloat16 max() noexcept {
309  return onnxruntime::MLFloat16::FromBits(0x7BFFU); // Largest representable value: 65504
310  }
311 
312  static constexpr onnxruntime::MLFloat16 lowest() noexcept {
313  return onnxruntime::MLFloat16::FromBits(0xFBFFU); // Smallest representable value: -65504
314  }
315 
316  static constexpr onnxruntime::MLFloat16 infinity() noexcept {
317  return onnxruntime::MLFloat16::FromBits(0x7C00U); // Bits: sign(0), exponent(111,11), fraction(00,0000,0000)
318  }
319 
320  static constexpr onnxruntime::MLFloat16 quiet_NaN() noexcept {
321  // The most significant fraction bit shall be 1, and no limitation on other fraction bits.
322  // Note that most frameworks use 0x7E00; while CUDA uses 0x7FFF; .Net System.Half.NaN uses 0xFE00;
323  return onnxruntime::MLFloat16::FromBits(0x7E00U); // Bits: sign(0), exponent(111,11), fraction(10,0000,0000)
324  }
325 
326  static constexpr onnxruntime::MLFloat16 signaling_NaN() noexcept {
327  return onnxruntime::MLFloat16::FromBits(0x7D00U); // Bits: sign(0), exponent(111,11), fraction(01,0000,0000)
328  }
329 
330  static constexpr onnxruntime::MLFloat16 denorm_min() noexcept {
331  return onnxruntime::MLFloat16::FromBits(0x0001U); // Minimum subnormal value: 0.000000059604645
332  }
333 
334  static constexpr onnxruntime::MLFloat16 epsilon() noexcept {
335  return onnxruntime::MLFloat16::FromBits(0x1400U); // Difference between 1.0 and the next value: 2^-10 = 0.0009765625
336  }
337 
338  static constexpr onnxruntime::MLFloat16 round_error() noexcept {
339  return onnxruntime::MLFloat16::FromBits(0x3800U); // 0.5
340  }
341 
342  static constexpr bool is_specialized = true;
343 
344  static constexpr bool is_signed = true;
345  static constexpr bool is_integer = false;
346  static constexpr bool is_exact = false;
347  static constexpr bool has_infinity = true;
348  static constexpr bool has_quiet_NaN = true;
349  static constexpr bool has_signaling_NaN = true;
350  static constexpr float_denorm_style has_denorm = denorm_present;
351  static constexpr bool has_denorm_loss = false;
352 
353  static constexpr bool is_bounded = true;
354  static constexpr bool is_iec559 = true;
355  static constexpr bool is_modulo = false;
356 
357  static constexpr int digits = 11; // Number of significant digits (mantissa)
358  static constexpr int digits10 = 3; // Decimal digits of precision
359  static constexpr int max_digits10 = 5; // Max decimal digits required for precision
360  static constexpr int radix = 2;
361  static constexpr int min_exponent = -13;
362  static constexpr int min_exponent10 = -4;
363  static constexpr int max_exponent = 16;
364  static constexpr int max_exponent10 = 4;
365 
366  static constexpr bool traps = false;
367  static constexpr bool tinyness_before = false;
368  static constexpr std::float_round_style round_style = std::round_to_nearest;
369 };
370 
371 template <>
372 class numeric_limits<onnxruntime::BFloat16> {
373  public:
374  static constexpr onnxruntime::BFloat16 min() noexcept {
375  return onnxruntime::BFloat16::FromBits(0x0080U); // Minimum positive normalized value: 1.175494e-38
376  }
377 
378  static constexpr onnxruntime::BFloat16 max() noexcept {
379  return onnxruntime::BFloat16::FromBits(0x7F7FU); // Largest representable value: 3.38953139e38
380  }
381 
382  static constexpr onnxruntime::BFloat16 lowest() noexcept {
383  return onnxruntime::BFloat16::FromBits(0xFF7FU); // Smallest representable value: -3.38953139e38
384  }
385 
386  static constexpr onnxruntime::BFloat16 infinity() noexcept {
387  return onnxruntime::BFloat16::FromBits(0x7F80U); // Bits: sign(0), exponent(111,1111,1), fraction(000,0000)
388  }
389 
390  static constexpr onnxruntime::BFloat16 quiet_NaN() noexcept {
391  // The most significant fraction bit shall be 1, and no limitation on other fraction bits.
392  // Note that Torch, Tensorflow, OpenVino, nGraph uses 0x7FC0; Paddle uses 0x7FC1; CUDA uses 0x7FFF.
393  return onnxruntime::BFloat16::FromBits(0x7FC1U); // Bits: sign(0), exponent(111,1111,1), fraction(100,0001)
394  }
395 
396  static constexpr onnxruntime::BFloat16 signaling_NaN() noexcept {
397  // The most significant fraction bit shall be 0, and there is at least one 1 in other fraction bits.
398  return onnxruntime::BFloat16::FromBits(0x7F81U); // Bits: sign(0), exponent(111,1111,1), fraction(000,0001)
399  }
400 
401  static constexpr onnxruntime::BFloat16 denorm_min() noexcept {
402  return onnxruntime::BFloat16::FromBits(0x0001U); // Minimum subnormal value: 9.1835e-41
403  }
404 
405  static constexpr onnxruntime::BFloat16 epsilon() noexcept {
406  return onnxruntime::BFloat16::FromBits(0x3C00U); // Difference between 1.0 and the next value: 2^-7 = 0.0078125
407  }
408 
409  static constexpr onnxruntime::BFloat16 round_error() noexcept {
410  return onnxruntime::BFloat16::FromBits(0x3F00U); // 0.5
411  }
412 
413  static constexpr bool is_specialized = true;
414  static constexpr bool is_signed = true;
415  static constexpr bool is_integer = false;
416  static constexpr bool is_exact = false;
417  static constexpr bool has_infinity = true;
418  static constexpr bool has_quiet_NaN = true;
419  static constexpr bool has_signaling_NaN = true;
420  static constexpr float_denorm_style has_denorm = denorm_present;
421  static constexpr bool has_denorm_loss = false;
422 
423  static constexpr bool is_bounded = true;
424  static constexpr bool is_iec559 = false;
425  static constexpr bool is_modulo = false;
426 
427  static constexpr int digits = 8;
428  static constexpr int digits10 = 2;
429  static constexpr int max_digits10 = 4;
430  static constexpr int radix = 2;
431  static constexpr int min_exponent = -125;
432  static constexpr int min_exponent10 = -37;
433  static constexpr int max_exponent = 128;
434  static constexpr int max_exponent10 = 38;
435 
436  static constexpr bool traps = false;
437  static constexpr bool tinyness_before = false;
438  static constexpr float_round_style round_style = round_to_nearest;
439 };
440 
441 } // namespace std
GLint first
Definition: glcorearb.h:405
bool IsNaN() const noexcept
Tests if the value is NaN
static constexpr onnxruntime::MLFloat16 infinity() noexcept
Definition: float16.h:316
MLFloat16 Abs() const noexcept
Creates an instance that represents absolute value.
ORT_HOST_DEVICE bool operator<(const BFloat16 &rhs) const noexcept
Definition: float16.h:228
bool_constant< is_integral< T >::value &&!std::is_same< T, bool >::value &&!std::is_same< T, char >::value &&!std::is_same< T, wchar_t >::value > is_integer
Definition: format.h:824
BFloat16 Negate() const noexcept
Creates a new instance with the sign flipped.
static const MLFloat16 NegativeInfinity
Definition: float16.h:47
const GLdouble * v
Definition: glcorearb.h:837
MLFloat16(float v) noexcept
Definition: float16.h:42
bool IsSubnormal() const noexcept
Tests if the value is subnormal (denormal).
GLsizei const GLfloat * value
Definition: glcorearb.h:824
static constexpr onnxruntime::MLFloat16 denorm_min() noexcept
Definition: float16.h:330
void BFloat16ToFloat(const BFloat16 *blf, float *flt, size_t size) noexcept
Definition: float16.h:281
static const BFloat16 MinusOne
Definition: float16.h:185
static constexpr onnxruntime::BFloat16 max() noexcept
Definition: float16.h:378
static const BFloat16 NaN
Definition: float16.h:178
**But if you need a result
Definition: thread.h:622
void FloatToBFloat16(const float *flt, BFloat16 *blf, size_t size)
Definition: float16.h:289
bool IsNegative() const noexcept
Checks if the value is negative
static constexpr MLFloat16 FromBits(uint16_t x) noexcept
Definition: float16.h:38
ORT_HOST_DEVICE BFloat16(float v) noexcept
Definition: float16.h:104
ORT_HOST_DEVICE bool IsNegativeHostDevice() const noexcept
Definition: float16.h:244
static constexpr uint16_t ToUint16Impl(float v) noexcept
Converts from float to uint16_t float16 representation
static constexpr onnxruntime::BFloat16 quiet_NaN() noexcept
Definition: float16.h:390
bool IsFinite() const noexcept
Tests if the value is finite
ORT_HOST_DEVICE bool operator!=(const BFloat16 &rhs) const noexcept
Definition: float16.h:224
static constexpr onnxruntime::BFloat16 min() noexcept
Definition: float16.h:374
static ORT_HOST_DEVICE bool AreZeroHostDevice(const BFloat16Impl &lhs, const BFloat16Impl &rhs) noexcept
Definition: float16.h:252
bool IsPositiveInfinity() const noexcept
Tests if the value represents positive infinity.
static constexpr ORT_HOST_DEVICE FromBitsT FromBits() noexcept
Definition: float16.h:97
bool IsNormal() const noexcept
Tests if the value is normal (not zero, subnormal, infinite, or NaN).
bool IsFinite() const noexcept
Tests if the value is finite
static constexpr onnxruntime::MLFloat16 lowest() noexcept
Definition: float16.h:312
Shared implementation between public and internal classes. CRTP pattern.
#define ORT_HOST_DEVICE
Definition: float16.h:25
bool IsNegative() const noexcept
Checks if the value is negative
ORT_HOST_DEVICE bool IsNaNHostDevice() const noexcept
Definition: float16.h:248
static constexpr onnxruntime::MLFloat16 quiet_NaN() noexcept
Definition: float16.h:320
ORT_HOST_DEVICE float ToFloat() const noexcept
Definition: float16.h:149
ORT_HOST_DEVICE bool operator==(const BFloat16 &rhs) const noexcept
Definition: float16.h:216
constexpr ORT_HOST_DEVICE BFloat16(unsigned short bits, FromBitsT) noexcept
Definition: float16.h:98
bool IsNegativeInfinity() const noexcept
Tests if the value represents negative infinity
float ToFloatImpl() const noexcept
Converts float16 to float
constexpr bool isnan(T value)
Definition: format.h:2797
static constexpr onnxruntime::MLFloat16 round_error() noexcept
Definition: float16.h:338
bool IsPositiveInfinity() const noexcept
Tests if the value represents positive infinity.
static constexpr onnxruntime::MLFloat16 min() noexcept
Definition: float16.h:304
static const BFloat16 Infinity
Definition: float16.h:180
MLFloat16 Negate() const noexcept
Creates a new instance with the sign flipped.
GLint GLenum GLint x
Definition: glcorearb.h:409
static const BFloat16 MaxValue
Definition: float16.h:182
static constexpr onnxruntime::BFloat16 denorm_min() noexcept
Definition: float16.h:401
static const BFloat16 NegativeNaN
Definition: float16.h:179
bool IsNaNOrZero() const noexcept
Tests if the value is NaN or zero. Useful for comparisons.
bool IsInfinity() const noexcept
Tests if the value is either positive or negative infinity.
static const MLFloat16 Infinity
Definition: float16.h:46
static const MLFloat16 NegativeNaN
Definition: float16.h:45
BFloat16 Abs() const noexcept
Creates an instance that represents absolute value.
bool IsSubnormal() const noexcept
Tests if the value is subnormal (denormal).
static constexpr onnxruntime::BFloat16 round_error() noexcept
Definition: float16.h:409
static constexpr onnxruntime::BFloat16 signaling_NaN() noexcept
Definition: float16.h:396
static constexpr onnxruntime::MLFloat16 epsilon() noexcept
Definition: float16.h:334
constexpr auto digits10() noexcept-> int
Definition: format.h:1289
GLsizeiptr size
Definition: glcorearb.h:664
static const MLFloat16 Zero
Definition: float16.h:49
std::integral_constant< bool, std::numeric_limits< T >::is_signed||std::is_same< T, int128_opt >::value > is_signed
Definition: format.h:818
IMATH_NAMESPACE::V2f IMATH_NAMESPACE::Box2i std::string this attribute is obsolete as of OpenEXR v3 float
bool IsNaN() const noexcept
Tests if the value is NaN
static const BFloat16 One
Definition: float16.h:184
static const MLFloat16 MinusOne
Definition: float16.h:51
static constexpr onnxruntime::BFloat16 infinity() noexcept
Definition: float16.h:386
static const BFloat16 NegativeInfinity
Definition: float16.h:181
GLuint GLfloat * val
Definition: glcorearb.h:1608
static constexpr onnxruntime::MLFloat16 signaling_NaN() noexcept
Definition: float16.h:326
static const MLFloat16 One
Definition: float16.h:50
static const MLFloat16 MaxValue
Definition: float16.h:48
bool IsNormal() const noexcept
Tests if the value is normal (not zero, subnormal, infinite, or NaN).
bool IsNaNOrZero() const noexcept
Tests if the value is NaN or zero. Useful for comparisons.
float ToFloat() const noexcept
Definition: float16.h:55
static constexpr ORT_HOST_DEVICE BFloat16 FromBits(uint16_t bits) noexcept
Definition: float16.h:100
static constexpr onnxruntime::BFloat16 epsilon() noexcept
Definition: float16.h:405
static const MLFloat16 NaN
Definition: float16.h:44
static constexpr onnxruntime::BFloat16 lowest() noexcept
Definition: float16.h:382
bool IsInfinity() const noexcept
Tests if the value is either positive or negative infinity.
Shared implementation between public and internal classes. CRTP pattern.
static constexpr onnxruntime::MLFloat16 max() noexcept
Definition: float16.h:308
bool IsNegativeInfinity() const noexcept
Tests if the value represents negative infinity
static const BFloat16 Zero
Definition: float16.h:183
GLenum src
Definition: glcorearb.h:1793