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

stillwater-sc / universal / 25988762611

17 May 2026 10:49AM UTC coverage: 84.022% (-0.06%) from 84.079%
25988762611

push

github

web-flow
feat: API parity / implementation for decimal string parse in specialized FP (Phase D of #835) (#850)

Phase D wraps up the remaining specialized FP family. Four types
covered, one (hfloat) explicitly deferred to a tracking issue.

posit1, posito (older posit revisions sharing posit_parse.hpp style):
  - Add nan / inf / infinity token detection (case-insensitive,
    optional sign). All non-finite spellings route to setnar() since
    posit has only one NaR encoding -- same convention as the
    current posit B2b path.
  - Fix latent bug in the stod fallback: was returning bSuccess=true
    even when istringstream extraction failed. Now properly checks
    ss.fail() and trailing-junk via !ss.eof().
  - operator>>: guard extraction (return if istr >> txt fails), set
    failbit on parse failure (alongside the existing cerr diagnostic).

unum (Type I):
  - Add nan / inf / infinity token detection. unum has no Inf
    encoding, so inf tokens collapse to NaN -- matching the existing
    operator=(double) behavior that also maps +/-inf to NaN.
  - operator>>: extraction guard + failbit, matching the B2c pattern.

bfloat16:
  - Implement parse(). Previous body was a stub that returned false
    and set value to zero, ignoring input. New implementation:
      * nan / inf / infinity token routing to setnan / setinf
      * stod fallback for decimal (bfloat16 has only 7 explicit
        mantissa bits; double precision is way more than enough)
      * ss.fail() and !ss.eof() guards reject malformed input
  - operator>>: extraction guard + failbit.

Tests (4 new files, ReportTestSuite pattern):
  - static/tapered/posit1/conversion/string_parse.cpp
  - static/tapered/posito/conversion/string_parse.cpp
  - static/float/bfloat16/conversion/string_parse.cpp
  - elastic/unum/string_parse.cpp

Each covers: canonical decimals, nan/inf token routing, operator>>
failbit, malformed-input rejection.

hfloat is NOT included -- its parse() is a stub like bfloat16's was,
but ... (continued)

66 of 70 new or added lines in 6 files covered. (94.29%)

10 existing lines in 1 file now uncovered.

46481 of 55320 relevant lines covered (84.02%)

6415245.07 hits per line

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

87.03
/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 <cctype>
9
#include <cstdint>
10
#include <string>
11
#include <sstream>
12
#include <iostream>
13
#include <iomanip>
14
#include <regex>
15

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

23
namespace sw { namespace universal {
24

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

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

81
        bfloat16() = default;
82

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

435
protected:
436
        unsigned short _bits;
437

438
private:
439

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

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

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

450
////////////////////////    functions   /////////////////////////////////
451

452

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

457

458
/// stream operators
459

460
// parse a bfloat16 ASCII format and make a binary bfloat16 out of it
461
inline bool parse(const std::string& number, bfloat16& value) {
30✔
462
        // Detect nan / inf / infinity tokens (case-insensitive, optional sign).
463
        {
464
                std::string t;
30✔
465
                t.reserve(number.size());
30✔
466
                for (char c : number) {
180✔
467
                        t.push_back(static_cast<char>(std::tolower(static_cast<unsigned char>(c))));
150✔
468
                }
469
                bool negative = !t.empty() && t.front() == '-';
30✔
470
                std::string body = t;
30✔
471
                if (!body.empty() && (body.front() == '+' || body.front() == '-')) body.erase(0, 1);
30✔
472
                if (body == "nan") {
30✔
473
                        value.setnan(NAN_TYPE_QUIET);
4✔
474
                        return true;
4✔
475
                }
476
                if (body == "inf" || body == "infinity") {
26✔
477
                        value.setinf(negative);
12✔
478
                        return true;
12✔
479
                }
480
        }
46✔
481
        // Decimal floating-point: bfloat16 has only 7 explicit mantissa bits,
482
        // so std::istringstream's double extraction is more than enough
483
        // precision -- the value gets immediately rounded into the bfloat16
484
        // encoding via convert_ieee754.
485
        std::istringstream ss(number);
14✔
486
        double d;
487
        ss >> d;
14✔
488
        if (ss.fail()) return false;
14✔
489
        ss >> std::ws;
10✔
490
        if (!ss.eof()) return false;
10✔
491
        value = d;
9✔
492
        return true;
9✔
493
}
14✔
494

495
// generate an bfloat16 format ASCII format
496
inline std::ostream& operator<<(std::ostream& ostr, bfloat16 bf) {
660✔
497
        return ostr << float(bf);
660✔
498
}
499

500
// read an ASCII bfloat16 format
501
inline std::istream& operator>>(std::istream& istr, bfloat16& p) {
1✔
502
        std::string txt;
1✔
503
        if (!(istr >> txt)) {
1✔
504
                // extraction failed (already-bad stream or EOF); failbit set by >>.
NEW
505
                return istr;
×
506
        }
507
        if (!parse(txt, p)) {
1✔
508
                std::cerr << "unable to parse -" << txt << "- into a bfloat16 value\n";
1✔
509
                istr.setstate(std::ios::failbit);
1✔
510
        }
511
        return istr;
1✔
512
}
1✔
513

514
////////////////// string operators
515

516
inline std::string to_binary(bfloat16 bf, bool bNibbleMarker = false) {
67✔
517
        std::stringstream s;
67✔
518
        unsigned short bits = bf.bits();
67✔
519
        unsigned short mask = 0x8000u;
67✔
520
        s << (bits & mask ? "0b1." : "0x0.");
67✔
521
        mask >>= 1;
67✔
522
        // exponent bits
523
        for (unsigned i = 0; i < 8; ++i) {
603✔
524
                if (bNibbleMarker && (4 == i)) {
536✔
525
                        s << '\'';
3✔
526
                }
527
                s << (bits & mask ? '1' : '0');
536✔
528
                mask >>= 1;
536✔
529
        }
530
        s << '.';
67✔
531
        for (unsigned i = 0; i < 7; ++i) {
536✔
532
                if (bNibbleMarker && (3 == i)) {
469✔
533
                        s << '\'';
3✔
534
                }        
535
                s << (bits & mask ? '1' : '0');
469✔
536
                mask >>= 1;
469✔
537
        }
538
        return s.str();
134✔
539
}
67✔
540

541
// native semantic representation: radix-2, delegates to to_binary
542
inline std::string to_native(bfloat16 v, bool nibbleMarker = false) {
543
        return to_binary(v, nibbleMarker);
544
}
545

546
//////////////////////////////////////////////////////////////////////////////////////////////////////
547
// bfloat16 - bfloat16 binary logic operators
548

549
// equal: precondition is that the storage is properly nulled in all arithmetic paths
550
constexpr inline bool operator==(bfloat16 lhs, bfloat16 rhs) {
4,574,140✔
551
        // NaN != NaN
552
        if (lhs.isnan() || rhs.isnan()) return false;
4,574,140✔
553
        // IEEE-754 signed zero: +0 and -0 compare equal even though their bit
554
        // patterns differ (0x0000 vs 0x8000). operator-(bfloat16(0)) produces
555
        // the 0x8000 pattern directly, so this case is observable in practice.
556
        if (lhs.iszero() && rhs.iszero()) return true;
4,477,254✔
557
        return lhs._bits == rhs._bits;
4,153,698✔
558
}
559

560
constexpr inline bool operator!=(bfloat16 lhs, bfloat16 rhs) {
4,573,935✔
561
        return !operator==(lhs, rhs);
4,573,935✔
562
}
563

564
// operator< is BIT_CAST_CONSTEXPR (depends on the float() conversion)
565
BIT_CAST_CONSTEXPR inline bool operator< (bfloat16 lhs, bfloat16 rhs) {
11✔
566
        return (float(lhs) - float(rhs)) < 0;
11✔
567
}
568

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

573
BIT_CAST_CONSTEXPR inline bool operator<=(bfloat16 lhs, bfloat16 rhs) {
2✔
574
        return operator< (lhs, rhs) || operator==(lhs, rhs);
2✔
575
}
576

577
BIT_CAST_CONSTEXPR inline bool operator>=(bfloat16 lhs, bfloat16 rhs) {
2✔
578
        return !operator< (lhs, rhs);
2✔
579
}
580

581
//////////////////////////////////////////////////////////////////////////////////////////////////////
582
// bfloat16 - literal binary logic operators
583
// All take bfloat16 by value to match the friend declarations and to enable
584
// BIT_CAST_CONSTEXPR (the bfloat16(float) ctor is BIT_CAST_CONSTEXPR).
585

586
BIT_CAST_CONSTEXPR inline bool operator==(bfloat16 lhs, float rhs) {
587
        return operator==(lhs, bfloat16(rhs));
588
}
589

590
BIT_CAST_CONSTEXPR inline bool operator!=(bfloat16 lhs, float rhs) {
×
591
        return !operator==(lhs, bfloat16(rhs));
×
592
}
593

594
BIT_CAST_CONSTEXPR inline bool operator< (bfloat16 lhs, float rhs) {
1✔
595
        return operator<(lhs, bfloat16(rhs));
1✔
596
}
597

598
BIT_CAST_CONSTEXPR inline bool operator> (bfloat16 lhs, float rhs) {
×
599
        return operator< (bfloat16(rhs), lhs);
×
600
}
601

602
BIT_CAST_CONSTEXPR inline bool operator<=(bfloat16 lhs, float rhs) {
603
        return operator< (lhs, bfloat16(rhs)) || operator==(lhs, bfloat16(rhs));
604
}
605

606
BIT_CAST_CONSTEXPR inline bool operator>=(bfloat16 lhs, float rhs) {
607
        return !operator< (lhs, bfloat16(rhs));
608
}
609

610
//////////////////////////////////////////////////////////////////////////////////////////////////////
611
// literal - bfloat16 binary logic operators
612

613
BIT_CAST_CONSTEXPR inline bool operator==(float lhs, bfloat16 rhs) {
614
        return operator==(bfloat16(lhs), rhs);
615
}
616

617
BIT_CAST_CONSTEXPR inline bool operator!=(float lhs, bfloat16 rhs) {
618
        return !operator==(bfloat16(lhs), rhs);
619
}
620

621
BIT_CAST_CONSTEXPR inline bool operator< (float lhs, bfloat16 rhs) {
622
        return operator<(bfloat16(lhs), rhs);
623
}
624

625
BIT_CAST_CONSTEXPR inline bool operator> (float lhs, bfloat16 rhs) {
626
        return operator< (rhs, bfloat16(lhs));
627
}
628

629
BIT_CAST_CONSTEXPR inline bool operator<=(float lhs, bfloat16 rhs) {
630
        return operator< (bfloat16(lhs), rhs) || operator==(bfloat16(lhs), rhs);
631
}
632

633
BIT_CAST_CONSTEXPR inline bool operator>=(float lhs, bfloat16 rhs) {
634
        return !operator< (bfloat16(lhs), rhs);
635
}
636

637
//////////////////////////////////////////////////////////////////////////////////////////////////////
638

639
//////////////////////////////////////////////////////////////////////////////////////////////////////
640
// bfloat16 - bfloat16 binary arithmetic operators
641
// BIT_CAST_CONSTEXPR via the compound-assignment operators (which are themselves
642
// BIT_CAST_CONSTEXPR via float()). This is the #725 public-API acceptance form:
643
// `constexpr auto c = a + b;` works once these are decorated.
644

645
BIT_CAST_CONSTEXPR inline bfloat16 operator+(bfloat16 lhs, bfloat16 rhs) {
1,000,000✔
646
        bfloat16 sum = lhs;
1,000,000✔
647
        sum += rhs;
1,000,000✔
648
        return sum;
1,000,000✔
649
}
650

651
BIT_CAST_CONSTEXPR inline bfloat16 operator-(bfloat16 lhs, bfloat16 rhs) {
1,000,002✔
652
        bfloat16 diff = lhs;
1,000,002✔
653
        diff -= rhs;
1,000,002✔
654
        return diff;
1,000,002✔
655
}
656

657
BIT_CAST_CONSTEXPR inline bfloat16 operator*(bfloat16 lhs, bfloat16 rhs) {
1,000,102✔
658
        bfloat16 mul = lhs;
1,000,102✔
659
        mul *= rhs;
1,000,102✔
660
        return mul;
1,000,102✔
661
}
662

663
BIT_CAST_CONSTEXPR inline bfloat16 operator/(bfloat16 lhs, bfloat16 rhs) {
1,000,001✔
664
        bfloat16 ratio = lhs;
1,000,001✔
665
        ratio /= rhs;
1,000,001✔
666
        return ratio;
1,000,001✔
667
}
668

669
//////////////////////////////////////////////////////////////////////////////////////////////////////
670
// bfloat16 - literal binary arithmetic operators
671

672
BIT_CAST_CONSTEXPR inline bfloat16 operator+(bfloat16 lhs, float rhs) {
673
        return operator+(lhs, bfloat16(rhs));
674
}
675

676
BIT_CAST_CONSTEXPR inline bfloat16 operator-(bfloat16 lhs, float rhs) {
677
        return operator-(lhs, bfloat16(rhs));
678
}
679

680
BIT_CAST_CONSTEXPR inline bfloat16 operator*(bfloat16 lhs, float rhs) {
681
        return operator*(lhs, bfloat16(rhs));
682
}
683

684
BIT_CAST_CONSTEXPR inline bfloat16 operator/(bfloat16 lhs, float rhs) {
685
        return operator/(lhs, bfloat16(rhs));
686
}
687

688
//////////////////////////////////////////////////////////////////////////////////////////////////////
689
// literal - bfloat16 binary arithmetic operators
690

691
BIT_CAST_CONSTEXPR inline bfloat16 operator+(float lhs, bfloat16 rhs) {
692
        return operator+(bfloat16(lhs), rhs);
693
}
694

695
BIT_CAST_CONSTEXPR inline bfloat16 operator-(float lhs, bfloat16 rhs) {
696
        return operator-(bfloat16(lhs), rhs);
697
}
698

699
BIT_CAST_CONSTEXPR inline bfloat16 operator*(float lhs, bfloat16 rhs) {
2✔
700
        return operator*(bfloat16(lhs), rhs);
2✔
701
}
702

703
BIT_CAST_CONSTEXPR inline bfloat16 operator/(float lhs, bfloat16 rhs) {
704
        return operator/(bfloat16(lhs), rhs);
705
}
706

707
}} // 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