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

stillwater-sc / universal / 25354925792

05 May 2026 02:39AM UTC coverage: 83.981% (-0.03%) from 84.01%
25354925792

Pull #813

github

web-flow
Merge 101a9d8ab into 750468781
Pull Request #813: feat(nvblock): full constexpr support across all operators

10 of 10 new or added lines in 1 file covered. (100.0%)

36 existing lines in 2 files now uncovered.

45846 of 54591 relevant lines covered (83.98%)

6482208.49 hits per line

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

74.11
/include/sw/universal/number/microfloat/microfloat_impl.hpp
1
#pragma once
2
// microfloat_impl.hpp: definition of the microfloat number system for MX/OCP element types
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 <string>
9
#include <sstream>
10
#include <iostream>
11
#include <iomanip>
12
#include <cstdint>
13
#include <cstring>
14
#include <cmath>
15
#include <limits>
16
#include <type_traits>
17

18
#include <universal/utility/bit_cast.hpp>
19
#include <universal/number/shared/specific_value_encoding.hpp>
20
#include <universal/number/shared/nan_encoding.hpp>
21
#include <universal/number/shared/infinite_encoding.hpp>
22
#include <universal/number/microfloat/microfloat_fwd.hpp>
23
#include <universal/number/microfloat/exceptions.hpp>
24

25
namespace sw { namespace universal {
26

27
// microfloat: a lightweight floating-point type for MX/OCP block formats
28
// Template parameters:
29
//   _nbits       - total number of bits (4, 6, or 8)
30
//   _es          - number of exponent bits
31
//   _hasInf      - whether the type supports IEEE-like infinity
32
//   _hasNaN      - whether the type supports NaN encoding
33
//   _isSaturating - whether overflow saturates to maxpos/maxneg
34
template<unsigned _nbits, unsigned _es, bool _hasInf, bool _hasNaN, bool _isSaturating>
35
class microfloat {
36
        static_assert(_nbits <= 8, "microfloat is limited to 8 bits");
37
        static_assert(_es < _nbits, "exponent bits must be less than total bits");
38
        static_assert(_es >= 1, "need at least 1 exponent bit");
39

40
        // HELPER methods
41
        template<typename SignedInt,
42
                typename = typename std::enable_if< std::is_integral<SignedInt>::value, SignedInt >::type>
43
        constexpr microfloat& convert_signed(SignedInt v) noexcept {
21✔
44
                from_float(static_cast<float>(v));
21✔
45
                return *this;
21✔
46
        }
47
        template<typename UnsignedInt,
48
                typename = typename std::enable_if< std::is_integral<UnsignedInt>::value, UnsignedInt >::type>
49
        constexpr microfloat& convert_unsigned(UnsignedInt v) noexcept {
50
                from_float(static_cast<float>(v));
51
                return *this;
52
        }
53
        template<typename Real,
54
                typename = typename std::enable_if< std::is_floating_point<Real>::value, Real >::type>
55
        constexpr microfloat& convert_ieee754(Real rhs) noexcept {
537,335✔
56
                from_float(static_cast<float>(rhs));
537,335✔
57
                return *this;
537,335✔
58
        }
59

60
public:
61
        static constexpr unsigned nbits  = _nbits;
62
        static constexpr unsigned es     = _es;
63
        static constexpr unsigned fbits  = nbits - 1u - es;   // fraction bits (without hidden bit)
64
        static constexpr int      bias   = (1 << (es - 1)) - 1;
65
        static constexpr bool     hasInf = _hasInf;
66
        static constexpr bool     hasNaN = _hasNaN;
67
        static constexpr bool     isSaturating = _isSaturating;
68
        static constexpr uint8_t  bitmask = static_cast<uint8_t>((1u << nbits) - 1u);
69

70
        // derived constants
71
        static constexpr uint8_t  sign_mask = static_cast<uint8_t>(1u << (nbits - 1u));
72
        static constexpr uint8_t  exponent_mask = static_cast<uint8_t>(((1u << es) - 1u) << fbits);
73
        static constexpr uint8_t  fraction_mask = static_cast<uint8_t>((1u << fbits) - 1u);
74
        static constexpr unsigned max_exp_code = (1u << es) - 1u;
75

76
        microfloat() = default;
77

78
        constexpr microfloat(const microfloat&) = default;
79
        constexpr microfloat(microfloat&&) = default;
80

81
        constexpr microfloat& operator=(const microfloat&) = default;
82
        constexpr microfloat& operator=(microfloat&&) = default;
83

84
        // specific value constructor
85
        constexpr microfloat(const SpecificValue code) noexcept : _bits{} {
422✔
86
                switch (code) {
422✔
UNCOV
87
                case SpecificValue::infpos:
×
UNCOV
88
                        setinf(false);
×
UNCOV
89
                        break;
×
90
                case SpecificValue::maxpos:
422✔
91
                        maxpos();
422✔
92
                        break;
422✔
UNCOV
93
                case SpecificValue::minpos:
×
UNCOV
94
                        minpos();
×
UNCOV
95
                        break;
×
UNCOV
96
                case SpecificValue::zero:
×
97
                default:
UNCOV
98
                        setzero();
×
UNCOV
99
                        break;
×
UNCOV
100
                case SpecificValue::minneg:
×
UNCOV
101
                        minneg();
×
UNCOV
102
                        break;
×
UNCOV
103
                case SpecificValue::infneg:
×
UNCOV
104
                        setinf(true);
×
UNCOV
105
                        break;
×
UNCOV
106
                case SpecificValue::maxneg:
×
UNCOV
107
                        maxneg();
×
UNCOV
108
                        break;
×
UNCOV
109
                case SpecificValue::qnan:
×
110
                case SpecificValue::nar:
UNCOV
111
                        setnan(NAN_TYPE_QUIET);
×
UNCOV
112
                        break;
×
UNCOV
113
                case SpecificValue::snan:
×
UNCOV
114
                        setnan(NAN_TYPE_SIGNALLING);
×
UNCOV
115
                        break;
×
116
                }
117
        }
422✔
118

119
        // initializers for native types
120
        constexpr microfloat(signed char iv)                    noexcept : _bits{} { *this = iv; }
121
        constexpr microfloat(short iv)                          noexcept : _bits{} { *this = iv; }
122
        constexpr microfloat(int iv)                            noexcept : _bits{} { *this = iv; }
123
        constexpr microfloat(long iv)                           noexcept : _bits{} { *this = iv; }
124
        constexpr microfloat(long long iv)                      noexcept : _bits{} { *this = iv; }
125
        constexpr microfloat(char iv)                           noexcept : _bits{} { *this = iv; }
126
        constexpr microfloat(unsigned short iv)                 noexcept : _bits{} { *this = iv; }
127
        constexpr microfloat(unsigned int iv)                   noexcept : _bits{} { *this = iv; }
128
        constexpr microfloat(unsigned long iv)                  noexcept : _bits{} { *this = iv; }
129
        constexpr microfloat(unsigned long long iv)             noexcept : _bits{} { *this = iv; }
130
        constexpr explicit microfloat(float iv)                 noexcept : _bits{} { *this = iv; }
537,335✔
131
        constexpr explicit microfloat(double iv)                noexcept : _bits{} { *this = iv; }
132

133
        // assignment operators for native types
134
        constexpr microfloat& operator=(signed char rhs)        noexcept { return convert_signed(rhs); }
135
        constexpr microfloat& operator=(short rhs)              noexcept { return convert_signed(rhs); }
136
        constexpr microfloat& operator=(int rhs)                noexcept { return convert_signed(rhs); }
21✔
137
        constexpr microfloat& operator=(long rhs)               noexcept { return convert_signed(rhs); }
138
        constexpr microfloat& operator=(long long rhs)          noexcept { return convert_signed(rhs); }
139
        constexpr microfloat& operator=(char rhs)               noexcept { return convert_unsigned(rhs); }
140
        constexpr microfloat& operator=(unsigned short rhs)     noexcept { return convert_unsigned(rhs); }
141
        constexpr microfloat& operator=(unsigned int rhs)       noexcept { return convert_unsigned(rhs); }
142
        constexpr microfloat& operator=(unsigned long rhs)      noexcept { return convert_unsigned(rhs); }
143
        constexpr microfloat& operator=(unsigned long long rhs) noexcept { return convert_unsigned(rhs); }
144
        constexpr microfloat& operator=(float rhs)              noexcept { return convert_ieee754(rhs); }
537,335✔
145
        constexpr microfloat& operator=(double rhs)             noexcept { return convert_ieee754(rhs); }
146

147
        // conversion operators
148
        constexpr explicit operator float()                       const noexcept { return to_float(); }
1,074,197✔
149
        constexpr explicit operator double()                      const noexcept { return static_cast<double>(to_float()); }
1✔
150
        constexpr explicit operator signed char()                 const noexcept { return static_cast<signed char>(to_float()); }
1✔
151
        constexpr explicit operator short()                       const noexcept { return static_cast<short>(to_float()); }
1✔
152
        constexpr explicit operator int()                         const noexcept { return static_cast<int>(to_float()); }
153
        constexpr explicit operator long()                        const noexcept { return static_cast<long>(to_float()); }
1✔
154
        constexpr explicit operator long long()                   const noexcept { return static_cast<long long>(to_float()); }
1✔
155
        constexpr explicit operator char()                        const noexcept { return static_cast<char>(to_float()); }
1✔
156
        constexpr explicit operator unsigned short()              const noexcept { return static_cast<unsigned short>(to_float()); }
1✔
157
        constexpr explicit operator unsigned int()                const noexcept { return static_cast<unsigned int>(to_float()); }
158
        constexpr explicit operator unsigned long()               const noexcept { return static_cast<unsigned long>(to_float()); }
1✔
159
        constexpr explicit operator unsigned long long()          const noexcept { return static_cast<unsigned long long>(to_float()); }
1✔
160

161
#if LONG_DOUBLE_SUPPORT
162
        constexpr explicit microfloat(long double iv)           noexcept : _bits{} { *this = iv; }
163
        constexpr microfloat& operator=(long double rhs)        noexcept { return convert_ieee754(rhs); }
164
        constexpr explicit operator long double()                 const noexcept { return static_cast<long double>(to_float()); }
165
#endif
166

167
        // prefix operators
168
        constexpr microfloat operator-() const noexcept {
1✔
169
                microfloat tmp;
170
                tmp.setbits(_bits ^ sign_mask);
1✔
171
                return tmp;
1✔
172
        }
173

174
        constexpr microfloat& operator++() noexcept {
2✔
175
                // increment to the next encoding
176
                if (_bits & sign_mask) {
2✔
177
                        // negative
178
                        uint8_t magnitude = _bits & static_cast<uint8_t>(~sign_mask);
×
179
                        if (magnitude == 1u) {
×
180
                                _bits = 0; // go to +0
×
181
                        }
182
                        else if (magnitude > 0u) {
×
183
                                --_bits;
×
184
                        }
185
                        // if magnitude == 0 (negative zero), stay at zero
186
                }
187
                else {
188
                        // positive: increment unless at max encoding
189
                        uint8_t magnitude = _bits & static_cast<uint8_t>(~sign_mask);
2✔
190
                        uint8_t maxMagnitude = static_cast<uint8_t>(bitmask >> 1);
2✔
191
                        if (magnitude < maxMagnitude) {
2✔
192
                                ++_bits;
2✔
193
                        }
194
                }
195
                return *this;
2✔
196
        }
197
        constexpr microfloat operator++(int) noexcept {
198
                microfloat tmp(*this);
199
                operator++();
200
                return tmp;
201
        }
202
        constexpr microfloat& operator--() noexcept {
2✔
203
                if (_bits & sign_mask) {
2✔
204
                        // negative: increment magnitude
205
                        uint8_t magnitude = _bits & static_cast<uint8_t>(~sign_mask);
×
206
                        uint8_t maxMagnitude = static_cast<uint8_t>(bitmask >> 1);
×
207
                        if (magnitude < maxMagnitude) {
×
208
                                ++_bits;
×
209
                        }
210
                }
211
                else {
212
                        // positive
213
                        if (_bits == 0u) {
2✔
214
                                _bits = sign_mask | 0x01u; // go to minneg
×
215
                        }
216
                        else {
217
                                --_bits;
2✔
218
                        }
219
                }
220
                return *this;
2✔
221
        }
222
        constexpr microfloat operator--(int) noexcept {
1✔
223
                microfloat tmp(*this);
1✔
224
                operator--();
1✔
225
                return tmp;
1✔
226
        }
227

228
        // arithmetic operators
229
        constexpr microfloat& operator+=(const microfloat& rhs) {
134,469✔
230
                float result = to_float() + rhs.to_float();
134,469✔
231
                from_float(result);
134,469✔
232
                return *this;
134,469✔
233
        }
234
        constexpr microfloat& operator-=(const microfloat& rhs) {
134,469✔
235
                float result = to_float() - rhs.to_float();
134,469✔
236
                from_float(result);
134,469✔
237
                return *this;
134,469✔
238
        }
239
        constexpr microfloat& operator*=(const microfloat& rhs) {
134,469✔
240
                float result = to_float() * rhs.to_float();
134,469✔
241
                from_float(result);
134,469✔
242
                return *this;
134,469✔
243
        }
244
        constexpr microfloat& operator/=(const microfloat& rhs) {
133,177✔
245
                float result = to_float() / rhs.to_float();
133,177✔
246
                from_float(result);
133,177✔
247
                return *this;
133,177✔
248
        }
249

250
        // modifiers
251
        constexpr void clear() noexcept { _bits = 0; }
376✔
252
        constexpr void setzero() noexcept { clear(); }
10✔
253

254
        constexpr void setnan(int NaNType = NAN_TYPE_SIGNALLING) noexcept {
3✔
255
                if constexpr (hasNaN) {
256
                        if constexpr (nbits == 8 && es == 4) {
257
                                // e4m3: NaN encodings are 0x7F (positive) and 0xFF (negative)
258
                                // S.1111.111 pattern
259
                                _bits = (NaNType == NAN_TYPE_SIGNALLING) ? 0xFFu : 0x7Fu;
1✔
260
                        }
261
                        else {
262
                                // e5m2 (IEEE-like): all-ones exponent with non-zero fraction
263
                                // quiet NaN: fraction MSB = 1, signaling NaN: fraction MSB = 0 with non-zero fraction
264
                                if (NaNType == NAN_TYPE_SIGNALLING) {
2✔
265
                                        _bits = static_cast<uint8_t>(sign_mask | exponent_mask | 0x01u);
1✔
266
                                }
267
                                else {
268
                                        _bits = static_cast<uint8_t>(exponent_mask | (1u << (fbits - 1u)));
1✔
269
                                }
270
                        }
271
                }
272
                else {
273
                        // no NaN support: set to zero
UNCOV
274
                        _bits = 0;
×
275
                }
276
                _bits &= bitmask;
3✔
277
        }
3✔
278

279
        constexpr void setinf(bool sign = false) noexcept {
32,842✔
280
                if constexpr (hasInf) {
281
                        // e5m2 (IEEE-like): all-ones exponent, zero fraction
282
                        _bits = exponent_mask;
32,842✔
283
                        if (sign) _bits |= sign_mask;
32,842✔
284
                        _bits &= bitmask;
32,842✔
285
                }
286
                else if constexpr (isSaturating) {
287
                        // saturate to maxpos/maxneg
UNCOV
288
                        if (sign) maxneg(); else maxpos();
×
289
                }
290
                else {
291
                        _bits = 0;
292
                }
293
        }
32,842✔
294

295
        constexpr void setbit(unsigned i, bool v = true) noexcept {
2✔
296
                if (i < nbits) {
2✔
297
                        uint8_t bit = static_cast<uint8_t>(1u << i);
2✔
298
                        if (v) {
2✔
299
                                _bits |= bit;
1✔
300
                        }
301
                        else {
302
                                _bits &= static_cast<uint8_t>(~bit);
1✔
303
                        }
304
                        _bits &= bitmask;
2✔
305
                }
306
        }
2✔
307
        constexpr void setbits(unsigned value) noexcept { _bits = static_cast<uint8_t>(value & bitmask); }
551,294✔
308

309
        constexpr microfloat& minpos() noexcept { _bits = 0x01u; return *this; }
6✔
310

311
        constexpr microfloat& maxpos() noexcept {
1,183,045✔
312
                if constexpr (hasNaN && hasInf) {
313
                        // e5m2: all-ones exponent is Inf/NaN, so max normal = exponent_mask - 1 step
314
                        // max = S.11110.11 = 0x7B
315
                        _bits = static_cast<uint8_t>(exponent_mask - (1u << fbits) + fraction_mask + (1u << fbits));
498,230✔
316
                        // Actually: max normal for e5m2: exp=11110, frac=11 -> 0b0.11110.11 = 0x7B
317
                        _bits = static_cast<uint8_t>((max_exp_code - 1u) << fbits | fraction_mask);
498,230✔
318
                }
319
                else if constexpr (hasNaN && !hasInf) {
320
                        // e4m3: NaN is all-ones exp + all-ones fraction, max = all-ones exp + (fraction_mask - 1)
321
                        // max = 0b0.1111.110 = 0x7E
322
                        _bits = static_cast<uint8_t>(exponent_mask | (fraction_mask - 1u));
558,828✔
323
                }
324
                else {
325
                        // No NaN, no Inf: all encodings are valid numbers
326
                        // max = 0.111...1 (all bits except sign set)
327
                        _bits = static_cast<uint8_t>(bitmask >> 1);
125,987✔
328
                }
329
                return *this;
1,183,045✔
330
        }
331

332
        constexpr microfloat& zero() noexcept { _bits = 0x00u; return *this; }
333

334
        constexpr microfloat& minneg() noexcept { _bits = static_cast<uint8_t>(sign_mask | 0x01u); return *this; }
5✔
335

336
        constexpr microfloat& maxneg() noexcept {
25,087✔
337
                maxpos();
25,087✔
338
                _bits |= sign_mask;
25,087✔
339
                _bits &= bitmask;
25,087✔
340
                return *this;
25,087✔
341
        }
342

343
        // selectors
344
        constexpr bool iszero() const noexcept {
4,044,525✔
345
                return (_bits == 0x00u) || (_bits == sign_mask);
4,044,525✔
346
        }
347
        constexpr bool isone() const noexcept {
1✔
348
                // 1.0 = sign=0, exponent=bias, fraction=0
349
                uint8_t one_encoding = static_cast<uint8_t>(static_cast<unsigned>(bias) << fbits);
1✔
350
                return _bits == one_encoding;
1✔
351
        }
352
        constexpr bool isodd() const noexcept { return (_bits & 0x01u); }
353
        constexpr bool iseven() const noexcept { return !isodd(); }
354
        constexpr bool ispos() const noexcept { return !isneg(); }
355
        constexpr bool isneg() const noexcept { return (_bits & sign_mask) != 0; }
3,351,358✔
356

357
        constexpr bool isnan(int NaNType = NAN_TYPE_EITHER) const noexcept {
4,126,316✔
358
                if constexpr (!hasNaN) return false;
68,106✔
359

360
                if constexpr (nbits == 8 && es == 4 && !hasInf && hasNaN) {
361
                        // e4m3: NaN is S.1111.111 -> encodings 0x7F and 0xFF
362
                        bool isNaN = (_bits & 0x7Fu) == 0x7Fu;
2,075,390✔
363
                        if (NaNType == NAN_TYPE_EITHER) return isNaN;
2,075,390✔
364
                        if (NaNType == NAN_TYPE_SIGNALLING) return isNaN && ((_bits & sign_mask) != 0);
×
365
                        if (NaNType == NAN_TYPE_QUIET) return isNaN && ((_bits & sign_mask) == 0);
×
366
                        return false;
×
367
                }
368
                else {
369
                        // IEEE-like (e5m2): NaN = all-ones exponent + non-zero fraction
370
                        uint8_t exp = (_bits & exponent_mask);
1,982,820✔
371
                        uint8_t frac = (_bits & fraction_mask);
1,982,820✔
372
                        bool isNaN = (exp == exponent_mask) && (frac != 0);
1,982,820✔
373
                        if (NaNType == NAN_TYPE_EITHER) return isNaN;
1,982,820✔
374
                        bool isQuietNaN = isNaN && ((frac & (1u << (fbits - 1u))) != 0);
×
375
                        bool isSignalNaN = isNaN && ((frac & (1u << (fbits - 1u))) == 0);
×
376
                        if (NaNType == NAN_TYPE_QUIET) return isQuietNaN;
×
377
                        if (NaNType == NAN_TYPE_SIGNALLING) return isSignalNaN;
×
378
                        return false;
×
379
                }
380
        }
381

382
        constexpr bool isinf(int InfType = INF_TYPE_EITHER) const noexcept {
2,024,483✔
383
                if constexpr (!hasInf) return false;
293,450✔
384

385
                // IEEE-like: all-ones exponent + zero fraction
386
                uint8_t exp = (_bits & exponent_mask);
1,731,033✔
387
                uint8_t frac = (_bits & fraction_mask);
1,731,033✔
388
                bool inf = (exp == exponent_mask) && (frac == 0);
1,731,033✔
389
                if (!inf) return false;
1,731,033✔
390
                bool negative = isneg();
2,403✔
391
                if (InfType == INF_TYPE_EITHER) return true;
2,403✔
392
                if (InfType == INF_TYPE_NEGATIVE) return negative;
×
393
                if (InfType == INF_TYPE_POSITIVE) return !negative;
×
394
                return false;
×
395
        }
396

397
        constexpr bool   sign()   const noexcept { return isneg(); }
398
        constexpr int    scale()  const noexcept {
399
                int e = static_cast<int>((_bits & exponent_mask) >> fbits);
400
                return e - bias;
401
        }
402
        constexpr uint8_t bits()  const noexcept { return _bits; }
1,027,142✔
403

404
        constexpr bool test(unsigned bitIndex) const noexcept { return at(bitIndex); }
405
        constexpr bool at(unsigned bitIndex) const noexcept {
406
                if (bitIndex < nbits) {
407
                        return (_bits & (1u << bitIndex)) != 0;
408
                }
409
                return false;
410
        }
411
        constexpr uint8_t nibble(unsigned n) const noexcept {
412
                if (n < 2) {
413
                        return static_cast<uint8_t>((_bits >> (n * 4u)) & 0x0Fu);
414
                }
415
                return 0;
416
        }
417
        constexpr uint8_t exponent() const noexcept {
3,328,964✔
418
                return static_cast<uint8_t>((_bits & exponent_mask) >> fbits);
3,328,964✔
419
        }
420
        constexpr uint8_t fraction() const noexcept {
3,328,964✔
421
                return static_cast<uint8_t>(_bits & fraction_mask);
3,328,964✔
422
        }
423

424
        // Constexpr-safe ldexp emulation: x * 2^exp via power-of-2 multiplication.
425
        // For microfloat the |exp| range is small (at most |bias - 1|, which for
426
        // e5m2 is 14, for e4m3 is 6), so the linear loop is well-bounded.
427
        // Used only on the constexpr code path; runtime keeps std::ldexp.
428
        static constexpr float cx_ldexp(float x, int exp) noexcept {
×
429
                if (exp >= 0) {
×
430
                        for (int i = 0; i < exp; ++i) x *= 2.0f;
×
431
                }
432
                else {
433
                        for (int i = 0; i < -exp; ++i) x *= 0.5f;
×
434
                }
435
                return x;
×
436
        }
437

438
        // Convert to float.
439
        // Constexpr-safe via std::is_constant_evaluated() dispatch: at runtime
440
        // we use std::ldexp (fast, exact); at constant evaluation we substitute
441
        // cx_ldexp (a power-of-2 loop).  Both produce bit-identical results
442
        // because the 2.0/0.5 multiplications are exactly representable in
443
        // IEEE 754 float.
444
        constexpr float to_float() const noexcept {
3,348,957✔
445
                if (iszero()) return isneg() ? -0.0f : 0.0f;
3,348,957✔
446
                if constexpr (hasNaN) {
447
                        if (isnan()) return std::numeric_limits<float>::quiet_NaN();
3,037,979✔
448
                }
449
                if constexpr (hasInf) {
450
                        if (isinf()) {
1,482,031✔
451
                                return isneg() ? -std::numeric_limits<float>::infinity()
409✔
452
                                               :  std::numeric_limits<float>::infinity();
409✔
453
                        }
454
                }
455

456
                bool     s = isneg();
3,328,964✔
457
                unsigned e = exponent();
3,328,964✔
458
                unsigned f = fraction();
3,328,964✔
459

460
                float value;
461
                if (e == 0) {
3,328,964✔
462
                        // subnormal: value = (-1)^s * 2^(1-bias) * (0.fraction)
463
                        float frac = static_cast<float>(f) / static_cast<float>(1u << fbits);
105,666✔
464
                        if (std::is_constant_evaluated()) {
105,666✔
465
                                value = cx_ldexp(frac, 1 - bias);
×
466
                        }
467
                        else {
468
                                value = std::ldexp(frac, 1 - bias);
105,666✔
469
                        }
470
                }
471
                else {
472
                        // normal: value = (-1)^s * 2^(e-bias) * (1.fraction)
473
                        float frac = 1.0f + static_cast<float>(f) / static_cast<float>(1u << fbits);
3,223,298✔
474
                        if (std::is_constant_evaluated()) {
3,223,298✔
475
                                value = cx_ldexp(frac, static_cast<int>(e) - bias);
×
476
                        }
477
                        else {
478
                                value = std::ldexp(frac, static_cast<int>(e) - bias);
3,223,298✔
479
                        }
480
                }
481
                return s ? -value : value;
3,328,964✔
482
        }
483

484
        // Constexpr-safe extraction of an IEEE 754 float's sign / biased
485
        // exponent / fraction fields via sw::bit_cast.  Used only on the
486
        // constexpr code path; runtime uses std::signbit / std::frexp etc.
487
        struct float_fields { bool sign; int rawExp; uint32_t rawFrac; };
488
        static_assert(sizeof(float) == sizeof(uint32_t) && std::numeric_limits<float>::is_iec559,
489
                "microfloat constexpr conversion requires IEEE 754 binary32 float layout "
490
                "(1 sign / 8 exp / 23 fraction bits).  On platforms where float is not "
491
                "32-bit IEC 559 the constexpr path's bit-field positions would be wrong; "
492
                "the runtime path (std::frexp / std::signbit) remains portable.");
493
        static constexpr float_fields extract_float_fields(float v) noexcept {
×
494
                uint32_t bits_u = sw::bit_cast<uint32_t>(v);
×
495
                return float_fields{
496
                        (bits_u >> 31) != 0u,
×
497
                        static_cast<int>((bits_u >> 23) & 0xFFu),
×
498
                        bits_u & 0x7FFFFFu
×
499
                };
×
500
        }
501

502
        // Convert from float with RNE rounding.
503
        // Constexpr-safe via std::is_constant_evaluated() dispatch:
504
        //   * at runtime  -- std::signbit / std::isinf / std::frexp / std::ldexp
505
        //   * at constexpr -- IEEE 754 field extraction via sw::bit_cast,
506
        //                     plus cx_ldexp for the subnormal divisor
507
        // Both paths converge on the same RNE-rounded encoding.
508
        constexpr void from_float(float v) noexcept {
1,142,687✔
509
                if (v != v) { // NaN check
1,142,687✔
510
                        if constexpr (hasNaN) {
511
                                setnan(NAN_TYPE_QUIET);
×
512
                        }
513
                        else {
514
                                setzero();
×
515
                        }
516
                        return;
93,956✔
517
                }
518

519
                bool s;
520
                bool is_inf;
521
                if (std::is_constant_evaluated()) {
1,142,687✔
522
                        float_fields ff = extract_float_fields(v);
×
523
                        s = ff.sign;
×
524
                        is_inf = (ff.rawExp == 0xFF) && (ff.rawFrac == 0u);
×
525
                }
526
                else {
527
                        s = std::signbit(v);
1,142,687✔
528
                        is_inf = std::isinf(v);
1,142,687✔
529
                }
530
                if (s) v = -v;
1,142,687✔
531

532
                if (is_inf) {
1,142,687✔
533
                        if constexpr (hasInf) {
534
                                setinf(s);
2✔
535
                        }
536
                        else if constexpr (isSaturating) {
537
                                if (s) maxneg(); else maxpos();
×
538
                        }
539
                        else {
540
                                setzero();
541
                        }
542
                        return;
2✔
543
                }
544

545
                if (v == 0.0f) {
1,142,685✔
546
                        // Preserve the sign bit for signed zero -- microfloat models
547
                        // +0 / -0 distinctly (iszero() accepts both encodings, unary -
548
                        // flips the sign on zero, to_float() emits the matching float
549
                        // sign).  Pre-fix code unconditionally collapsed -0.0f to +0,
550
                        // breaking the round-trip contract.  CodeRabbit catch on PR #811.
551
                        _bits = s ? sign_mask : 0x00u;
10,597✔
552
                        return;
10,597✔
553
                }
554

555
                // Compute the maxpos value for clamping
556
                microfloat mp;
557
                mp.maxpos();
1,132,088✔
558
                float maxval = mp.to_float();
1,132,088✔
559

560
                if (v >= maxval) {
1,132,088✔
561
                        // Check if we need to round to maxpos or to inf
562
                        if constexpr (hasInf) {
563
                                // Compute the tie-point between maxpos and inf
564
                                // For IEEE-like types, values > maxval round to inf or stay at maxval
565
                                // The tie point is maxval + 0.5 ULP above maxval
566
                                // For simplicity with these small types: if v > maxval, go to inf
567
                                if (v > maxval) {
34,089✔
568
                                        setinf(s);
32,839✔
569
                                        return;
32,839✔
570
                                }
571
                        }
572
                        if constexpr (isSaturating) {
573
                                if (s) maxneg(); else maxpos();
49,268✔
574
                                return;
49,268✔
575
                        }
576
                        // non-saturating without inf: clamp to max
577
                        if (s) maxneg(); else maxpos();
1,250✔
578
                        return;
1,250✔
579
                }
580

581
                // Extract exponent and fraction from the float value.
582
                // frexp returns frac in [0.5, 1.0), exp such that v = frac * 2^exp.
583
                // At constant evaluation we substitute IEEE 754 bit-extraction,
584
                // which yields the same (frac, exp) for normal floats:
585
                //   frac    = (1 + rawFrac/2^23) / 2     in [0.5, 1)
586
                //   exp     = rawExp - 127 + 1            (frexp adds 1 to make
587
                //                                          frac < 1)
588
                // For subnormal floats the bit-extraction would need a leading-zero
589
                // scan; subnormals here are within microfloat's subnormal range only
590
                // when the source float is itself subnormal -- a rare path.  We
591
                // route the constexpr subnormal-float case through the existing
592
                // ldexp-based subnormal branch by computing exp via the raw
593
                // exponent and frac via shifted mantissa, exactly as the runtime
594
                // std::frexp does.
595
                int exp;
596
                float frac;
597
                if (std::is_constant_evaluated()) {
1,048,731✔
598
                        float_fields ff = extract_float_fields(v);
×
599
                        if (ff.rawExp == 0) {
×
600
                                // Source is a subnormal float.  Reconstruct via cx_ldexp
601
                                // to match std::frexp semantics: find the leading 1 of
602
                                // rawFrac, shift to align as 0.5, set exp accordingly.
603
                                // rawFrac > 0 here (zero already handled).
604
                                int leading = 22;  // top fraction bit index
×
605
                                while (leading >= 0 && ((ff.rawFrac >> leading) & 1u) == 0u) --leading;
×
606
                                int shift = 22 - leading;
×
607
                                uint32_t aligned = ff.rawFrac << shift;
×
608
                                // aligned now has its top bit at position 22, representing
609
                                // the implicit leading 1 of a frexp-style mantissa.
610
                                // frac = aligned / 2^23 + 0.5; equivalently aligned has
611
                                // bit 22 set, lower 22 bits are the fractional part.
612
                                // Construct frac directly via float bit pattern: exp_field=126
613
                                // (which gives 2^-1 = 0.5), mantissa = aligned bits 21..0
614
                                // shifted into the 23-bit field.
615
                                uint32_t frac_bits = (uint32_t(126) << 23) | (aligned & 0x7FFFFFu);
×
616
                                frac = sw::bit_cast<float>(frac_bits);
×
617
                                exp = -126 - shift + 1;  // frexp's exp for subnormal source
×
618
                        }
619
                        else {
620
                                // Normal float: frexp(v) = ((1 + rawFrac/2^23) / 2, rawExp - 127 + 1)
621
                                // Construct frac with exp_field=126 (-> 2^-1 = 0.5 base),
622
                                // mantissa = rawFrac.  This equals (1 + rawFrac/2^23)/2
623
                                // bit-exactly.
624
                                uint32_t frac_bits = (uint32_t(126) << 23) | ff.rawFrac;
×
625
                                frac = sw::bit_cast<float>(frac_bits);
×
626
                                exp = ff.rawExp - 127 + 1;
×
627
                        }
628
                }
629
                else {
630
                        frac = std::frexp(v, &exp);
1,048,731✔
631
                }
632
                // We want: v = 1.mantissa * 2^(exp-1)
633
                // so our biased_exp = exp - 1 + bias
634
                exp -= 1; // now v = (2*frac) * 2^exp, and 2*frac in [1.0, 2.0)
1,048,731✔
635
                float significand = 2.0f * frac; // in [1.0, 2.0)
1,048,731✔
636

637
                int biased_exp = exp + bias;
1,048,731✔
638

639
                if (biased_exp <= 0) {
1,048,731✔
640
                        // Subnormal range
641
                        // subnormal: v = f * 2^(1-bias) where f = 0.mantissa in [0, 1)
642
                        float subnormal_divisor;
643
                        if (std::is_constant_evaluated()) {
94,693✔
644
                                subnormal_divisor = cx_ldexp(1.0f, 1 - bias);
×
645
                        }
646
                        else {
647
                                subnormal_divisor = std::ldexp(1.0f, 1 - bias);
94,693✔
648
                        }
649
                        float subnormal_frac = v / subnormal_divisor;
94,693✔
650
                        // subnormal_frac is in [0, 1)
651
                        // Quantize to fbits bits with RNE
652
                        float scaled = subnormal_frac * static_cast<float>(1u << fbits);
94,693✔
653
                        unsigned f_int = rne_round(scaled);
94,693✔
654
                        if (f_int >= (1u << fbits)) {
94,693✔
655
                                // Rounded up to smallest normal
656
                                biased_exp = 1;
2,683✔
657
                                f_int = 0;
2,683✔
658
                                _bits = static_cast<uint8_t>((static_cast<unsigned>(biased_exp) << fbits) | f_int);
2,683✔
659
                        }
660
                        else {
661
                                _bits = static_cast<uint8_t>(f_int);
92,010✔
662
                        }
663
                }
664
                else {
665
                        // Normal range
666
                        // significand is in [1.0, 2.0), we need the fractional part
667
                        float mantissa = significand - 1.0f; // in [0, 1)
954,038✔
668
                        float scaled = mantissa * static_cast<float>(1u << fbits);
954,038✔
669
                        unsigned f_int = rne_round(scaled);
954,038✔
670
                        if (f_int >= (1u << fbits)) {
954,038✔
671
                                // Carry into exponent
672
                                f_int = 0;
58,281✔
673
                                biased_exp += 1;
58,281✔
674
                        }
675
                        // Check for overflow after rounding
676
                        if constexpr (hasNaN && hasInf) {
677
                                // e5m2: max biased exp for normal = max_exp_code - 1
678
                                if (static_cast<unsigned>(biased_exp) >= max_exp_code) {
428,890✔
679
                                        setinf(s);
×
680
                                        return;
×
681
                                }
682
                        }
683
                        else if constexpr (hasNaN && !hasInf) {
684
                                // e4m3: max biased exp = max_exp_code, but all-ones exp + all-ones frac = NaN
685
                                if (static_cast<unsigned>(biased_exp) > max_exp_code) {
441,676✔
686
                                        if (s) maxneg(); else maxpos();
×
687
                                        return;
×
688
                                }
689
                                if (static_cast<unsigned>(biased_exp) == max_exp_code && f_int >= fraction_mask) {
441,676✔
690
                                        if (s) maxneg(); else maxpos();
×
691
                                        return;
×
692
                                }
693
                        }
694
                        else {
695
                                // No NaN, no Inf: all encodings valid
696
                                if (static_cast<unsigned>(biased_exp) > max_exp_code) {
83,472✔
697
                                        if (s) maxneg(); else maxpos();
×
698
                                        return;
×
699
                                }
700
                        }
701
                        _bits = static_cast<uint8_t>((static_cast<unsigned>(biased_exp) << fbits) | f_int);
954,038✔
702
                }
703

704
                if (s) _bits |= sign_mask;
1,048,731✔
705
                _bits &= bitmask;
1,048,731✔
706
        }
707

708
protected:
709
        uint8_t _bits;
710

711
private:
712
        // Round-to-nearest-even helper
713
        static constexpr unsigned rne_round(float v) noexcept {
1,048,731✔
714
                unsigned truncated = static_cast<unsigned>(v);
1,048,731✔
715
                float remainder = v - static_cast<float>(truncated);
1,048,731✔
716
                if (remainder > 0.5f) return truncated + 1u;
1,048,731✔
717
                if (remainder < 0.5f) return truncated;
703,051✔
718
                // Exactly 0.5: round to even
719
                return (truncated & 1u) ? truncated + 1u : truncated;
73,349✔
720
        }
721

722
        // microfloat - microfloat logic comparisons
723
        template<unsigned n, unsigned e, bool i, bool na, bool s>
724
        friend constexpr bool operator==(microfloat<n,e,i,na,s> lhs, microfloat<n,e,i,na,s> rhs);
725

726
        // microfloat - literal logic comparisons
727
        template<unsigned n, unsigned e, bool i, bool na, bool s>
728
        friend constexpr bool operator==(microfloat<n,e,i,na,s> lhs, float rhs);
729

730
        // literal - microfloat logic comparisons
731
        template<unsigned n, unsigned e, bool i, bool na, bool s>
732
        friend constexpr bool operator==(float lhs, microfloat<n,e,i,na,s> rhs);
733
};
734

735
////////////////////////    functions   /////////////////////////////////
736

737
template<unsigned n, unsigned e, bool i, bool na, bool s>
738
inline constexpr microfloat<n,e,i,na,s> abs(microfloat<n,e,i,na,s> a) {
739
        return (a.isneg() ? -a : a);
740
}
741

742
/// stream operators
743

744
template<unsigned n, unsigned e, bool i, bool na, bool s>
745
inline std::ostream& operator<<(std::ostream& ostr, microfloat<n,e,i,na,s> mf) {
98✔
746
        return ostr << float(mf);
98✔
747
}
748

749
template<unsigned n, unsigned e, bool i, bool na, bool s>
750
inline std::istream& operator>>(std::istream& istr, microfloat<n,e,i,na,s>& p) {
751
        float f;
752
        istr >> f;
753
        p = microfloat<n,e,i,na,s>(f);
754
        return istr;
755
}
756

757
////////////////// string operators
758

759
template<unsigned nbits, unsigned es, bool hasInf, bool hasNaN, bool isSaturating>
760
inline std::string to_binary(microfloat<nbits, es, hasInf, hasNaN, isSaturating> mf, bool bNibbleMarker = false) {
250✔
761
        constexpr unsigned fbits = nbits - 1u - es;
250✔
762
        std::stringstream ss;
250✔
763
        uint8_t bits = mf.bits();
250✔
764
        uint8_t mask = static_cast<uint8_t>(1u << (nbits - 1u));
250✔
765

766
        ss << (bits & mask ? "0b1." : "0b0.");
250✔
767
        mask >>= 1;
250✔
768
        // exponent bits
769
        for (unsigned j = 0; j < es; ++j) {
964✔
770
                if (bNibbleMarker && j > 0 && (j % 4) == 0) ss << '\'';
714✔
771
                ss << ((bits & mask) ? '1' : '0');
714✔
772
                mask >>= 1;
714✔
773
        }
774
        ss << '.';
250✔
775
        // fraction bits
776
        for (unsigned j = 0; j < fbits; ++j) {
838✔
777
                if (bNibbleMarker && j > 0 && (j % 4) == 0) ss << '\'';
588✔
778
                ss << ((bits & mask) ? '1' : '0');
588✔
779
                mask >>= 1;
588✔
780
        }
781
        return ss.str();
500✔
782
}
250✔
783

784
// native semantic representation: radix-2, delegates to to_binary
785
template<unsigned nbits, unsigned es, bool hasInf, bool hasNaN, bool isSaturating>
786
inline std::string to_native(microfloat<nbits, es, hasInf, hasNaN, isSaturating> mf, bool nibbleMarker = false) {
787
        return to_binary(mf, nibbleMarker);
788
}
789

790
//////////////////////////////////////////////////////////////////////////////////////////////////////
791
// microfloat - microfloat binary logic operators
792

793
template<unsigned n, unsigned e, bool i, bool na, bool s>
794
inline constexpr bool operator==(microfloat<n,e,i,na,s> lhs, microfloat<n,e,i,na,s> rhs) {
44✔
795
        if (lhs.isnan() || rhs.isnan()) return false;
44✔
796
        // +0 == -0
797
        if (lhs.iszero() && rhs.iszero()) return true;
40✔
798
        return lhs._bits == rhs._bits;
35✔
799
}
800

801
template<unsigned n, unsigned e, bool i, bool na, bool s>
802
inline constexpr bool operator!=(microfloat<n,e,i,na,s> lhs, microfloat<n,e,i,na,s> rhs) {
13✔
803
        return !operator==(lhs, rhs);
13✔
804
}
805

806
template<unsigned n, unsigned e, bool i, bool na, bool s>
807
inline constexpr bool operator<(microfloat<n,e,i,na,s> lhs, microfloat<n,e,i,na,s> rhs) {
52✔
808
        if (lhs.isnan() || rhs.isnan()) return false;
52✔
809
        return (float(lhs) - float(rhs)) < 0;
48✔
810
}
811

812
template<unsigned n, unsigned e, bool i, bool na, bool s>
813
inline constexpr bool operator>(microfloat<n,e,i,na,s> lhs, microfloat<n,e,i,na,s> rhs) {
26✔
814
        return operator<(rhs, lhs);
26✔
815
}
816

817
template<unsigned n, unsigned e, bool i, bool na, bool s>
818
inline constexpr bool operator<=(microfloat<n,e,i,na,s> lhs, microfloat<n,e,i,na,s> rhs) {
12✔
819
        return operator<(lhs, rhs) || operator==(lhs, rhs);
12✔
820
}
821

822
template<unsigned n, unsigned e, bool i, bool na, bool s>
823
inline constexpr bool operator>=(microfloat<n,e,i,na,s> lhs, microfloat<n,e,i,na,s> rhs) {
12✔
824
        // Cannot just be `!operator<`: operator< returns false for any NaN
825
        // operand, so negating it would yield true for NaN >= x.  Build from
826
        // operator> and operator==, both of which return false on NaN.
827
        return operator>(lhs, rhs) || operator==(lhs, rhs);
12✔
828
}
829

830
//////////////////////////////////////////////////////////////////////////////////////////////////////
831
// microfloat - literal binary logic operators
832
//
833
// All overloads short-circuit a float NaN operand BEFORE narrowing to
834
// microfloat.  For instantiations with hasNaN == false, from_float(NaN)
835
// collapses to zero, which would erase the IEEE 754 NaN-comparison
836
// contract (the "all relational ops false / != true" rule) -- mf == NaN
837
// would silently become mf == 0.  CodeRabbit catch on PR #811.
838

839
template<unsigned n, unsigned e, bool i, bool na, bool s>
840
inline constexpr bool operator==(microfloat<n,e,i,na,s> lhs, float rhs) {
841
        if (rhs != rhs) return false;  // float NaN: == always false
842
        return operator==(lhs, microfloat<n,e,i,na,s>(rhs));
843
}
844

845
template<unsigned n, unsigned e, bool i, bool na, bool s>
846
inline constexpr bool operator!=(microfloat<n,e,i,na,s> lhs, float rhs) {
847
        if (rhs != rhs) return true;   // float NaN: != always true
848
        return !operator==(lhs, microfloat<n,e,i,na,s>(rhs));
849
}
850

851
template<unsigned n, unsigned e, bool i, bool na, bool s>
852
inline constexpr bool operator<(microfloat<n,e,i,na,s> lhs, float rhs) {
853
        if (rhs != rhs) return false;  // float NaN: ordering always false
854
        return operator<(lhs, microfloat<n,e,i,na,s>(rhs));
855
}
856

857
template<unsigned n, unsigned e, bool i, bool na, bool s>
858
inline constexpr bool operator>(microfloat<n,e,i,na,s> lhs, float rhs) {
859
        if (rhs != rhs) return false;
860
        return operator<(microfloat<n,e,i,na,s>(rhs), lhs);
861
}
862

863
template<unsigned n, unsigned e, bool i, bool na, bool s>
864
inline constexpr bool operator<=(microfloat<n,e,i,na,s> lhs, float rhs) {
865
        if (rhs != rhs) return false;
866
        return operator<(lhs, microfloat<n,e,i,na,s>(rhs)) || operator==(lhs, microfloat<n,e,i,na,s>(rhs));
867
}
868

869
template<unsigned n, unsigned e, bool i, bool na, bool s>
870
inline constexpr bool operator>=(microfloat<n,e,i,na,s> lhs, float rhs) {
871
        if (rhs != rhs) return false;
872
        // NaN-safe via operator> + operator== (see microfloat-microfloat
873
        // operator>= for the !operator< trap rationale).
874
        return operator>(lhs, microfloat<n,e,i,na,s>(rhs)) || operator==(lhs, microfloat<n,e,i,na,s>(rhs));
875
}
876

877
//////////////////////////////////////////////////////////////////////////////////////////////////////
878
// literal - microfloat binary logic operators (symmetric NaN-safety)
879

880
template<unsigned n, unsigned e, bool i, bool na, bool s>
881
inline constexpr bool operator==(float lhs, microfloat<n,e,i,na,s> rhs) {
882
        if (lhs != lhs) return false;
883
        return operator==(microfloat<n,e,i,na,s>(lhs), rhs);
884
}
885

886
template<unsigned n, unsigned e, bool i, bool na, bool s>
887
inline constexpr bool operator!=(float lhs, microfloat<n,e,i,na,s> rhs) {
888
        if (lhs != lhs) return true;
889
        return !operator==(microfloat<n,e,i,na,s>(lhs), rhs);
890
}
891

892
template<unsigned n, unsigned e, bool i, bool na, bool s>
893
inline constexpr bool operator<(float lhs, microfloat<n,e,i,na,s> rhs) {
894
        if (lhs != lhs) return false;
895
        return operator<(microfloat<n,e,i,na,s>(lhs), rhs);
896
}
897

898
template<unsigned n, unsigned e, bool i, bool na, bool s>
899
inline constexpr bool operator>(float lhs, microfloat<n,e,i,na,s> rhs) {
900
        if (lhs != lhs) return false;
901
        return operator<(rhs, microfloat<n,e,i,na,s>(lhs));
902
}
903

904
template<unsigned n, unsigned e, bool i, bool na, bool s>
905
inline constexpr bool operator<=(float lhs, microfloat<n,e,i,na,s> rhs) {
906
        if (lhs != lhs) return false;
907
        return operator<(microfloat<n,e,i,na,s>(lhs), rhs) || operator==(microfloat<n,e,i,na,s>(lhs), rhs);
908
}
909

910
template<unsigned n, unsigned e, bool i, bool na, bool s>
911
inline constexpr bool operator>=(float lhs, microfloat<n,e,i,na,s> rhs) {
912
        if (lhs != lhs) return false;
913
        // NaN-safe (see microfloat-microfloat operator>= for the rationale).
914
        return operator>(microfloat<n,e,i,na,s>(lhs), rhs) || operator==(microfloat<n,e,i,na,s>(lhs), rhs);
915
}
916

917
//////////////////////////////////////////////////////////////////////////////////////////////////////
918
// microfloat - microfloat binary arithmetic operators
919

920
template<unsigned n, unsigned e, bool i, bool na, bool s>
921
inline constexpr microfloat<n,e,i,na,s> operator+(microfloat<n,e,i,na,s> lhs, microfloat<n,e,i,na,s> rhs) {
134,469✔
922
        microfloat<n,e,i,na,s> sum = lhs;
134,469✔
923
        sum += rhs;
134,469✔
924
        return sum;
134,469✔
925
}
926

927
template<unsigned n, unsigned e, bool i, bool na, bool s>
928
inline constexpr microfloat<n,e,i,na,s> operator-(microfloat<n,e,i,na,s> lhs, microfloat<n,e,i,na,s> rhs) {
134,469✔
929
        microfloat<n,e,i,na,s> diff = lhs;
134,469✔
930
        diff -= rhs;
134,469✔
931
        return diff;
134,469✔
932
}
933

934
template<unsigned n, unsigned e, bool i, bool na, bool s>
935
inline constexpr microfloat<n,e,i,na,s> operator*(microfloat<n,e,i,na,s> lhs, microfloat<n,e,i,na,s> rhs) {
134,469✔
936
        microfloat<n,e,i,na,s> mul = lhs;
134,469✔
937
        mul *= rhs;
134,469✔
938
        return mul;
134,469✔
939
}
940

941
template<unsigned n, unsigned e, bool i, bool na, bool s>
942
inline constexpr microfloat<n,e,i,na,s> operator/(microfloat<n,e,i,na,s> lhs, microfloat<n,e,i,na,s> rhs) {
133,177✔
943
        microfloat<n,e,i,na,s> ratio = lhs;
133,177✔
944
        ratio /= rhs;
133,177✔
945
        return ratio;
133,177✔
946
}
947

948
//////////////////////////////////////////////////////////////////////////////////////////////////////
949
// microfloat - literal binary arithmetic operators
950

951
template<unsigned n, unsigned e, bool i, bool na, bool s>
952
inline constexpr microfloat<n,e,i,na,s> operator+(microfloat<n,e,i,na,s> lhs, float rhs) {
953
        return operator+(lhs, microfloat<n,e,i,na,s>(rhs));
954
}
955

956
template<unsigned n, unsigned e, bool i, bool na, bool s>
957
inline constexpr microfloat<n,e,i,na,s> operator-(microfloat<n,e,i,na,s> lhs, float rhs) {
958
        return operator-(lhs, microfloat<n,e,i,na,s>(rhs));
959
}
960

961
template<unsigned n, unsigned e, bool i, bool na, bool s>
962
inline constexpr microfloat<n,e,i,na,s> operator*(microfloat<n,e,i,na,s> lhs, float rhs) {
963
        return operator*(lhs, microfloat<n,e,i,na,s>(rhs));
964
}
965

966
template<unsigned n, unsigned e, bool i, bool na, bool s>
967
inline constexpr microfloat<n,e,i,na,s> operator/(microfloat<n,e,i,na,s> lhs, float rhs) {
968
        return operator/(lhs, microfloat<n,e,i,na,s>(rhs));
969
}
970

971
//////////////////////////////////////////////////////////////////////////////////////////////////////
972
// literal - microfloat binary arithmetic operators
973

974
template<unsigned n, unsigned e, bool i, bool na, bool s>
975
inline constexpr microfloat<n,e,i,na,s> operator+(float lhs, microfloat<n,e,i,na,s> rhs) {
976
        return operator+(microfloat<n,e,i,na,s>(lhs), rhs);
977
}
978

979
template<unsigned n, unsigned e, bool i, bool na, bool s>
980
inline constexpr microfloat<n,e,i,na,s> operator-(float lhs, microfloat<n,e,i,na,s> rhs) {
981
        return operator-(microfloat<n,e,i,na,s>(lhs), rhs);
982
}
983

984
template<unsigned n, unsigned e, bool i, bool na, bool s>
985
inline constexpr microfloat<n,e,i,na,s> operator*(float lhs, microfloat<n,e,i,na,s> rhs) {
986
        return operator*(microfloat<n,e,i,na,s>(lhs), rhs);
987
}
988

989
template<unsigned n, unsigned e, bool i, bool na, bool s>
990
inline constexpr microfloat<n,e,i,na,s> operator/(float lhs, microfloat<n,e,i,na,s> rhs) {
991
        return operator/(microfloat<n,e,i,na,s>(lhs), rhs);
992
}
993

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