• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

stillwater-sc / universal / 24904143675

24 Apr 2026 05:56PM UTC coverage: 84.382% (+0.01%) from 84.371%
24904143675

Pull #751

github

web-flow
Merge e14987a9f into a943617cb
Pull Request #751: feat(bfloat16): full constexpr support across all operators

41 of 43 new or added lines in 1 file covered. (95.35%)

9 existing lines in 2 files now uncovered.

44945 of 53264 relevant lines covered (84.38%)

6454147.76 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.71
/include/sw/universal/number/bfloat16/bfloat16_impl.hpp
1
#pragma once
2
// bfloat16_impl.hpp: definition of the Google Brain Float number system
3
//
4
// Copyright (C) 2017 Stillwater Supercomputing, Inc.
5
// SPDX-License-Identifier: MIT
6
//
7
// This file is part of the universal numbers project, which is released under an MIT Open Source license.
8
#include <cstdint>
9
#include <string>
10
#include <sstream>
11
#include <iostream>
12
#include <iomanip>
13
#include <regex>
14

15
#include <universal/utility/bit_cast.hpp>
16
#include <universal/number/shared/specific_value_encoding.hpp>
17
#include <universal/number/shared/nan_encoding.hpp>
18
#include <universal/number/shared/infinite_encoding.hpp>
19
#include <universal/number/bfloat16/bfloat16_fwd.hpp>
20
#include <universal/number/bfloat16/exceptions.hpp>
21

22
namespace sw { namespace universal {
23

24
        // forward reference
25
        inline bfloat16 abs(bfloat16);
26
        inline bfloat16 sqrt(bfloat16);
27
        inline bfloat16 floor(bfloat16);
28

29
// bfloat16 is Google's Brain Float type
30
class bfloat16 {
31
                // HELPER methods
32
        // bfloat16 is the upper 16 bits of an IEEE-754 float; conversion is pure
33
        // bit-shuffle via sw::bit_cast (constexpr where __builtin_bit_cast is
34
        // constexpr - i.e. on gcc, clang, MSVC modern). This replaces the previous
35
        // std::memcpy-based punning, which was decorated constexpr but is not
36
        // actually constexpr until C++26.
37
        template<typename SignedInt,
38
                typename = typename std::enable_if< std::is_integral<SignedInt>::value, SignedInt >::type>
39
        BIT_CAST_CONSTEXPR bfloat16& convert_signed(SignedInt v) noexcept {
362✔
40
                if (0 == v) {
362✔
41
                        setzero();
3✔
42
                }
43
                else {
44
                        return convert_ieee754(static_cast<float>(v));
359✔
45
                }
46
                return *this;
3✔
47
        }
48
        template<typename UnsignedInt,
49
                typename = typename std::enable_if< std::is_integral<UnsignedInt>::value, UnsignedInt >::type>
50
        BIT_CAST_CONSTEXPR bfloat16& convert_unsigned(UnsignedInt v) noexcept {
51
                if (0 == v) {
52
                        setzero();
53
                }
54
                else {
55
                        return convert_ieee754(static_cast<float>(v));
56
                }
57
                return *this;
58
        }
59
        template<typename Real,
60
                typename = typename std::enable_if< std::is_floating_point<Real>::value, Real >::type>
61
        BIT_CAST_CONSTEXPR bfloat16& convert_ieee754(Real rhs) noexcept {
9,635,075✔
62
                // Down-cast wider floats to single precision first; bfloat16 is
63
                // defined relative to the 32-bit IEEE-754 layout.
64
                float f = static_cast<float>(rhs);
9,635,075✔
65
                uint32_t bits = sw::bit_cast<uint32_t>(f);
9,635,075✔
66
                _bits = static_cast<uint16_t>(bits >> 16);
9,635,181✔
67
                return *this;
9,635,181✔
68
        }
69
        template<typename Real,
70
                typename = typename std::enable_if< std::is_floating_point<Real>::value, Real >::type>
71
        BIT_CAST_CONSTEXPR Real convert_to_ieee754() const noexcept {
177,667,704✔
72
                uint32_t bits = static_cast<uint32_t>(_bits) << 16;
177,667,704✔
73
                float f = sw::bit_cast<float>(bits);
177,667,704✔
74
                return static_cast<Real>(f);
179,533,688✔
75
        }
76
public:
77
        static constexpr unsigned nbits = 16;
78
        static constexpr unsigned es = 8;
79

80
        bfloat16() = default;
81

82
        constexpr bfloat16(const bfloat16&) = default;
83
        constexpr bfloat16(bfloat16&&) = default;
84

85
        constexpr bfloat16& operator=(const bfloat16&) = default;
86
        constexpr bfloat16& operator=(bfloat16&&) = default;
87

88
        // specific value constructor
89
        constexpr bfloat16(const SpecificValue code) noexcept : _bits{} {
35✔
90
                switch (code) {
35✔
91
                case SpecificValue::infpos:
6✔
92
                        setinf(false);
6✔
93
                        break;
6✔
94
                case SpecificValue::maxpos:
10✔
95
                        maxpos();
10✔
96
                        break;
10✔
97
                case SpecificValue::minpos:
8✔
98
                        minpos();
8✔
99
                        break;
8✔
100
                case SpecificValue::zero:
×
101
                default:
102
                        zero();
×
103
                        break;
×
104
                case SpecificValue::minneg:
×
105
                        minneg();
×
106
                        break;
×
107
                case SpecificValue::infneg:
×
108
                        setinf(true);
×
109
                        break;
×
110
                case SpecificValue::maxneg:
5✔
111
                        maxneg();
5✔
112
                        break;
5✔
113
                case SpecificValue::qnan:
3✔
114
                case SpecificValue::nar:
115
                        setnan(NAN_TYPE_QUIET);
3✔
116
                        break;
3✔
117
                case SpecificValue::snan:
3✔
118
                        setnan(NAN_TYPE_SIGNALLING);
3✔
119
                        break;
3✔
120
                }
121
        }
35✔
122

123
        // initializers for native types. Integer ctors are BIT_CAST_CONSTEXPR
124
        // because they route through convert_ieee754 (via convert_signed /
125
        // convert_unsigned), which is BIT_CAST_CONSTEXPR.
126
        BIT_CAST_CONSTEXPR bfloat16(signed char iv)                    noexcept : _bits{} { *this = iv; }
127
        BIT_CAST_CONSTEXPR bfloat16(short iv)                          noexcept : _bits{} { *this = iv; }
128
        BIT_CAST_CONSTEXPR bfloat16(int iv)                            noexcept : _bits{} { *this = iv; }
356✔
129
        BIT_CAST_CONSTEXPR bfloat16(long iv)                           noexcept : _bits{} { *this = iv; }
130
        BIT_CAST_CONSTEXPR bfloat16(long long iv)                      noexcept : _bits{} { *this = iv; }
131
        BIT_CAST_CONSTEXPR bfloat16(char iv)                           noexcept : _bits{} { *this = iv; }
132
        BIT_CAST_CONSTEXPR bfloat16(unsigned short iv)                 noexcept : _bits{} { *this = iv; }
133
        BIT_CAST_CONSTEXPR bfloat16(unsigned int iv)                   noexcept : _bits{} { *this = iv; }
134
        BIT_CAST_CONSTEXPR bfloat16(unsigned long iv)                  noexcept : _bits{} { *this = iv; }
135
        BIT_CAST_CONSTEXPR bfloat16(unsigned long long iv)             noexcept : _bits{} { *this = iv; }
136
        explicit BIT_CAST_CONSTEXPR bfloat16(float iv)                 noexcept : _bits{} { *this = iv; }
891,851✔
137
        explicit BIT_CAST_CONSTEXPR bfloat16(double iv)                noexcept : _bits{} { *this = iv; }
168,557✔
138

139
        // assignment operators for native types
140
        BIT_CAST_CONSTEXPR bfloat16& operator=(signed char rhs)        noexcept { return convert_signed(rhs); }
141
        BIT_CAST_CONSTEXPR bfloat16& operator=(short rhs)              noexcept { return convert_signed(rhs); }
142
        BIT_CAST_CONSTEXPR bfloat16& operator=(int rhs)                noexcept { return convert_signed(rhs); }
362✔
143
        BIT_CAST_CONSTEXPR bfloat16& operator=(long rhs)               noexcept { return convert_signed(rhs); }
144
        BIT_CAST_CONSTEXPR bfloat16& operator=(long long rhs)          noexcept { return convert_signed(rhs); }
145
        BIT_CAST_CONSTEXPR bfloat16& operator=(char rhs)               noexcept {
146
                // Plain char is implementation-defined as either signed or unsigned;
147
                // dispatch on its signedness so negative values on signed-char
148
                // platforms (the common case) sign-extend correctly.
149
                if constexpr (std::is_signed_v<char>) {
150
                        return convert_signed(rhs);
151
                }
152
                else {
153
                        return convert_unsigned(static_cast<unsigned char>(rhs));
154
                }
155
        }
156
        BIT_CAST_CONSTEXPR bfloat16& operator=(unsigned short rhs)     noexcept { return convert_unsigned(rhs); }
157
        BIT_CAST_CONSTEXPR bfloat16& operator=(unsigned int rhs)       noexcept { return convert_unsigned(rhs); }
158
        BIT_CAST_CONSTEXPR bfloat16& operator=(unsigned long rhs)      noexcept { return convert_unsigned(rhs); }
159
        BIT_CAST_CONSTEXPR bfloat16& operator=(unsigned long long rhs) noexcept { return convert_unsigned(rhs); }
160
        BIT_CAST_CONSTEXPR bfloat16& operator=(float rhs)              noexcept { return convert_ieee754(rhs); }
4,990,799✔
161
        BIT_CAST_CONSTEXPR bfloat16& operator=(double rhs)             noexcept { return convert_ieee754(rhs); }
4,643,943✔
162

163
        // conversion operators (BIT_CAST_CONSTEXPR via convert_to_ieee754)
164
        explicit BIT_CAST_CONSTEXPR operator signed char()        const noexcept { return (signed char)(float(*this)); }
1✔
165
        explicit BIT_CAST_CONSTEXPR operator short()              const noexcept { return short(float(*this)); }
1✔
166
        explicit BIT_CAST_CONSTEXPR operator int()                const noexcept { return int(float(*this)); }
167
        explicit BIT_CAST_CONSTEXPR operator long()               const noexcept { return long(float(*this)); }
1✔
168
        explicit BIT_CAST_CONSTEXPR operator long long()          const noexcept { return (long long)(float(*this)); }
1✔
169
        explicit BIT_CAST_CONSTEXPR operator char()               const noexcept { return (char)(float(*this)); }
1✔
170
        explicit BIT_CAST_CONSTEXPR operator unsigned short()     const noexcept { return (unsigned short)(float(*this)); }
1✔
171
        explicit BIT_CAST_CONSTEXPR operator unsigned int()       const noexcept { return (unsigned int)(float(*this)); }
172
        explicit BIT_CAST_CONSTEXPR operator unsigned long()      const noexcept { return (unsigned long)(float(*this)); }
101✔
173
        explicit BIT_CAST_CONSTEXPR operator unsigned long long() const noexcept { return (unsigned long long)(float(*this)); }
1✔
174
        explicit BIT_CAST_CONSTEXPR operator float()              const noexcept { return convert_to_ieee754<float>(); }
170,931,669✔
175
        explicit BIT_CAST_CONSTEXPR operator double()             const noexcept { return convert_to_ieee754<double>(); }
8,639,238✔
176

177
#if LONG_DOUBLE_SUPPORT
178
        explicit BIT_CAST_CONSTEXPR bfloat16(long double iv)           noexcept : _bits{} { *this = iv; }
179
        BIT_CAST_CONSTEXPR bfloat16& operator=(long double rhs)        noexcept { return convert_ieee754(rhs); }
180
        explicit BIT_CAST_CONSTEXPR operator long double()        const noexcept { return convert_to_ieee754<long double>(); }
181
#endif
182

183
        // prefix operators
184
        constexpr bfloat16 operator-() const noexcept {
5✔
185
                bfloat16 tmp;
186
                tmp.setbits(_bits ^ 0x8000u);
5✔
187
                return tmp;
5✔
188
        }
189

190
        constexpr bfloat16& operator++() noexcept {
259✔
191
                if (isneg()) {
259✔
192
                        if (_bits == 0x8001u) { // pattern 1.00.001 == minneg
×
193
                                _bits = 0;
×
194
                        }
195
                        else {
196
                                --_bits;
×
197
                        }
198
                }
199
                else {
200
                        if (_bits == 0x7FFFu) { // pattern = qnan
259✔
201
                                _bits = 0xFFFFu;  // pattern = snan 
×
202
                        }
203
                        else {
204
                                ++_bits;
259✔
205
                        }
206
                }
207
                return *this;
259✔
208
        }
209
        constexpr bfloat16 operator++(int) noexcept {
210
                bfloat16 tmp(*this);
211
                operator++();
212
                return tmp;
213
        }
214
        constexpr bfloat16& operator--() noexcept {
3✔
215
                if (sign()) {
3✔
216
                        ++_bits;
×
217
                }
218
                else {
219
                        if (_bits == 0) { // pattern 0.00.000 = 0
3✔
220
                                _bits = 0x8001u; // pattern 1.00.001 = minneg
1✔
221
                        }
222
                        else {
223
                                --_bits;
2✔
224
                        }
225
                }
226
                return *this;
3✔
227
        }
228
        constexpr bfloat16 operator--(int) noexcept {
1✔
229
                bfloat16 tmp(*this);
1✔
230
                operator--();
1✔
231
                return tmp;
1✔
232
        }
233

234
        // arithmetic operators - cast to float, compute, cast back. Both legs use
235
        // BIT_CAST_CONSTEXPR conversions, and IEEE-754 float arithmetic itself is
236
        // constexpr in C++20, so the compound is BIT_CAST_CONSTEXPR overall.
237
        BIT_CAST_CONSTEXPR bfloat16& operator+=(const bfloat16& rhs) {
1,000,100✔
238
                *this = float(*this) + float(rhs);
1,000,100✔
239
                return *this;
1,000,100✔
240
        }
241
        BIT_CAST_CONSTEXPR bfloat16& operator-=(const bfloat16& rhs) {
1,000,002✔
242
                *this = float(*this) - float(rhs);
1,000,002✔
243
                return *this;
1,000,002✔
244
        }
245
        BIT_CAST_CONSTEXPR bfloat16& operator*=(const bfloat16& rhs) {
1,000,356✔
246
                *this = float(*this) * float(rhs);
1,000,356✔
247
                return *this;
1,000,356✔
248
        }
249
        BIT_CAST_CONSTEXPR bfloat16& operator/=(const bfloat16& rhs) {
1,000,001✔
250
                *this = float(*this) / float(rhs);
1,000,001✔
251
                return *this;
1,000,001✔
252
        }
253

254
        // modifiers
255
        constexpr void clear() noexcept { _bits = 0; }
5✔
256
        constexpr void setzero() noexcept { clear(); }
3✔
257
        constexpr void setnan(int NaNType = NAN_TYPE_SIGNALLING) noexcept {
7✔
258
                _bits = (NaNType == NAN_TYPE_SIGNALLING ? 0xFF81u : 0x7F81u);
7✔
259
        }
7✔
260
        constexpr void setinf(bool sign = false) noexcept { 
7✔
261
                _bits = (sign ? 0xFF80u : 0x7F80u); 
7✔
262
        }
7✔
263
        constexpr void setbit(unsigned i, bool v = true) noexcept {
34✔
264
                unsigned short bit = (1u << i);
34✔
265
                if (v) {
34✔
266
                        _bits |= bit;
17✔
267
                }
268
                else {
269
                        _bits &= ~bit;
17✔
270
                }
271
        }
34✔
272
        constexpr void setbits(unsigned value) noexcept { _bits = (value & 0xFFFFu); }
8,573,849✔
273

274
        constexpr bfloat16& minpos() noexcept { _bits = 0x0001u; return *this; }
11✔
275
        constexpr bfloat16& maxpos() noexcept { _bits = 0x7F7Fu; return *this; }
13✔
276
        constexpr bfloat16& zero()   noexcept { _bits = 0x0000u; return *this; }
2✔
277
        constexpr bfloat16& minneg() noexcept { _bits = 0x8001u; return *this; }
3✔
278
        constexpr bfloat16& maxneg() noexcept { _bits = 0xFF7Fu; return *this; }
8✔
279

280
        /// <summary>
281
        /// assign the value of the string representation to the bfloat16
282
        /// </summary>
283
        /// <param name="stringRep">decimal scientific notation of a real number to be assigned</param>
284
        /// <returns>reference to this cfloat</returns>
285
        /// Clang doesn't support constexpr yet on string manipulations, so we need to make it conditional
286
        bfloat16& assign(const std::string& str) noexcept {
2✔
287
                clear();
2✔
288
                unsigned nrChars = static_cast<unsigned>(str.size());
2✔
289
                unsigned nrBits = 0;
2✔
290
                unsigned nrDots = 0;
2✔
291
                std::string bits;
2✔
292
                if (nrChars > 2) {
2✔
293
                        if (str[0] == '0' && str[1] == 'b') {
2✔
294
                                for (unsigned i = 2; i < nrChars; ++i) {
42✔
295
                                        char c = str[i];
40✔
296
                                        switch (c) {
40✔
297
                                        case '0':
32✔
298
                                        case '1':
299
                                                ++nrBits;
32✔
300
                                                bits += c;
32✔
301
                                                break;
32✔
302
                                        case '.':
4✔
303
                                                ++nrDots;
4✔
304
                                                bits += c;
4✔
305
                                                break;
4✔
306
                                        case '\'':
4✔
307
                                                // consume this delimiting character
308
                                                break;
4✔
309
                                        default:
×
310
                                                std::cerr << "string contained a non-standard character: " << c << '\n';
×
311
                                                return *this;
×
312
                                        }
313
                                }
314
                        }
315
                        else {
316
                                std::cerr << "string must start with 0b: instead input pattern was " << str << '\n';
×
317
                                return *this;
×
318
                        }
319
                }
320
                else {
321
                        std::cerr << "string is too short\n";
×
322
                        return *this;
×
323
                }
324

325
                if (nrBits != nbits) {
2✔
326
                        std::cerr << "number of bits in the string is " << nrBits << " and needs to be " << nbits << '\n';
×
327
                        return *this;
×
328
                }
329
                if (nrDots != 2) {
2✔
330
                        std::cerr << "number of segment delimiters in string is " << nrDots << " and needs to be 2 for a cfloat<>\n";
×
331
                        return *this;
×
332
                }
333

334
                // assign the bits
335
                int field{ 0 };  // three fields: sign, exponent, mantissa: fields are separated by a '.'
2✔
336
                int nrExponentBits{ -1 };
2✔
337
                unsigned bit = nrBits;
2✔
338
                for (unsigned i = 0; i < bits.size(); ++i) {
38✔
339
                        char c = bits[i];
36✔
340
                        if (c == '.') {
36✔
341
                                ++field;
4✔
342
                                if (field == 2) { // just finished parsing exponent field: we can now check the number of exponent bits
4✔
343
                                        if (nrExponentBits != es) {
2✔
344
                                                std::cerr << "provided binary string representation does not contain " << es << " exponent bits. Found " << nrExponentBits << ". Reset to 0\n";
×
345
                                                clear();
×
346
                                                return *this;
×
347
                                        }
348
                                }
349
                        }
350
                        else {
351
                                setbit(--bit, c == '1');
32✔
352
                        }
353
                        if (field == 1) { // exponent field
36✔
354
                                ++nrExponentBits;
18✔
355
                        }
356
                }
357
                if (field != 2) {
2✔
358
                        std::cerr << "provided binary string did not contain three fields separated by '.': Reset to 0\n";
×
359
                        clear();
×
360
                        return *this;
×
361
                }
362
                return *this;
2✔
363
        }
2✔
364

365
        // selectors
366
        constexpr bool iszero()    const noexcept { return (_bits == 0x0000u) || (_bits == 0x8000u); }
367
        constexpr bool isone()     const noexcept { return (_bits == 0x3F80u); }
1✔
368
        constexpr bool isodd()     const noexcept { return (_bits & 0x0001u); }
369
        constexpr bool iseven()    const noexcept { return !isodd(); }
370
        // not constexpr because of floor()
371
        bool isinteger() const noexcept {
200✔
372
                if (isnan() || isinf()) return false;
200✔
373
                return (floor(*this) == *this);
200✔
374
        }
375
        constexpr bool ispos()     const noexcept { return !isneg(); }
376
        constexpr bool isneg()     const noexcept { return (_bits & 0x8000u); }
977✔
377
        /*
378
        * IEEE 754-2008 specifies the encoding of NaN and Infinity in the following way:
379
         Sign | Exponent | Mantissa
380
        ----- | -------- | -------- -
381
                x | 11111111 | 1000000   Quiet NaN(qNaN)
382
                x | 11111111 | 0xxxxxx   Signaling NaN(sNaN)
383
        */
384
        constexpr bool isnan(int NaNType = NAN_TYPE_EITHER)  const noexcept { 
9,183,456✔
385
                // bool negative = isneg(); is not used to determine NaN
386
                bool isNaN    = ((_bits & 0x7F80u) == 0x7f80u) && ((_bits & 0x007Fu) != 0);
9,183,456✔
387
                bool isQuietNaN = isNaN && ((_bits & 0x0040u) != 0); // MSB of mantissa is 1
9,183,456✔
388
                bool isSignalNaN = isNaN && ((_bits & 0x0040u) == 0); // MSB of mantissa is 0        
9,183,456✔
389
                return (NaNType == NAN_TYPE_EITHER ? isNaN :
9,183,460✔
390
                        (NaNType == NAN_TYPE_SIGNALLING ? isSignalNaN :
6✔
391
                                (NaNType == NAN_TYPE_QUIET ? isQuietNaN : false)));
9,183,460✔
392
        }
9,183,456✔
393
        constexpr bool isinf(int InfType = INF_TYPE_EITHER)  const noexcept { 
200✔
394
                bool negative = isneg();
200✔
395
                bool isInf = ((_bits & 0x7F80u) == 0x7f80u) && ((_bits & 0x007fu) == 0u);  // all exponent bits set, no mantissa bits set
200✔
396
                bool isNegInf = isInf && negative;
200✔
397
                bool isPosInf = isInf && !negative;
200✔
398
                return (InfType == INF_TYPE_EITHER ? (isNegInf || isPosInf) :
200✔
399
                        (InfType == INF_TYPE_NEGATIVE ? isNegInf :
×
400
                                (InfType == INF_TYPE_POSITIVE ? isPosInf : false)));
200✔
401
        }
×
402
        constexpr bool sign()   const noexcept { return isneg(); }
518✔
403
        constexpr int  scale()  const noexcept { int biased = static_cast<int>((_bits & 0x7F80u) >> 7); return biased - 127; }
5✔
404
        constexpr unsigned short bits() const noexcept { return _bits; }
67✔
405

406
        constexpr bool test(unsigned bitIndex) const noexcept { return at(bitIndex); }
407
        constexpr bool at(unsigned bitIndex) const noexcept {
408
                if (bitIndex < nbits) {
409
                        uint16_t word = _bits;
410
                        uint16_t mask = uint16_t(1ull << bitIndex);
411
                        return (word & mask);
412
                }
413
                return false;
414
        }
415
        constexpr uint8_t nibble(unsigned n) const noexcept {
416
                if (n < 4) {
417
                        uint16_t word = _bits;
418
                        int nibbleIndexInWord = int(n);
419
                        uint16_t mask = uint16_t(0xF << (nibbleIndexInWord * 4));
420
                        uint16_t nibblebits = uint16_t(mask & word);
421
                        return uint8_t(nibblebits >> (nibbleIndexInWord * 4));
422
                }
423
                return 0;
424
        }
425
        constexpr uint8_t exponent() const noexcept {
515✔
426
                uint8_t e = static_cast<uint8_t>((_bits & 0x7f80) >> 7);
515✔
427
                return e;
515✔
428
        }
429
        constexpr uint8_t fraction() const noexcept {
515✔
430
                uint8_t f = static_cast<uint8_t>(_bits & 0x7f);
515✔
431
                return f;
515✔
432
        }
433

434
protected:
435
        unsigned short _bits;
436

437
private:
438

439
        // bfloat16 - bfloat16 logic comparisons
440
        friend constexpr bool operator==(bfloat16 lhs, bfloat16 rhs);
441

442
        // bfloat16 - literal logic comparisons (BIT_CAST_CONSTEXPR via bfloat16(float))
443
        friend BIT_CAST_CONSTEXPR bool operator==(bfloat16 lhs, float rhs);
444

445
        // literal - bfloat16 logic comparisons (BIT_CAST_CONSTEXPR via bfloat16(float))
446
        friend BIT_CAST_CONSTEXPR bool operator==(float lhs, bfloat16 rhs);
447
};
448

449
////////////////////////    functions   /////////////////////////////////
450

451

452
inline bfloat16 abs(bfloat16 a) {
453
        return (a.isneg() ? -a : a);
454
}
455

456

457
/// stream operators
458

459
// parse a bfloat16 ASCII format and make a binary bfloat16 out of it
460
inline bool parse(const std::string& /* number */, bfloat16& value) {
461
        bool bSuccess = false;
462
        value.zero();
463
        return bSuccess;
464
}
465

466
// generate an bfloat16 format ASCII format
467
inline std::ostream& operator<<(std::ostream& ostr, bfloat16 bf) {
660✔
468
        return ostr << float(bf);
660✔
469
}
470

471
// read an ASCII bfloat16 format
472
inline std::istream& operator>>(std::istream& istr, bfloat16& p) {
473
        std::string txt;
474
        istr >> txt;
475
        if (!parse(txt, p)) {
476
                std::cerr << "unable to parse -" << txt << "- into a bfloat16 value\n";
477
        }
478
        return istr;
479
}
480

481
////////////////// string operators
482

483
inline std::string to_binary(bfloat16 bf, bool bNibbleMarker = false) {
67✔
484
        std::stringstream s;
67✔
485
        unsigned short bits = bf.bits();
67✔
486
        unsigned short mask = 0x8000u;
67✔
487
        s << (bits & mask ? "0b1." : "0x0.");
67✔
488
        mask >>= 1;
67✔
489
        // exponent bits
490
        for (unsigned i = 0; i < 8; ++i) {
603✔
491
                if (bNibbleMarker && (4 == i)) {
536✔
492
                        s << '\'';
3✔
493
                }
494
                s << (bits & mask ? '1' : '0');
536✔
495
                mask >>= 1;
536✔
496
        }
497
        s << '.';
67✔
498
        for (unsigned i = 0; i < 7; ++i) {
536✔
499
                if (bNibbleMarker && (3 == i)) {
469✔
500
                        s << '\'';
3✔
501
                }        
502
                s << (bits & mask ? '1' : '0');
469✔
503
                mask >>= 1;
469✔
504
        }
505
        return s.str();
134✔
506
}
67✔
507

508
// native semantic representation: radix-2, delegates to to_binary
509
inline std::string to_native(bfloat16 v, bool nibbleMarker = false) {
510
        return to_binary(v, nibbleMarker);
511
}
512

513
//////////////////////////////////////////////////////////////////////////////////////////////////////
514
// bfloat16 - bfloat16 binary logic operators
515

516
// equal: precondition is that the storage is properly nulled in all arithmetic paths
517
constexpr inline bool operator==(bfloat16 lhs, bfloat16 rhs) {
4,574,131✔
518
        // NaN != NaN
519
        if (lhs.isnan() || rhs.isnan()) return false;
4,574,131✔
520
        return lhs._bits == rhs._bits;
4,477,539✔
521
}
522

523
constexpr inline bool operator!=(bfloat16 lhs, bfloat16 rhs) {
4,573,926✔
524
        return !operator==(lhs, rhs);
4,573,926✔
525
}
526

527
// operator< is BIT_CAST_CONSTEXPR (depends on the float() conversion)
528
BIT_CAST_CONSTEXPR inline bool operator< (bfloat16 lhs, bfloat16 rhs) {
11✔
529
        return (float(lhs) - float(rhs)) < 0;
11✔
530
}
531

532
BIT_CAST_CONSTEXPR inline bool operator> (bfloat16 lhs, bfloat16 rhs) {
4✔
533
        return operator< (rhs, lhs);
4✔
534
}
535

536
BIT_CAST_CONSTEXPR inline bool operator<=(bfloat16 lhs, bfloat16 rhs) {
2✔
537
        return operator< (lhs, rhs) || operator==(lhs, rhs);
2✔
538
}
539

540
BIT_CAST_CONSTEXPR inline bool operator>=(bfloat16 lhs, bfloat16 rhs) {
2✔
541
        return !operator< (lhs, rhs);
2✔
542
}
543

544
//////////////////////////////////////////////////////////////////////////////////////////////////////
545
// bfloat16 - literal binary logic operators
546
// All take bfloat16 by value to match the friend declarations and to enable
547
// BIT_CAST_CONSTEXPR (the bfloat16(float) ctor is BIT_CAST_CONSTEXPR).
548

549
BIT_CAST_CONSTEXPR inline bool operator==(bfloat16 lhs, float rhs) {
550
        return operator==(lhs, bfloat16(rhs));
551
}
552

NEW
553
BIT_CAST_CONSTEXPR inline bool operator!=(bfloat16 lhs, float rhs) {
×
554
        return !operator==(lhs, bfloat16(rhs));
×
555
}
556

557
BIT_CAST_CONSTEXPR inline bool operator< (bfloat16 lhs, float rhs) {
1✔
558
        return operator<(lhs, bfloat16(rhs));
1✔
559
}
560

NEW
561
BIT_CAST_CONSTEXPR inline bool operator> (bfloat16 lhs, float rhs) {
×
562
        return operator< (bfloat16(rhs), lhs);
×
563
}
564

565
BIT_CAST_CONSTEXPR inline bool operator<=(bfloat16 lhs, float rhs) {
566
        return operator< (lhs, bfloat16(rhs)) || operator==(lhs, bfloat16(rhs));
567
}
568

569
BIT_CAST_CONSTEXPR inline bool operator>=(bfloat16 lhs, float rhs) {
570
        return !operator< (lhs, bfloat16(rhs));
571
}
572

573
//////////////////////////////////////////////////////////////////////////////////////////////////////
574
// literal - bfloat16 binary logic operators
575

576
BIT_CAST_CONSTEXPR inline bool operator==(float lhs, bfloat16 rhs) {
577
        return operator==(bfloat16(lhs), rhs);
578
}
579

580
BIT_CAST_CONSTEXPR inline bool operator!=(float lhs, bfloat16 rhs) {
581
        return !operator==(bfloat16(lhs), rhs);
582
}
583

584
BIT_CAST_CONSTEXPR inline bool operator< (float lhs, bfloat16 rhs) {
585
        return operator<(bfloat16(lhs), rhs);
586
}
587

588
BIT_CAST_CONSTEXPR inline bool operator> (float lhs, bfloat16 rhs) {
589
        return operator< (rhs, bfloat16(lhs));
590
}
591

592
BIT_CAST_CONSTEXPR inline bool operator<=(float lhs, bfloat16 rhs) {
593
        return operator< (bfloat16(lhs), rhs) || operator==(bfloat16(lhs), rhs);
594
}
595

596
BIT_CAST_CONSTEXPR inline bool operator>=(float lhs, bfloat16 rhs) {
597
        return !operator< (bfloat16(lhs), rhs);
598
}
599

600
//////////////////////////////////////////////////////////////////////////////////////////////////////
601

602
//////////////////////////////////////////////////////////////////////////////////////////////////////
603
// bfloat16 - bfloat16 binary arithmetic operators
604
// BINARY ADDITION
605

606
inline bfloat16 operator+(bfloat16 lhs, bfloat16 rhs) {
1,000,000✔
607
        bfloat16 sum = lhs;
1,000,000✔
608
        sum += rhs;
1,000,000✔
609
        return sum;
1,000,000✔
610
}
611
// BINARY SUBTRACTION
612

613
inline bfloat16 operator-(bfloat16 lhs, bfloat16 rhs) {
1,000,002✔
614
        bfloat16 diff = lhs;
1,000,002✔
615
        diff -= rhs;
1,000,002✔
616
        return diff;
1,000,002✔
617
}
618
// BINARY MULTIPLICATION
619

620
inline bfloat16 operator*(bfloat16 lhs, bfloat16 rhs) {
1,000,102✔
621
        bfloat16 mul = lhs;
1,000,102✔
622
        mul *= rhs;
1,000,102✔
623
        return mul;
1,000,102✔
624
}
625
// BINARY DIVISION
626

627
inline bfloat16 operator/(bfloat16 lhs, bfloat16 rhs) {
1,000,001✔
628
        bfloat16 ratio = lhs;
1,000,001✔
629
        ratio /= rhs;
1,000,001✔
630
        return ratio;
1,000,001✔
631
}
632

633
//////////////////////////////////////////////////////////////////////////////////////////////////////
634
// bfloat16 - literal binary arithmetic operators
635
// BINARY ADDITION
636

637
inline bfloat16 operator+(bfloat16 lhs, float rhs) {
638
        return operator+(lhs, bfloat16(rhs));
639
}
640
// BINARY SUBTRACTION
641

642
inline bfloat16 operator-(bfloat16 lhs, float rhs) {
643
        return operator-(lhs, bfloat16(rhs));
644
}
645
// BINARY MULTIPLICATION
646

647
inline bfloat16 operator*(bfloat16 lhs, float rhs) {
648
        return operator*(lhs, bfloat16(rhs));
649
}
650
// BINARY DIVISION
651

652
inline bfloat16 operator/(bfloat16 lhs, float rhs) {
653
        return operator/(lhs, bfloat16(rhs));
654
}
655

656
//////////////////////////////////////////////////////////////////////////////////////////////////////
657
// literal - bfloat16 binary arithmetic operators
658
// BINARY ADDITION
659

660
inline bfloat16 operator+(float lhs, bfloat16 rhs) {
661
        return operator+(bfloat16(lhs), rhs);
662
}
663
// BINARY SUBTRACTION
664

665
inline bfloat16 operator-(float lhs, bfloat16 rhs) {
666
        return operator-(bfloat16(lhs), rhs);
667
}
668
// BINARY MULTIPLICATION
669

670
inline bfloat16 operator*(float lhs, bfloat16 rhs) {
2✔
671
        return operator*(bfloat16(lhs), rhs);
2✔
672
}
673
// BINARY DIVISION
674

675
inline bfloat16 operator/(float lhs, bfloat16 rhs) {
676
        return operator/(bfloat16(lhs), rhs);
677
}
678

679
}} // namespace sw::universal
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc