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

stillwater-sc / universal / 25959866834

16 May 2026 10:27AM UTC coverage: 83.953% (+0.007%) from 83.946%
25959866834

Pull #838

github

web-flow
Merge e96fa0568 into 825310924
Pull Request #838: feat(utility): constexpr string-parse primitives (Phase A of #835)

46033 of 54832 relevant lines covered (83.95%)

6383955.66 hits per line

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

93.48
/include/sw/universal/number/cfloat/cfloat_impl.hpp
1
#pragma once
2
// cfloat_impl.hpp: implementation of an arbitrary configuration fixed-size 'classic' floating-point representation
3
// cfloat<> can emulate IEEE-754 floats and the new Deep Learning types, such as 
4
// IEEE-754 half-precision floats
5
// Google bfloat16
6
// NVIDIA TensorFloat 
7
// AMD FP16 and FP32
8
// Microsoft FP8 and FP9
9
// Tesla CFP8, CFP16
10
// 
11
// cfloat<> can also emulate more precise configurations, such as
12
// 80bit IEEE-754 extended precision floats
13
// true 128bit quad precision floats
14
//
15
// Copyright (C) 2017 Stillwater Supercomputing, Inc.
16
// SPDX-License-Identifier: MIT
17
//
18
// This file is part of the universal numbers project, which is released under an MIT Open Source license.
19

20
// compiler specific environment has been delegated to be handled by the
21
// number system include file <universal/number/cfloat/cfloat.hpp>
22
// 
23
// supporting types and functions
24
#include <limits>
25
#include <regex>
26
#include <type_traits>
27
#include <universal/native/ieee754.hpp>
28
#include <universal/native/subnormal.hpp>
29
#include <universal/utility/find_msb.hpp>
30
#include <universal/number/shared/nan_encoding.hpp>
31
#include <universal/number/shared/infinite_encoding.hpp>
32
#include <universal/number/shared/specific_value_encoding.hpp>
33
// arithmetic tracing options
34
#include <universal/number/algorithm/trace_constants.hpp>
35
// cfloat exception structure
36
#include <universal/number/cfloat/exceptions.hpp>
37
// composition types used by cfloat
38
#include <universal/internal/blockbinary/blockbinary.hpp>
39
#include <universal/internal/blocktriple/blocktriple.hpp>
40
#include <universal/number/support/decimal.hpp>
41

42
#ifndef CFLOAT_THROW_ARITHMETIC_EXCEPTION
43
#define CFLOAT_THROW_ARITHMETIC_EXCEPTION 0
44
#endif
45

46
#ifndef TRACE_CONVERSION
47
#define TRACE_CONVERSION 0
48
#endif
49

50
namespace sw { namespace universal {
51

52
/*
53
 * classic floating-point cfloat offers subnormals, max-exponent values, and
54
 * saturation. The default configuration turns off subnormals, max-exponent
55
 * values, and projects values outside of their dynamic range to +-inf
56
 *
57
 * Behavior flags
58
 *   hasSubnormals   : gradual underflow: use all fraction encodings when exponent is all 0's
59
 *   hasMaxExpValues  : reclaim max-exponent encodings as numeric values instead of
60
 *                      reserving the entire all-1s exponent binade for inf/NaN;
61
 *                      only 4 encodings are reserved for +-inf and quiet/signalling NaN
62
 *   isSaturating     : saturate to maxneg or maxpos when value is out of dynamic range
63
 */
64

65
/// <summary>
66
/// decode an cfloat value into its constituent parts
67
/// </summary>
68
/// <typeparam name="bt">block type</typeparam>
69
/// <param name="v">cfloat value to decode (input: const ref)</param>
70
/// <param name="s">sign (output: bool ref)</param>
71
/// <param name="e">exponent (output: blockbinary ref)</param>
72
/// <param name="f">fraction (output: blockbinary ref)</param>
73
template<unsigned nbits, unsigned es, unsigned fbitsPlus1, typename bt,
74
        bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
75
void decode(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& v, bool& s, blockbinary<es, bt>& e, blockbinary<fbitsPlus1, bt>& f) {
800✔
76
        v.sign(s);
800✔
77
        v.exponent(e);
800✔
78
        v.fraction(f);
800✔
79
}
800✔
80

81
/// <summary>
82
/// return the binary scale of the given number
83
/// </summary>
84
/// <typeparam name="bt">Block type used for storage: derived through ADL</typeparam>
85
/// <param name="v">the cfloat number for which we seek to know the binary scale</param>
86
/// <returns>binary scale, i.e. 2^scale, of the value of the cfloat</returns>
87
template<unsigned nbits, unsigned es, typename bt,
88
        bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
89
int scale(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& v) {
329✔
90
        return v.scale();
329✔
91
}
92

93
/// <summary>
94
/// convert a blocktriple to a cfloat. blocktriples come out of the arithmetic
95
/// engine in the form ii.ff...ff and a scale. The conversion must take this
96
/// denormalized form into account during conversion.
97
/// 
98
/// The blocktriple must be in this form to round correctly, as all the bits
99
/// after an arithmetic operation must be taken into account.
100
/// 
101
/// Transformation:
102
///    ii.ff...ff  transform to    s.eee.fffff
103
/// All number systems that depend on blocktriple will need to have
104
/// the rounding decision answered, so that functionality can be
105
/// reused if we locate it inside blocktriple.
106
/// 
107
/// if (srcbits > fbits) // we need to round
108
///     if (ii.00..00 > 1) 
109
///         mask is at srcbits - fbits + 1
110
///     else 
111
///                    mask is at srcbits - fbits
112
/// }
113
/// else {               // no need to round
114
/// }
115
/// </summary>
116
/// <typeparam name="bt">type of the block used for cfloat storage</typeparam>
117
/// <param name="src">the blocktriple to be converted</param>
118
/// <param name="tgt">the resulting cfloat</param>
119
template<unsigned srcbits, BlockTripleOperator op, unsigned nbits, unsigned es, typename bt,
120
        bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
121
constexpr inline void convert(const blocktriple<srcbits, op, bt>& src, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& tgt) {
78,740,161✔
122
        using btType = blocktriple<srcbits, op, bt>;
123
        using cfloatType = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
124
        // test special cases
125
        if (src.isnan()) {
78,740,161✔
126
                tgt.setnan(src.sign() ? NAN_TYPE_SIGNALLING : NAN_TYPE_QUIET);
560✔
127
        }
128
        else        if (src.isinf()) {
78,739,601✔
129
                tgt.setinf(src.sign());
560✔
130
        }
131
        else         if (src.iszero()) {
78,739,041✔
132
                tgt.setzero();
46,483✔
133
                tgt.setsign(src.sign()); // preserve sign
46,483✔
134
        }
135
        else {
136
                int significandScale = src.significandscale();
78,692,558✔
137
                int exponent = src.scale() + significandScale;
78,692,558✔
138
                // blocktriple keeps arithmetic results intentionally unnormalized. scale() tracks the source binade,
139
                // while significandScale() reports whether the exact result spilled into the integer headroom above
140
                // the radix. cfloat conversion has to combine both before it can classify the value as normal,
141
                // subnormal, or overflowing.
142
                // special case of underflow
143
                if constexpr (hasSubnormals) {
144
//                        std::cout << "exponent = " << exponent << " bias = " << cfloatType::EXP_BIAS << " exp subnormal = " << cfloatType::MIN_EXP_SUBNORMAL << '\n';
145
                        // why must exponent be less than (minExpSubnormal - 1) to be rounded to zero? 
146
                        // because the half-way value that would round up to minpos is at exp = (minExpSubnormal - 1)
147
                        if (exponent < cfloatType::MIN_EXP_SUBNORMAL) {
55,340,780✔
148
                                tgt.setzero();
183,217✔
149
                                if (exponent == (cfloatType::MIN_EXP_SUBNORMAL - 1)) {
183,217✔
150
                                        // -exponent because we are right shifting and exponent in this range is negative
151
                                        int adjustment = -(exponent + subnormal_reciprocal_shift[es]);
39,881✔
152
                                        std::pair<bool, unsigned> alignment = src.roundingDecision(adjustment);
39,881✔
153
                                        if (alignment.first) ++tgt; // we are minpos
39,881✔
154
                                }
155
                                tgt.setsign(src.sign());
183,217✔
156
                                return;
3,108,569✔
157
                        }
158
                }
159
                else {
160
                        if (exponent + cfloatType::EXP_BIAS <= 0) {  // value is in the subnormal range, which maps to 0
23,351,778✔
161
                                tgt.setzero();
363,535✔
162
                                tgt.setsign(src.sign());
363,535✔
163
                                return;
1,241,229✔
164
                        }
165
                }
166
                // special case of overflow
167
                if constexpr (hasMaxExpValues) {
168
                        if constexpr (isSaturating) {
169
                                if (exponent > cfloatType::MAX_EXP) {
21,098,841✔
170
                                        if (src.sign()) tgt.maxneg(); else tgt.maxpos();
934,372✔
171
                                        return;
934,372✔
172
                                }
173
                        }
174
                        else {
175
                                if (exponent > cfloatType::MAX_EXP) {
30,556,129✔
176
                                        tgt.setinf(src.sign());
2,673,424✔
177
                                        return;
2,673,424✔
178
                                }
179
                        }
180
                }
181
                else {  // no max-exponent values: will saturate at a different encoding: TODO can we hide it all in maxpos?
182
                        if constexpr (isSaturating) {
183
                                if (exponent > cfloatType::MAX_EXP) {
3,705✔
184
                                        if (src.sign()) tgt.maxneg(); else tgt.maxpos();
6✔
185
                                        return;
6✔
186
                                }
187
                        }
188
                        else {
189
                                if (exponent > cfloatType::MAX_EXP) {
26,487,131✔
190
                                        tgt.setinf(src.sign());
195,244✔
191
                                        return;
195,244✔
192
                                }
193
                        }
194
                }
195

196
                // our value needs to go through rounding to be correctly interpreted
197
                // 
198
                // tgt.clear();  // no need as all bits are going to be set by the code below
199

200
                // exponent construction
201
                int adjustment{ 0 };
74,342,760✔
202
                // construct exponent
203
                uint64_t biasedExponent = static_cast<uint64_t>(static_cast<long long>(exponent) + static_cast<long long>(cfloatType::EXP_BIAS)); // this is guaranteed to be positive if exponent in encoding range
74,342,760✔
204
//                        std::cout << "exponent         " << to_binary(biasedExponent) << '\n';        
205
                if constexpr (hasSubnormals) {
206
                        //if (exponent >= cfloatType::MIN_EXP_SUBNORMAL && exponent < cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>::MIN_EXP_NORMAL) {
207
                        if (exponent < cfloatType::MIN_EXP_NORMAL) {
52,232,211✔
208
                                // the value is in the subnormal range of the cfloat
209
                                biasedExponent = 0;
35,478,789✔
210
                                // -exponent because we are right shifting and exponent in this range is negative
211
                                adjustment = -(exponent + subnormal_reciprocal_shift[es]);
35,478,789✔
212
                                // this is the right shift adjustment required for subnormal representation due 
213
                                // to the scale of the input number, i.e. the exponent of 2^-adjustment
214
                        }
215
                        else {
216
                                // the value is in the normal range of the cfloat
217
                                biasedExponent = static_cast<uint64_t>(static_cast<long long>(exponent) + static_cast<long long>(cfloatType::EXP_BIAS)); // this is guaranteed to be positive
16,753,422✔
218
                        }
219
                }
220
                else {
221
                        if (exponent < cfloatType::MIN_EXP_NORMAL) biasedExponent = 1ull; // fixup biasedExponent if we are in the subnormal region
22,110,549✔
222
                }
223

224

225
                // get the rounding direction and the LSB right shift: 
226
                std::pair<bool, unsigned> alignment = src.roundingDecision(adjustment);
74,342,760✔
227
                unsigned rightShift = alignment.second;  // this is the shift to get the LSB of the src to the LSB of the tgt
74,342,760✔
228
                // rightShift is also the normalization step: it lines the blocktriple hidden bit up with the cfloat
229
                // hidden-bit position, while the bool tells us whether the discarded tail rounds the retained payload up.
230
                //std::cout << "rightShift       " << rightShift << '\n';
231

232
                if constexpr (btType::bfbits < 65) {
233
                        bool roundup = alignment.first;
73,810,882✔
234
                        //std::cout << "round-up?        " << (roundup ? "yes" : "no") << '\n';
235
                        // we can use a uint64_t to construct the cfloat
236
                        uint64_t raw = (src.sign() ? 1ull : 0ull); // process sign
73,810,882✔
237
                        //std::cout << "raw bits (sign)  " << to_binary(raw) << '\n';
238
                        // construct the fraction bits
239
                        uint64_t fracbits = src.significand_ull(); // get all the bits, including the integer bits
73,810,882✔
240
                        //std::cout << "fracbits         " << to_binary(fracbits) << '\n';
241
                        fracbits >>= rightShift;
73,810,882✔
242
                        //std::cout << "fracbits shifted " << to_binary(fracbits) << '\n';
243
                        fracbits &= cfloatType::ALL_ONES_FR; // remove the hidden bit
73,810,882✔
244
                        //std::cout << "fracbits masked  " << to_binary(fracbits) << '\n';
245
                        if (roundup) ++fracbits;
73,810,882✔
246
                        // Rounding can turn 1.111... into 10.000..., so the carry has to be reinterpreted as an exponent bump.
247
                        // This is the last place where the unrounded blocktriple state can still change the target binade.
248
                        if (fracbits == (1ull << cfloatType::fbits)) { // check for overflow
73,810,882✔
249
                                if (biasedExponent == cfloatType::ALL_ONES_ES) {
456,326✔
250
                                        fracbits = cfloatType::INF_ENCODING; // project to INF
6,424✔
251
                                }
252
                                else {
253
                                        ++biasedExponent;
449,902✔
254
                                        fracbits = 0;
449,902✔
255
                                }
256
                        }
257

258
                        raw <<= es; // shift sign to make room for the exponent bits
73,810,882✔
259
                        raw |= biasedExponent;
73,810,882✔
260
                        //std::cout << "raw bits (exp)   " << to_binary(raw) << '\n';
261
                        raw <<= cfloatType::fbits; // make room for the fraction bits
73,810,882✔
262
                        //std::cout << "raw bits (s+exp) " << to_binary(raw) << '\n';
263
                        raw |= fracbits;
73,810,882✔
264
                        //std::cout << "raw bits (final) " << to_binary(raw) << '\n';
265
                        tgt.setbits(raw);
73,810,882✔
266
//                        std::cout << "raw bits (all)   " << to_binary(raw) << '\n';
267
                        if constexpr (isSaturating) {
268
                                if (tgt.isnan()) {
20,168,160✔
269
                                        if (src.sign()) {
722✔
270
                                                tgt.maxneg();        // map back to maxneg
361✔
271
                                        }
272
                                        else {
273
                                                tgt.maxpos();        // map back to maxpos
361✔
274
                                        }
275
                                }
276
                        }
277
                        else {
278
                                // when you get too far, map it back to +-inf: 
279
                                // TBD: this doesn't appear to be the right algorithm to catch all overflow patterns
280
                                if (tgt.isnan()) tgt.setinf(src.sign());        // map back to +-inf
53,642,722✔
281
                        }
282
                }
283
                else {
284
                        // compose the segments
285
                        bool roundup = alignment.first;
531,878✔
286
                        auto fracbits = src.significand();
531,878✔
287
                        fracbits >>= static_cast<int>(rightShift);
531,878✔
288
                        fracbits.setbit(cfloatType::fbits, false); // remove the hidden bit (matches narrow path: fracbits &= ALL_ONES_FR)
531,878✔
289

290
                        // apply rounding (matches the bfbits < 65 path above)
291
                        if (roundup) fracbits.increment();
531,878✔
292

293
                        // check for rounding overflow: if the fraction carried into
294
                        // the hidden-bit position, clear the fraction and bump the exponent
295
                        if (fracbits.test(cfloatType::fbits)) {
531,878✔
296
                                fracbits.clear();
33✔
297
                                if (biasedExponent == cfloatType::ALL_ONES_ES) {
33✔
298
                                        // rounding overflowed beyond the maximum binade:
299
                                        // handle explicitly because the isnan() remap below
300
                                        // does not catch all configurations (e.g. hasMaxExpValues
301
                                        // with zero fraction is a valid finite encoding, not NaN)
302
                                        if constexpr (isSaturating) {
303
                                                if (src.sign()) tgt.maxneg(); else tgt.maxpos();
×
304
                                        }
305
                                        else {
306
                                                tgt.setinf(src.sign());
×
307
                                        }
308
                                        return;
×
309
                                }
310
                                else {
311
                                        ++biasedExponent;
33✔
312
                                        ++exponent;
33✔
313
                                }
314
                        }
315

316
                        // copy the blocks that contain fraction bits
317
                        tgt.clear();
531,878✔
318
                        for (unsigned b = 0; b < btType::nrBlocks; ++b) {
1,074,252✔
319
                                tgt.setblock(b, fracbits.block(b));
542,374✔
320
                        }
321
                        tgt.setsign(src.sign());
531,878✔
322
                        if (!tgt.setexponent(exponent)) {
531,878✔
323
                                // std::cerr is not constexpr-callable; gate the diagnostic on
324
                                // runtime context only. The constant-evaluator silently drops
325
                                // the diagnostic but the enclosing branch is rare in practice
326
                                // (only triggered when blocktriple bfbits >= 65 and the
327
                                // computed exponent doesn't fit the cfloat config).
328
                                if (!std::is_constant_evaluated()) {
×
329
                                        std::cerr << "exponent value is out of range: " << exponent << '\n';
×
330
                                }
331
                        }
332

333
                        // saturation / overflow-to-inf handling (matches the bfbits < 65 path)
334
                        if constexpr (isSaturating) {
335
                                if (tgt.isnan()) {
8✔
336
                                        if (src.sign()) {
×
337
                                                tgt.maxneg();
×
338
                                        }
339
                                        else {
340
                                                tgt.maxpos();
×
341
                                        }
342
                                }
343
                        }
344
                        else {
345
                                if (tgt.isnan()) tgt.setinf(src.sign());
531,870✔
346
                        }
347
                }
348
        }
349
}
350

351

352
/// <summary>
353
/// An arbitrary, fixed-size floating-point number with configurable gradual under/overflow and saturation/non-saturation arithmetic.
354
/// Default configuration offers normal encoding and non-saturating arithmetic.
355
/// /// </summary>
356
/// <typeparam name="nbits">number of bits in the encoding</typeparam>
357
/// <typeparam name="es">number of exponent bits in the encoding</typeparam>
358
/// <typeparam name="bt">the type to use as storage class: one of [uint8_t|uint16_t|uint32_t]</typeparam>
359
/// <typeparam name="hasSubnormals">configure gradual underflow (==subnormals)</typeparam>
360
/// <typeparam name="hasMaxExpValues">reclaim max-exponent encodings as numeric values</typeparam>
361
/// <typeparam name="isSaturating">configure saturation arithmetic</typeparam>
362
template<unsigned _nbits, unsigned _es, typename bt = uint8_t,
363
        bool _hasSubnormals = false, bool _hasMaxExpValues = false, bool _isSaturating = false>
364
class cfloat {
365
public:
366
        static_assert(_nbits > _es + 1ull, "nbits is too small to accomodate the requested number of exponent bits");
367
        static_assert(_es < 21ull, "my God that is a big number, are you trying to break the Interweb?");
368
        static_assert(_es > 0, "number of exponent bits must be bigger than 0 to be a classic floating point number");
369
        // how do you assert on the condition that if es == 1 then subnormals and max-exponent values must be true?
370
        static constexpr bool     subsuper = (_hasSubnormals && _hasMaxExpValues);
371
        static constexpr bool     special = (subsuper ? true : (_es > 1));
372
        static_assert(special, "when es == 1, cfloat must have both subnormals and max-exponent values");
373
        static constexpr unsigned bitsInByte = 8u;
374
        static constexpr unsigned bitsInBlock = sizeof(bt) * bitsInByte;
375
        static_assert(bitsInBlock <= 64, "storage unit for block arithmetic needs to be <= uint64_t"); // TODO: carry propagation on uint64_t requires assembly code
376

377
        static constexpr unsigned nbits = _nbits;
378
        static constexpr unsigned es = _es;
379
        static constexpr unsigned fbits  = nbits - 1u - es;    // number of fraction bits excluding the hidden bit
380
        static constexpr unsigned fhbits = nbits - es;           // number of fraction bits including the hidden bit
381

382
        static constexpr uint64_t  storageMask = (0xFFFFFFFFFFFFFFFFull >> (64ull - bitsInBlock));
383
        static constexpr bt       BLOCK_MASK = bt(~0);
384
        static constexpr bt       ALL_ONES = bt(~0); // block type specific all 1's value
385
        static constexpr uint32_t ALL_ONES_ES = (0xFFFF'FFFFul >> (32 - es));
386
        static constexpr uint64_t topfbits = fbits % 64;
387
        static constexpr uint64_t FR_SHIFT = (topfbits > 0 ? (64 - topfbits) : 0);
388
        static constexpr uint64_t ALL_ONES_FR = (topfbits > 0 ? (0xFFFF'FFFF'FFFF'FFFFull >> FR_SHIFT) : 0ull); // special case for nbits <= 64
389
        static constexpr uint64_t INF_ENCODING = (ALL_ONES_FR & ~1ull);
390

391
        static constexpr unsigned nrBlocks = 1u + ((nbits - 1ull) / bitsInBlock);
392
        static constexpr unsigned MSU = nrBlocks - 1u; // MSU == Most Significant Unit, as MSB is already taken
393
        static constexpr bt       MSU_MASK = (ALL_ONES >> (nrBlocks * bitsInBlock - nbits));
394
        static constexpr unsigned bitsInMSU = bitsInBlock - (nrBlocks * bitsInBlock - nbits);
395
        static constexpr unsigned fBlocks = 1ull + ((fbits - 1ull) / bitsInBlock); // nr of blocks with fraction bits
396
        static constexpr unsigned FSU = fBlocks - 1u;  // FSU = Fraction Significant Unit: the index of the block that contains the most significant fraction bits
397
        static constexpr bt       FSU_MASK = (ALL_ONES >> (fBlocks * bitsInBlock - fbits));
398
        static constexpr unsigned bitsInFSU = bitsInBlock - (fBlocks * bitsInBlock - fbits);
399

400
        static constexpr bt       SIGN_BIT_MASK = bt(bt(1ull) << ((nbits - 1ull) % bitsInBlock));
401
        static constexpr bt       LSB_BIT_MASK = bt(1ull);
402
        static constexpr bool     MSU_CAPTURES_EXP = (1ull + es) <= bitsInMSU;
403
        static constexpr unsigned EXP_SHIFT = (MSU_CAPTURES_EXP ? (1 == nrBlocks ? (nbits - 1ull - es) : (bitsInMSU - 1ull - es)) : 0);
404
        static constexpr bt       MSU_EXP_MASK = ((ALL_ONES << EXP_SHIFT) & ~SIGN_BIT_MASK) & MSU_MASK;
405
        static constexpr int      EXP_BIAS = ((1 << (es - 1u)) - 1l);
406
        static constexpr int      MAX_EXP = (es == 1) ? 1 : ((1 << es) - EXP_BIAS - 1);
407
        static constexpr int      MIN_EXP_NORMAL = 1 - EXP_BIAS;
408
        static constexpr int      MIN_EXP_SUBNORMAL = 1 - EXP_BIAS - int(fbits); // the scale of smallest ULP
409

410
        static constexpr bool     hasSubnormals   = _hasSubnormals;
411
        static constexpr bool     hasMaxExpValues = _hasMaxExpValues;
412
        static constexpr bool     isSaturating    = _isSaturating;
413
        typedef bt BlockType;
414

415
        // constructors
416
        cfloat() = default;
417
        cfloat(const cfloat&) = default;
418
        cfloat& operator=(const cfloat&) = default;
419

420
        // construct a cfloat from another
421
        template<unsigned nnbits, unsigned ees, typename bbt, bool ssub, bool ssup, bool ssat>
422
        cfloat(const cfloat<nnbits, ees, bbt, ssub, ssup, ssat>& rhs) noexcept : _block{} {
10,995✔
423
                if (rhs.isnan()) {
10,995✔
424
                        setnan(rhs.sign() ? NAN_TYPE_SIGNALLING : NAN_TYPE_QUIET);
461✔
425
                }
426
                else if (rhs.isinf()) {
10,534✔
427
                        setinf(rhs.sign());
23✔
428
                }
429
                else if (rhs.iszero()) {
10,511✔
430
                        setzero();
1,125✔
431
                }
432
                else {
433
                        if constexpr (std::is_same_v<bt, bbt>) {
434
                                // Full-precision path: create a blocktriple sized for the TARGET's
435
                                // fraction width, pre-align the source fraction bits into it (with
436
                                // proper rounding-bit collection), then let convert() handle
437
                                // rounding and encoding.
438
                                constexpr unsigned srcFbits = nnbits - 1u - ees;
4,968✔
439
                                using TgtBT = blocktriple<fbits, BlockTripleOperator::ADD, bt>;
440
                                constexpr unsigned addRbits = TgtBT::rbits;
4,968✔
441
                                TgtBT value;
4,968✔
442

443
                                bool srcSign = rhs.sign();
4,968✔
444
                                int srcScale = rhs.scale();
4,968✔
445

446
                                if constexpr (srcFbits < 64 && (fbits + addRbits) < 64) {
447
                                        // Fast path: fits in uint64_t
448
                                        // Guard requires (fbits + addRbits) < 64 to prevent UB from
449
                                        // left-shift of amount (fbits + addRbits - srcFbits) >= 64.
450
                                        uint64_t srcRaw = rhs.fraction_ull();
4,968✔
451
                                        if (rhs.isnormal() || rhs.ismaxexpvalue()) {
4,968✔
452
                                                // Normal and max-exponent values both have a hidden bit
453
                                                srcRaw |= (1ull << srcFbits);
4,968✔
454
                                        }
455
                                        else {
456
                                                // Normalize subnormal: find leading 1, shift to hidden-bit position
457
                                                using SrcCfloat = cfloat<nnbits, ees, bbt, ssub, ssup, ssat>;
458
                                                srcScale = SrcCfloat::MIN_EXP_NORMAL;
×
459
                                                for (int i = static_cast<int>(srcFbits) - 1; i >= 0; --i) {
×
460
                                                        if (srcRaw & (1ull << i)) {
×
461
                                                                unsigned shift = srcFbits - static_cast<unsigned>(i);
×
462
                                                                srcScale -= static_cast<int>(shift);
×
463
                                                                srcRaw <<= shift;
×
464
                                                                break;
×
465
                                                        }
466
                                                }
467
                                        }
468
                                        // Align source hidden bit (at srcFbits) to target position (fbits + addRbits)
469
                                        uint64_t tgtRaw;
470
                                        if constexpr (srcFbits >= fbits + addRbits) {
471
                                                constexpr unsigned rshift = srcFbits - fbits - addRbits;
4,230✔
472
                                                if constexpr (rshift > 0) {
473
                                                        uint64_t stickyMask = (1ull << rshift) - 1;
646✔
474
                                                        bool sticky = (srcRaw & stickyMask) != 0;
646✔
475
                                                        tgtRaw = srcRaw >> rshift;
646✔
476
                                                        if (sticky) tgtRaw |= 1; // fold discarded bits into sticky
646✔
477
                                                }
478
                                                else {
479
                                                        tgtRaw = srcRaw;
3,584✔
480
                                                }
481
                                        }
482
                                        else {
483
                                                constexpr unsigned lshift = fbits + addRbits - srcFbits;
738✔
484
                                                tgtRaw = srcRaw << lshift;
738✔
485
                                        }
486
                                        value.setsign(srcSign);
4,968✔
487
                                        value.setscale(srcScale);
4,968✔
488
                                        value.setbits(tgtRaw);
4,968✔
489
                                }
490
                                else {
491
                                        // Wide path: bit-by-bit copy with alignment
492
                                        int offset = static_cast<int>(fbits + addRbits) - static_cast<int>(srcFbits);
493
                                        // Initialize radix via setbits, then overwrite significand
494
                                        value.setbits(0);
495
                                        bool sticky = false;
496
                                        for (unsigned i = 0; i < srcFbits; ++i) {
497
                                                bool v = rhs.at(i);
498
                                                if (!v) continue;
499
                                                int tgtBit = static_cast<int>(i) + offset;
500
                                                if (tgtBit >= 0 && tgtBit < static_cast<int>(TgtBT::bfbits)) {
501
                                                        value.setbit(static_cast<unsigned>(tgtBit), true);
502
                                                }
503
                                                else if (tgtBit < 0) {
504
                                                        sticky = true;
505
                                                }
506
                                        }
507
                                        if (sticky) value.setbit(0, true);
508
                                        if (rhs.isnormal() || rhs.ismaxexpvalue()) {
509
                                                // Normal and max-exponent values both have a hidden bit
510
                                                value.setbit(static_cast<unsigned>(fbits + addRbits), true);
511
                                        }
512
                                        else {
513
                                                // Normalize subnormal for wide path.
514
                                                // The bit-copy loop above placed source bit i at target position
515
                                                // i + offset = i + (fbits + addRbits - srcFbits).  The blocktriple
516
                                                // scale represents the exponent such that:
517
                                                //   value = 2^scale * significant * 2^(-fbits-addRbits)
518
                                                // For the subnormal with leading 1 at source bit i, the significant
519
                                                // integer has its leading 1 at position i + fbits + addRbits - srcFbits,
520
                                                // giving value = 2^scale * 2^(i - srcFbits) * 1.xxx.
521
                                                // The true subnormal value is 2^MIN_EXP_NORMAL * 2^(i - srcFbits) * 1.xxx,
522
                                                // so scale = MIN_EXP_NORMAL with no further adjustment.
523
                                                using SrcCfloat = cfloat<nnbits, ees, bbt, ssub, ssup, ssat>;
524
                                                srcScale = SrcCfloat::MIN_EXP_NORMAL;
525
                                                // (No srcScale -= shift: the offset alignment already accounts for
526
                                                //  the bit position; subtracting shift would apply it twice.)
527
                                        }
528
                                        value.setnormal(); // clear _zero flag set by setbits(0)
529
                                        value.setsign(srcSign);
530
                                        value.setscale(srcScale);
531
                                }
532
                                convert(value, *this);
4,968✔
533
                        }
534
                        else {
535
                                // Cross-block-type: marshall through double.
536
                                // Safe only when the source fraction fits in double's 52-bit significand.
537
                                constexpr unsigned srcFbitsXB = nnbits - ees - 1u;
4,418✔
538
                                static_assert(srcFbitsXB <= 52u,
539
                                        "cfloat converting constructor: source and target have different "
540
                                        "block types and source has more fraction bits than double can represent; "
541
                                        "use matching block types for full-precision cross-config conversion");
542
                                *this = double(rhs);
4,418✔
543
                        }
544
                }
545
        }
10,995✔
546

547
        // converting constructors
548
        constexpr cfloat(const std::string& stringRep) : _block{} { assign(stringRep); }
1✔
549
        // specific value constructor
550
        constexpr cfloat(const SpecificValue code) noexcept : _block{} {
305✔
551
                switch (code) {
305✔
552
                case SpecificValue::maxpos:
91✔
553
                        maxpos();
91✔
554
                        break;
91✔
555
                case SpecificValue::minpos:
111✔
556
                        minpos();
111✔
557
                        break;
111✔
558
                case SpecificValue::zero:
×
559
                default:
560
                        zero();
×
561
                        break;
×
562
                case SpecificValue::minneg:
×
563
                        minneg();
×
564
                        break;
×
565
                case SpecificValue::maxneg:
48✔
566
                        maxneg();
48✔
567
                        break;
48✔
568
                case SpecificValue::infpos:
21✔
569
                        setinf(false);
21✔
570
                        break;
21✔
571
                case SpecificValue::infneg:
×
572
                        setinf(true);
×
573
                        break;
×
574
                case SpecificValue::nar: // approximation as cfloats don't have a NaR
17✔
575
                case SpecificValue::qnan:
576
                        setnan(NAN_TYPE_QUIET);
17✔
577
                        break;
17✔
578
                case SpecificValue::snan:
17✔
579
                        setnan(NAN_TYPE_SIGNALLING);
17✔
580
                        break;
17✔
581
                }
582
        }
305✔
583

584
        constexpr cfloat(signed char iv)                    noexcept : _block{} { *this = iv; }
585
        constexpr cfloat(short iv)                          noexcept : _block{} { *this = iv; }
586
        constexpr cfloat(int iv)                            noexcept : _block{} { *this = iv; }
7,846✔
587
        constexpr cfloat(long iv)                           noexcept : _block{} { *this = iv; }
588
        constexpr cfloat(long long iv)                      noexcept : _block{} { *this = iv; }
1✔
589
        constexpr cfloat(char iv)                           noexcept : _block{} { *this = iv; }
590
        constexpr cfloat(unsigned short iv)                 noexcept : _block{} { *this = iv; }
591
        constexpr cfloat(unsigned int iv)                   noexcept : _block{} { *this = iv; }
592
        constexpr cfloat(unsigned long iv)                  noexcept : _block{} { *this = iv; }
100✔
593
        constexpr cfloat(unsigned long long iv)             noexcept : _block{} { *this = iv; }
594
        CONSTEXPRESSION cfloat(float iv)                    noexcept : _block{} { *this = iv; }
340,801✔
595
        CONSTEXPRESSION cfloat(double iv)                   noexcept : _block{} { *this = iv; }
475,818✔
596

597
        // assignment operators
598
        constexpr cfloat& operator=(signed char rhs)        noexcept { return convert_signed_integer(rhs); }
599
        constexpr cfloat& operator=(short rhs)              noexcept { return convert_signed_integer(rhs); }
600
        constexpr cfloat& operator=(int rhs)                noexcept { return convert_signed_integer(rhs); }
13,328✔
601
        constexpr cfloat& operator=(long rhs)               noexcept { return convert_signed_integer(rhs); }
602
        constexpr cfloat& operator=(long long rhs)          noexcept { return convert_signed_integer(rhs); }
1✔
603

604
        constexpr cfloat& operator=(char rhs)               noexcept { return convert_unsigned_integer(rhs); }
605
        constexpr cfloat& operator=(unsigned short rhs)     noexcept { return convert_unsigned_integer(rhs); }
606
        constexpr cfloat& operator=(unsigned int rhs)       noexcept { return convert_unsigned_integer(rhs); }
2,685✔
607
        constexpr cfloat& operator=(unsigned long rhs)      noexcept { return convert_unsigned_integer(rhs); }
110✔
608
        constexpr cfloat& operator=(unsigned long long rhs) noexcept { return convert_unsigned_integer(rhs); }
609

610
        BIT_CAST_CONSTEXPR cfloat& operator=(float rhs)     noexcept { return convert_ieee754(rhs); }
32,488,676✔
611
        BIT_CAST_CONSTEXPR cfloat& operator=(double rhs)    noexcept { return convert_ieee754(rhs); }
105,789,820✔
612

613
        // make conversions to native types explicit
614
        explicit constexpr operator int()                       const noexcept { return to_int(); }
615
        explicit constexpr operator long()                      const noexcept { return to_long(); }
616
        explicit constexpr operator long long()                 const noexcept { return to_long_long(); }
1✔
617
        explicit constexpr operator float()                     const noexcept { return to_native<float>(); }
35,177,193✔
618
        explicit constexpr operator double()                    const noexcept { return to_native<double>(); }
78,364,918✔
619

620
        // guard long double support to enable ARM and RISC-V embedded environments
621
#if LONG_DOUBLE_SUPPORT
622
        explicit constexpr operator long double()               const noexcept { return to_native<long double>(); }
48✔
623
        BIT_CAST_CONSTEXPR cfloat(long double iv)           noexcept : _block{} { *this = iv; }
8✔
624
        BIT_CAST_CONSTEXPR cfloat& operator=(long double rhs)  noexcept { return convert_ieee754(rhs); }
80✔
625
#endif
626

627
        // arithmetic operators
628
        // prefix operator
629
        constexpr inline cfloat operator-() const {
8,724,119✔
630
                cfloat tmp(*this);
8,724,119✔
631
                tmp._block[MSU] ^= SIGN_BIT_MASK;
8,724,119✔
632
                return tmp;
8,724,119✔
633
        }
634

635
        constexpr cfloat& operator+=(const cfloat& rhs) CFLOAT_EXCEPT {
19,172,371✔
636
                if constexpr (_trace_add) std::cout << "---------------------- ADD -------------------" << std::endl;
637
                // special case handling of the inputs
638
#if CFLOAT_THROW_ARITHMETIC_EXCEPTION
639
                if (isnan(NAN_TYPE_SIGNALLING) || rhs.isnan(NAN_TYPE_SIGNALLING)) {
739✔
640
                        throw cfloat_operand_is_nan{};
×
641
                }
642
#else
643
                if (isnan(NAN_TYPE_SIGNALLING) || rhs.isnan(NAN_TYPE_SIGNALLING)) {
19,171,632✔
644
                        setnan(NAN_TYPE_SIGNALLING);
234,874✔
645
                        return *this;
234,874✔
646
                }
647
                if (isnan(NAN_TYPE_QUIET) || rhs.isnan(NAN_TYPE_QUIET)) {
18,936,758✔
648
                        setnan(NAN_TYPE_QUIET);
215,298✔
649
                        return *this;
215,298✔
650
                }
651
#endif
652
                // normal + inf  = inf
653
                // normal + -inf = -inf
654
                // inf + normal = inf
655
                // inf + inf    = inf
656
                // inf + -inf    = ?
657
                // -inf + normal = -inf
658
                // -inf + -inf   = -inf
659
                // -inf + inf    = ?
660
                if (isinf()) {
18,722,199✔
661
                        if (rhs.isinf()) {
60,327✔
662
                                if (sign() != rhs.sign()) {
939✔
663
                                        setnan(NAN_TYPE_SIGNALLING);
514✔
664
                                }
665
                                return *this;
939✔
666
                        }
667
                        else {
668
                                return *this;
59,388✔
669
                        }
670
                }
671
                else {
672
                        if (rhs.isinf()) {
18,661,872✔
673
                                *this = rhs;
60,433✔
674
                                return *this;
60,433✔
675
                        }
676
                }
677

678
                if (iszero()) {
18,601,439✔
679
                        *this = rhs;
214,866✔
680
                        return *this;
214,866✔
681
                }
682
                if (rhs.iszero()) return *this;
18,386,573✔
683

684
                // arithmetic operation
685
                blocktriple<fbits, BlockTripleOperator::ADD, bt> a, b, sum;
18,217,712✔
686

687
                // transform the inputs into (sign,scale,significant) 
688
                normalizeAddition(a); 
18,217,712✔
689
                rhs.normalizeAddition(b);
18,217,712✔
690
                sum.add(a, b);
18,217,712✔
691

692
                convert(sum, *this);
18,217,712✔
693

694
                return *this;
18,217,712✔
695
        }
696
        constexpr cfloat& operator+=(double rhs) CFLOAT_EXCEPT {
697
                return *this += cfloat(rhs);
698
        }
699
        constexpr cfloat& operator-=(const cfloat& rhs) CFLOAT_EXCEPT {
8,807,074✔
700
                if constexpr (_trace_sub) std::cout << "---------------------- SUB -------------------" << std::endl;
701
                if (rhs.isnan()) 
8,807,074✔
702
                        return *this += rhs;
170,204✔
703
                else 
704
                        return *this += -rhs;
8,636,870✔
705
        }
706
        constexpr cfloat& operator-=(double rhs) CFLOAT_EXCEPT {
8✔
707
                return *this -= cfloat(rhs);
8✔
708
        }
709
        constexpr cfloat& operator*=(const cfloat& rhs) CFLOAT_EXCEPT {
4,202,562✔
710
                if constexpr (_trace_mul) std::cout << "---------------------- MUL -------------------\n";
322✔
711
                // special case handling of the inputs
712
#if CFLOAT_THROW_ARITHMETIC_EXCEPTION
713
                if (isnan(NAN_TYPE_SIGNALLING) || rhs.isnan(NAN_TYPE_SIGNALLING)) {
613✔
714
                        throw cfloat_operand_is_nan{};
×
715
                }
716
#else
717
                if (isnan(NAN_TYPE_SIGNALLING) || rhs.isnan(NAN_TYPE_SIGNALLING)) {
4,201,949✔
718
                        setnan(NAN_TYPE_SIGNALLING);
124,657✔
719
                        return *this;
124,657✔
720
                }
721
                if (isnan(NAN_TYPE_QUIET) || rhs.isnan(NAN_TYPE_QUIET)) {
4,077,292✔
722
                        setnan(NAN_TYPE_QUIET);
113,354✔
723
                        return *this;
113,354✔
724
                }
725
#endif
726
                //  inf * inf = inf
727
                //  inf * -inf = -inf
728
                // -inf * inf = -inf
729
                // -inf * -inf = inf
730
                //        0 * inf = -nan(ind)
731
                //        inf * 0 = -nan(ind)
732
                bool resultSign = sign() != rhs.sign();
3,964,551✔
733
                if (isinf()) {
3,964,551✔
734
                        if (rhs.iszero()) {
24,458✔
735
                                setnan(NAN_TYPE_QUIET);
1,591✔
736
                        }
737
                        else {
738
                                setsign(resultSign);
22,867✔
739
                        }
740
                        return *this;
24,458✔
741
                }
742
                if (rhs.isinf()) {
3,940,093✔
743
                        if (iszero()) {
25,815✔
744
                                setnan(NAN_TYPE_QUIET);
2,969✔
745
                        }
746
                        else {
747
                                setinf(resultSign);
22,846✔
748
                        }
749
                        return *this;
25,815✔
750
                }
751

752
                if (iszero() || rhs.iszero()) {                        
3,914,278✔
753
                        setzero();
1,816,893✔
754
                        setsign(resultSign); // deal with negative 0
1,816,893✔
755
                        return *this;
1,816,893✔
756
                }
757

758
                // arithmetic operation
759
                blocktriple<fbits, BlockTripleOperator::MUL, bt> a, b, product;
2,097,385✔
760

761
                // transform the inputs into (sign,scale,significant) 
762
                // triples of the correct width
763
                normalizeMultiplication(a);
2,097,385✔
764
                rhs.normalizeMultiplication(b);
2,097,385✔
765
                product.mul(a, b);
2,097,385✔
766
                convert(product, *this);
2,097,385✔
767

768
                if constexpr (_trace_mul) std::cout << to_binary(a) << " : " << a << " *\n" << to_binary(b) << " : " << b << " =\n" << to_binary(product) << " : " << product << '\n';
106✔
769

770
                return *this;
2,097,385✔
771
        }
772
        constexpr cfloat& operator*=(double rhs) CFLOAT_EXCEPT {
40✔
773
                return *this *= cfloat(rhs);
40✔
774
        }
775
        constexpr cfloat& operator/=(const cfloat& rhs) CFLOAT_EXCEPT {
5,022,569✔
776
                if constexpr (_trace_div) std::cout << "---------------------- DIV -------------------" << std::endl;
777

778
                // special case handling of the inputs
779
                // qnan / qnan = qnan
780
                // qnan / snan = qnan
781
                // snan / qnan = snan
782
                // snan / snan = snan
783
#if CFLOAT_THROW_ARITHMETIC_EXCEPTION
784
                if (rhs.iszero()) throw cfloat_divide_by_zero();
49✔
785
                if (rhs.isnan()) throw cfloat_divide_by_nan();
48✔
786
                if (isnan()) throw cfloat_operand_is_nan();
48✔
787
#else
788
                if (isnan(NAN_TYPE_SIGNALLING) || rhs.isnan(NAN_TYPE_SIGNALLING)) {
5,022,520✔
789
                        setnan(NAN_TYPE_SIGNALLING);
162,762✔
790
                        return *this;
162,762✔
791
                }
792
                if (isnan(NAN_TYPE_QUIET) || rhs.isnan(NAN_TYPE_QUIET)) {
4,859,758✔
793
                        setnan(NAN_TYPE_QUIET);
149,935✔
794
                        return *this;
149,935✔
795
                }
796
                if (rhs.iszero()) {
4,709,823✔
797
                        if (iszero()) {
169,760✔
798
                                // zero divide by zero yields quiet NaN (in MSVC it is labeled -nan(ind) for indeterminate)
799
                                setnan(NAN_TYPE_QUIET);
29,299✔
800
                        }
801
                        else {
802
                                // non-zero divide by zero yields INF
803
                                bool resultSign = sign() != rhs.sign();
140,461✔
804
                                setinf(resultSign);
140,461✔
805
                        }
806
                        return *this;
169,760✔
807
                }
808
#endif
809
                //  inf /  inf = -nan(ind)
810
                //  inf / -inf = -nan(ind)
811
                // -inf /  inf = -nan(ind)
812
                // -inf / -inf = -nan(ind)
813
                //        1.0 /  inf = 0
814
                bool resultSign = sign() != rhs.sign();
4,540,111✔
815
                if (isinf()) {
4,540,111✔
816
                        if (rhs.isinf()) {
31,087✔
817
                                // inf divide by inf yields quiet NaN (in MSVC it is labeled -nan(ind) for indeterminate)
818
                                setnan(NAN_TYPE_QUIET);
559✔
819
                                return *this;
559✔
820
                        }
821
                        else {
822
                                // we stay an infinite but may change sign
823
                                setsign(resultSign);
30,528✔
824
                                return *this;
30,528✔
825
                        }
826
                }
827
                else {
828
                        if (rhs.isinf()) {
4,509,024✔
829
                                setzero();
32,833✔
830
                                setsign(resultSign);
32,833✔
831
                                return *this;
32,833✔
832
                        }
833
                }
834

835
                if (iszero()) {
4,476,191✔
836
                        setzero();
139,452✔
837
                        setsign(resultSign); // deal with negative 0
139,452✔
838
                        return *this;
139,452✔
839
                }
840

841
                // arithmetic operation
842
                using BlockTriple = blocktriple<fbits, BlockTripleOperator::DIV, bt>;
843
                BlockTriple a, b, quotient;
4,336,739✔
844

845
                // transform the inputs into (sign,scale,significant) 
846
                // triples of the correct width
847
                normalizeDivision(a);
4,336,739✔
848
                rhs.normalizeDivision(b);
4,336,739✔
849
                quotient.div(a, b);
4,336,739✔
850
                quotient.setradix(BlockTriple::radix);
4,336,739✔
851
                convert(quotient, *this);
4,336,739✔
852

853
                if constexpr (_trace_div) std::cout << to_binary(a) << " : " << a << " /\n" << to_binary(b) << " : " << b << " =\n" << to_binary(quotient) << " : " << quotient << '\n';
854

855
                return *this;
4,336,739✔
856
        }
857
        constexpr cfloat& operator/=(double rhs) CFLOAT_EXCEPT {
858
                return *this /= cfloat(rhs);
859
        }
860
        constexpr cfloat& reciprocal() CFLOAT_EXCEPT {
861
                cfloat c = 1.0 / *this;
862
                return *this = c;
863
        }
864
        /// <summary>
865
        /// move to the next bit encoding modulo 2^nbits
866
        /// </summary>
867
        /// <typeparam name="bt"></typeparam>
868
        constexpr cfloat& operator++() {
4,596,085✔
869
                if constexpr (0 == nrBlocks) {
870
                        return *this;
871
                }
872
                else if constexpr (1 == nrBlocks) {
873
                        if (sign()) {
861,696✔
874
                                if (_block[MSU] == (SIGN_BIT_MASK | 1ul)) { // pattern: 1.00.001 = minneg
467✔
875
                                        _block[MSU] = 0; // pattern: 0.00.000 = +0 
7✔
876
                                }
877
                                else {
878
                                        --_block[MSU];
460✔
879
                                }
880
                                if constexpr (!hasSubnormals) {
881
                                        if (isdenormal()) {
198✔
882
                                                // special case, we need to jump past all the subnormal value encodings which puts us on 0
883
                                                _block[MSU] = 0; // pattern: 0.00.000 = +0
3✔
884
                                        }
885
                                }
886
                        }
887
                        else {
888
                                if constexpr (!hasSubnormals) {
889
                                        if (_block[MSU] == 0) {
384,647✔
890
                                                // special case, we need to jump past all the subnormal value encodings minus 1
891
                                                setfraction(0xFFFF'FFFF'FFFF'FFFFull);
4✔
892
                                        }
893
                                }
894
                                if ((_block[MSU] & (MSU_MASK >> 1)) == (MSU_MASK >> 1)) { // pattern: 0.11.111 = nan
861,229✔
895
                                        _block[MSU] |= SIGN_BIT_MASK; // pattern: 1.11.111 = snan : wrap to the other side of the encoding
×
896
                                }
897
                                else {
898
                                        ++_block[MSU];
861,229✔
899
                                }
900
                        }
901
                }
902
                else {
903
                        if (sign()) {
3,734,389✔
904
                                // special case: pattern: 1.00.001 = minneg transitions to pattern: 0.00.000 = +0 
905
                                if (isminnegencoding()) {
66,947✔
906
                                        setzero();
8✔
907
                                }
908
                                else {
909
                                        //  1111 0000
910
                                        //  1110 1111
911
                                        bool borrow = true;
66,939✔
912
                                        for (unsigned i = 0; i < MSU; ++i) {
199,415✔
913
                                                if (borrow) {
132,476✔
914
                                                        if ((_block[i] & storageMask) == 0) { // block will underflow
67,197✔
915
                                                                --_block[i];
261✔
916
                                                                borrow = true;
261✔
917
                                                        }
918
                                                        else {
919
                                                                --_block[i];
66,936✔
920
                                                                borrow = false;
66,936✔
921
                                                        }
922
                                                }
923
                                        }
924
                                        if (borrow) {
66,939✔
925
                                                --_block[MSU];
3✔
926
                                        }
927
                                        if constexpr (!hasSubnormals) {
928
                                                if (isdenormal()) {
385✔
929
                                                        // special case, we need to jump past all the subnormal value encodings which puts us on 0
930
                                                        setzero(); // pattern: 0.00.000 = +0
3✔
931
                                                }
932
                                        }
933
                                }
934
                        }
935
                        else {
936
                                // special case: pattern: 0.11.111 = nan transitions to pattern: 1.11.111 = snan 
937
                                if (isnanencoding()) {
3,667,442✔
938
                                        setnan(NAN_TYPE_SIGNALLING);
×
939
                                }
940
                                else {
941
                                        if constexpr (!hasSubnormals) {
942
                                                if (iszero()) {
1,720,661✔
943
                                                        // special case, we need to jump past all the subnormal value encodings minus 1 so that the increment code below ends up on normal minpos
944
                                                        setfraction(0xFFFF'FFFF'FFFF'FFFFull);
3✔
945
                                                }
946
                                        }
947
                                        bool carry = true;
3,667,442✔
948
                                        for (unsigned i = 0; i < MSU; ++i) {
7,400,795✔
949
                                                if (carry) {
3,733,353✔
950
                                                        if ((_block[i] & storageMask) == storageMask) { // block will overflow
3,667,697✔
951
                                                                _block[i] = 0;
256✔
952
                                                                carry = true;
256✔
953
                                                        }
954
                                                        else {
955
                                                                ++_block[i];
3,667,441✔
956
                                                                carry = false;
3,667,441✔
957
                                                        }
958
                                                }
959
                                        }
960
                                        if (carry) {
3,667,442✔
961
                                                ++_block[MSU];
1✔
962
                                        }
963
                                }
964
                        }
965
                }
966
                return *this;
4,596,085✔
967
        }
968
        constexpr cfloat operator++(int) {
134,800✔
969
                cfloat tmp(*this);
134,800✔
970
                operator++();
134,800✔
971
                return tmp;
134,800✔
972
        }
973
        constexpr cfloat& operator--() {
5,014,100✔
974
                if constexpr (0 == nrBlocks) {
975
                        return *this;
976
                }
977
                else if constexpr (1 == nrBlocks) {
978
                        if (sign()) {
942,793✔
979
                                ++_block[MSU];
942,299✔
980
                        }
981
                        else {
982
                                // positive range
983
                                if (_block[MSU] == 0) { // pattern: 0.00.000 = 0
494✔
984
                                        if constexpr (hasSubnormals) {
985
                                                _block[MSU] |= SIGN_BIT_MASK | bt(1u); // pattern: 1.00.001 = minneg 
8✔
986
                                        }
987
                                        else {
988
                                                // special case, we need to jump past all the subnormal value encodings
989
                                                setfraction(0xFFFF'FFFF'FFFF'FFFFull); // set to 0.00.11...11
3✔
990
                                                ++_block[MSU]; // increment into 0.01.0000
3✔
991
                                                _block[MSU] |= SIGN_BIT_MASK; // set to 1.01.0000
3✔
992
                                        }
993
                                }
994
                                else {
995
                                        --_block[MSU];
483✔
996
                                }
997
                                if constexpr (!hasSubnormals) {
998
                                        if (isdenormal()) {
211✔
999
                                                // special case, we need to jump past all the subnormal value encodings which puts us on 0
1000
                                                _block[MSU] = 0; // pattern: 0.00.000 = +0
3✔
1001
                                        }
1002
                                }
1003
                        }
1004
                }
1005
                else {
1006
                        if (sign()) {
4,071,307✔
1007
                                bool carry = true;
4,004,349✔
1008
                                for (unsigned i = 0; i < MSU; ++i) {
8,074,235✔
1009
                                        if (carry) {
4,069,886✔
1010
                                                if ((_block[i] & storageMask) == storageMask) { // block will overflow
4,004,604✔
1011
                                                        _block[i] = 0;
256✔
1012
                                                        carry = true;
256✔
1013
                                                }
1014
                                                else {
1015
                                                        ++_block[i];
4,004,348✔
1016
                                                        carry = false;
4,004,348✔
1017
                                                }
1018
                                        }
1019
                                }
1020
                                if (carry) {
4,004,349✔
1021
                                        ++_block[MSU];
1✔
1022
                                }
1023
                        }
1024
                        else {
1025
                                // special case: pattern: 0.00.000 = +0 transitions to pattern: 1.00.001 = minneg
1026
                                if (iszeroencoding()) {
66,958✔
1027
                                        if constexpr (hasSubnormals) {
1028
                                                setsign(true);
8✔
1029
                                                setbit(0, true);
8✔
1030
                                        }
1031
                                        else {
1032
                                                // special case, we need to jump past all the subnormal value encodings 1.01.0000 = minneg normal
1033
                                                setexponent(1 - EXP_BIAS);
2✔
1034
                                                setsign(true);
2✔
1035
                                        }
1036
                                }
1037
                                else {
1038
                                        bool borrow = true;
66,948✔
1039
                                        for (unsigned i = 0; i < MSU; ++i) {
199,441✔
1040
                                                if (borrow) {
132,493✔
1041
                                                        if ((_block[i] & storageMask) == 0) { // block will underflow
67,209✔
1042
                                                                --_block[i];
266✔
1043
                                                                borrow = true;
266✔
1044
                                                        }
1045
                                                        else {
1046
                                                                --_block[i];
66,943✔
1047
                                                                borrow = false;
66,943✔
1048
                                                        }
1049
                                                }
1050
                                        }
1051
                                        if (borrow) {
66,948✔
1052
                                                --_block[MSU];
5✔
1053
                                        }
1054
                                        if constexpr (!hasSubnormals) {
1055
                                                if (isdenormal()) {
384✔
1056
                                                        // special case, we need to jump past all the subnormal value encodings which puts us on 0
1057
                                                        setzero(); // pattern: 0.00.000 = +0
2✔
1058
                                                }
1059
                                        }
1060
                                }
1061
                        }
1062
                }
1063
                return *this;
5,014,100✔
1064
        }
1065
        constexpr cfloat operator--(int) {
134,812✔
1066
                cfloat tmp(*this);
134,812✔
1067
                operator--();
134,812✔
1068
                return tmp;
134,812✔
1069
        }
1070

1071
        // modifiers        
1072
        constexpr void clear() noexcept {
143,250,820✔
1073
                if constexpr (0 == nrBlocks) {
1074
                        return;
1075
                }
1076
                else if constexpr (1 == nrBlocks) {
1077
                        _block[0] = bt(0);
41,085,053✔
1078
                }
1079
                else if constexpr (2 == nrBlocks) {
1080
                        _block[0] = bt(0);
48,058,422✔
1081
                        _block[1] = bt(0);
48,058,422✔
1082
                }
1083
                else if constexpr (3 == nrBlocks) {
1084
                        _block[0] = bt(0);
53,973,317✔
1085
                        _block[1] = bt(0);
53,973,317✔
1086
                        _block[2] = bt(0);
53,973,317✔
1087
                }
1088
                else if constexpr (4 == nrBlocks) {
1089
                        _block[0] = bt(0);
43,348✔
1090
                        _block[1] = bt(0);
43,348✔
1091
                        _block[2] = bt(0);
43,348✔
1092
                        _block[3] = bt(0);
43,348✔
1093
                }
1094
                else if constexpr (5 == nrBlocks) {
1095
                        _block[0] = bt(0);
29,999✔
1096
                        _block[1] = bt(0);
29,999✔
1097
                        _block[2] = bt(0);
29,999✔
1098
                        _block[3] = bt(0);
29,999✔
1099
                        _block[4] = bt(0);
29,999✔
1100
                }
1101
                else if constexpr (6 == nrBlocks) {
1102
                        _block[0] = bt(0);
10,015✔
1103
                        _block[1] = bt(0);
10,015✔
1104
                        _block[2] = bt(0);
10,015✔
1105
                        _block[3] = bt(0);
10,015✔
1106
                        _block[4] = bt(0);
10,015✔
1107
                        _block[5] = bt(0);
10,015✔
1108
                }
1109
                else if constexpr (7 == nrBlocks) {
1110
                        _block[0] = bt(0);
9,999✔
1111
                        _block[1] = bt(0);
9,999✔
1112
                        _block[2] = bt(0);
9,999✔
1113
                        _block[3] = bt(0);
9,999✔
1114
                        _block[4] = bt(0);
9,999✔
1115
                        _block[5] = bt(0);
9,999✔
1116
                        _block[6] = bt(0);
9,999✔
1117
                }
1118
                else if constexpr (8 == nrBlocks) {
1119
                        _block[0] = bt(0);
20,320✔
1120
                        _block[1] = bt(0);
20,320✔
1121
                        _block[2] = bt(0);
20,320✔
1122
                        _block[3] = bt(0);
20,320✔
1123
                        _block[4] = bt(0);
20,320✔
1124
                        _block[5] = bt(0);
20,320✔
1125
                        _block[6] = bt(0);
20,320✔
1126
                        _block[7] = bt(0);
20,320✔
1127
                }
1128
                else {
1129
                        for (unsigned i = 0; i < nrBlocks; ++i) {
224,339✔
1130
                                _block[i] = bt(0);
203,992✔
1131
                        }
1132
                }
1133
        }
143,250,820✔
1134
        constexpr void setzero() noexcept { clear(); }
2,617,277✔
1135
        constexpr void setinf(bool sign = true) noexcept {
6,548,614✔
1136
                // the Inf encoding is the pattern 0b0'11...11'11...10 for a +inf, and 0b1'11...11'11...110 for a -inf
1137
                if constexpr (0 == nrBlocks) {
1138
                        return;
1139
                }
1140
                else if constexpr (1 == nrBlocks) {
1141
                        _block[MSU] = sign ? bt(MSU_MASK ^ LSB_BIT_MASK) : bt(~SIGN_BIT_MASK & (MSU_MASK ^ LSB_BIT_MASK));
2,192,457✔
1142
                }
1143
                else if constexpr (2 == nrBlocks) {
1144
                        _block[0] = BLOCK_MASK ^ LSB_BIT_MASK;
1,859,453✔
1145
                        _block[MSU] = sign ? MSU_MASK : bt(~SIGN_BIT_MASK & MSU_MASK);
1,859,453✔
1146
                }
1147
                else if constexpr (3 == nrBlocks) {
1148
                        _block[0] = BLOCK_MASK ^ LSB_BIT_MASK;
2,493,816✔
1149
                        _block[1] = BLOCK_MASK;
2,493,816✔
1150
                        _block[MSU] = sign ? MSU_MASK : bt(~SIGN_BIT_MASK & MSU_MASK);
2,493,816✔
1151
                }
1152
                else if constexpr (4 == nrBlocks) {
1153
                        _block[0] = BLOCK_MASK ^ LSB_BIT_MASK;
2,613✔
1154
                        _block[1] = BLOCK_MASK;
2,613✔
1155
                        _block[2] = BLOCK_MASK;
2,613✔
1156
                        _block[MSU] = sign ? MSU_MASK : bt(~SIGN_BIT_MASK & MSU_MASK);
2,613✔
1157
                }
1158
                else if constexpr (5 == nrBlocks) {
1159
                        _block[0] = BLOCK_MASK ^ LSB_BIT_MASK;
73✔
1160
                        _block[1] = BLOCK_MASK;
73✔
1161
                        _block[2] = BLOCK_MASK;
73✔
1162
                        _block[3] = BLOCK_MASK;
73✔
1163
                        _block[MSU] = sign ? MSU_MASK : bt(~SIGN_BIT_MASK & MSU_MASK);
73✔
1164
                }
1165
                else if constexpr (6 == nrBlocks) {
1166
                        _block[0] = BLOCK_MASK ^ LSB_BIT_MASK;
42✔
1167
                        _block[1] = BLOCK_MASK;
42✔
1168
                        _block[2] = BLOCK_MASK;
42✔
1169
                        _block[3] = BLOCK_MASK;
42✔
1170
                        _block[4] = BLOCK_MASK;
42✔
1171
                        _block[MSU] = sign ? MSU_MASK : bt(~SIGN_BIT_MASK & MSU_MASK);
42✔
1172
                }
1173
                else if constexpr (7 == nrBlocks) {
1174
                        _block[0] = BLOCK_MASK ^ LSB_BIT_MASK;
37✔
1175
                        _block[1] = BLOCK_MASK;
37✔
1176
                        _block[2] = BLOCK_MASK;
37✔
1177
                        _block[3] = BLOCK_MASK;
37✔
1178
                        _block[4] = BLOCK_MASK;
37✔
1179
                        _block[5] = BLOCK_MASK;
37✔
1180
                        _block[MSU] = sign ? MSU_MASK : bt(~SIGN_BIT_MASK & MSU_MASK);
37✔
1181
                }
1182
                else if constexpr (8 == nrBlocks) {
1183
                        _block[0] = BLOCK_MASK ^ LSB_BIT_MASK;
85✔
1184
                        _block[1] = BLOCK_MASK;
85✔
1185
                        _block[2] = BLOCK_MASK;
85✔
1186
                        _block[3] = BLOCK_MASK;
85✔
1187
                        _block[4] = BLOCK_MASK;
85✔
1188
                        _block[5] = BLOCK_MASK;
85✔
1189
                        _block[6] = BLOCK_MASK;
85✔
1190
                        _block[MSU] = sign ? MSU_MASK : bt(~SIGN_BIT_MASK & MSU_MASK);
85✔
1191
                }
1192
                else {
1193
                        _block[0] = BLOCK_MASK ^ LSB_BIT_MASK;
38✔
1194
                        for (unsigned i = 1; i < nrBlocks - 1; ++i) {
342✔
1195
                                _block[i] = BLOCK_MASK;
304✔
1196
                        }
1197
                        _block[MSU] = sign ? MSU_MASK : bt(~SIGN_BIT_MASK & MSU_MASK);
38✔
1198
                }        
1199
        }
6,548,614✔
1200
        constexpr void setnan(int NaNType = NAN_TYPE_SIGNALLING) noexcept {
7,118,293✔
1201
                // the NaN encoding is the pattern 0b0'11...11'11...11 for a quiet Nan, and 0b1'11...11'11...111 for a signalling NaN
1202
                if constexpr (0 == nrBlocks) {
1203
                        return;
1204
                }
1205
                else if constexpr (1 == nrBlocks) {
1206
                        // fall through
1207
                }
1208
                else if constexpr (2 == nrBlocks) {
1209
                        _block[0] = BLOCK_MASK;
1,994,923✔
1210
                }
1211
                else if constexpr (3 == nrBlocks) {
1212
                        _block[0] = BLOCK_MASK;
1,048,703✔
1213
                        _block[1] = BLOCK_MASK;
1,048,703✔
1214
                }
1215
                else if constexpr (4 == nrBlocks) {
1216
                        _block[0] = BLOCK_MASK;
30✔
1217
                        _block[1] = BLOCK_MASK;
30✔
1218
                        _block[2] = BLOCK_MASK;
30✔
1219
                }
1220
                else if constexpr (5 == nrBlocks) {
1221
                        _block[0] = BLOCK_MASK;
8✔
1222
                        _block[1] = BLOCK_MASK;
8✔
1223
                        _block[2] = BLOCK_MASK;
8✔
1224
                        _block[3] = BLOCK_MASK;
8✔
1225
                }
1226
                else if constexpr (6 == nrBlocks) {
1227
                        _block[0] = BLOCK_MASK;
4✔
1228
                        _block[1] = BLOCK_MASK;
4✔
1229
                        _block[2] = BLOCK_MASK;
4✔
1230
                        _block[3] = BLOCK_MASK;
4✔
1231
                        _block[4] = BLOCK_MASK;
4✔
1232
                }
1233
                else if constexpr (7 == nrBlocks) {
1234
                        _block[0] = BLOCK_MASK;
4✔
1235
                        _block[1] = BLOCK_MASK;
4✔
1236
                        _block[2] = BLOCK_MASK;
4✔
1237
                        _block[3] = BLOCK_MASK;
4✔
1238
                        _block[4] = BLOCK_MASK;
4✔
1239
                        _block[5] = BLOCK_MASK;
4✔
1240
                }
1241
                else if constexpr (8 == nrBlocks) {
1242
                        _block[0] = BLOCK_MASK;
4✔
1243
                        _block[1] = BLOCK_MASK;
4✔
1244
                        _block[2] = BLOCK_MASK;
4✔
1245
                        _block[3] = BLOCK_MASK;
4✔
1246
                        _block[4] = BLOCK_MASK;
4✔
1247
                        _block[5] = BLOCK_MASK;
4✔
1248
                        _block[6] = BLOCK_MASK;
4✔
1249
                }
1250
                else {
1251
                        for (unsigned i = 0; i < nrBlocks - 1; ++i) {
60✔
1252
                                _block[i] = BLOCK_MASK;
54✔
1253
                        }
1254
                }
1255
                _block[MSU] = NaNType == NAN_TYPE_SIGNALLING ? MSU_MASK : bt(~SIGN_BIT_MASK & MSU_MASK);
7,118,293✔
1256
        }
7,118,293✔
1257
        constexpr void setsign(bool sign = true) {
3,402,972✔
1258
                if (sign) {
3,402,972✔
1259
                        _block[MSU] |= SIGN_BIT_MASK;
614,583✔
1260
                }
1261
                else {
1262
                        _block[MSU] &= ~SIGN_BIT_MASK;
2,788,389✔
1263
                }
1264
        }
3,402,972✔
1265
        constexpr bool setexponent(int scale) {
533,847✔
1266
                if (scale < MIN_EXP_SUBNORMAL || scale > MAX_EXP) return false; // this scale cannot be represented
533,847✔
1267
                if constexpr (nbits < 65) {
1268
                        uint32_t exponentBits = static_cast<uint32_t>(scale + EXP_BIAS);
532,306✔
1269
                        if (scale >= MIN_EXP_SUBNORMAL && scale < MIN_EXP_NORMAL) {
532,306✔
1270
                                // setexponent() only writes the encoding field. In the subnormal interval the true scale is carried
1271
                                // by a zero exponent field plus a left-shifted fraction, so callers must have already denormalized
1272
                                // the significand before asking for this exponent pattern.
1273
                                // we are a subnormal number: all exponent bits are 0
1274
                                exponentBits = 0;
46✔
1275
                        }
1276
                        // TODO: optimize
1277
                        uint32_t mask = (1ul << (es - 1));
532,306✔
1278
                        for (unsigned i = nbits - 2; i > nbits - 2 - es; --i) {
4,800,607✔
1279
                                setbit(i, (mask & exponentBits));
4,268,301✔
1280
                                mask >>= 1;
4,268,301✔
1281
                        }
1282
                }
1283
                else {
1284
                        uint32_t exponentBits = static_cast<uint32_t>(scale + EXP_BIAS);
1,541✔
1285
                        uint32_t mask = (1ul << (es - 1));
1,541✔
1286
                        for (unsigned i = nbits - 2; i > nbits - 2 - es; --i) {
25,156✔
1287
                                setbit(i, (mask & exponentBits));
23,615✔
1288
                                mask >>= 1;
23,615✔
1289
                        }
1290
                }
1291
                return true;
533,847✔
1292
        }
1293
        constexpr void setfraction(uint64_t raw_bits) {
610✔
1294
                // unoptimized as it is not meant to be an end-user API, it is a test API
1295
                // raw_bits is uint64_t so can have at most 64 bits of fraction data
1296
                constexpr unsigned bitsToSet = (fbits < 64) ? fbits : 64;
610✔
1297
                uint64_t mask{ 1ull };
610✔
1298
                for (unsigned i = 0; i < bitsToSet; ++i) {
24,744✔
1299
                        setbit(i, (mask & raw_bits));
24,134✔
1300
                        mask <<= 1;
24,134✔
1301
                }
1302
        }
610✔
1303
        constexpr void setbit(unsigned i, bool v = true) noexcept {
8,137,638✔
1304
                unsigned blockIndex = i / bitsInBlock;
8,137,638✔
1305
                if (blockIndex < nrBlocks) {
8,137,638✔
1306
                        bt block = _block[blockIndex];
8,137,638✔
1307
                        bt null = ~(1ull << (i % bitsInBlock));
8,137,638✔
1308
                        bt bit = bt(v ? 1 : 0);
8,137,638✔
1309
                        bt mask = bt(bit << (i % bitsInBlock));
8,137,638✔
1310
                        _block[blockIndex] = bt((block & null) | mask);
8,137,638✔
1311
                }
1312
        }
8,137,638✔
1313
        constexpr cfloat& setbits(uint64_t raw_bits) noexcept {
307,366,003✔
1314
                if constexpr (0 == nrBlocks) {
1315
                        return *this;
1316
                }
1317
                else if constexpr (1 == nrBlocks) {
1318
                        _block[0] = raw_bits & storageMask;
89,991,578✔
1319
                }
1320
                else if constexpr (2 == nrBlocks) {
1321
                        if constexpr (bitsInBlock < 64) {
1322
                                _block[0] = raw_bits & storageMask;
103,256,714✔
1323
                                raw_bits >>= bitsInBlock;
103,256,714✔
1324
                                _block[1] = raw_bits & storageMask;
103,256,714✔
1325
                        }
1326
                        else {
1327
                                _block[0] = raw_bits & storageMask;
1328
                                _block[1] = 0;
1329
                        }
1330
                }
1331
                else if constexpr (3 == nrBlocks) {
1332
                        if constexpr (bitsInBlock < 64) {
1333
                                _block[0] = raw_bits & storageMask;
113,957,441✔
1334
                                raw_bits >>= bitsInBlock;
113,957,441✔
1335
                                _block[1] = raw_bits & storageMask;
113,957,441✔
1336
                                raw_bits >>= bitsInBlock;
113,957,441✔
1337
                                _block[2] = raw_bits & storageMask;
113,957,441✔
1338
                        }
1339
                        else {
1340
                                _block[0] = raw_bits & storageMask;
1341
                                _block[1] = 0;
1342
                                _block[2] = 0;
1343
                        }
1344
                }
1345
                else if constexpr (4 == nrBlocks) {
1346
                        if constexpr (bitsInBlock < 64) {
1347
                                _block[0] = raw_bits & storageMask;
70,254✔
1348
                                raw_bits >>= bitsInBlock;
70,254✔
1349
                                _block[1] = raw_bits & storageMask;
70,254✔
1350
                                raw_bits >>= bitsInBlock;
70,254✔
1351
                                _block[2] = raw_bits & storageMask;
70,254✔
1352
                                raw_bits >>= bitsInBlock;
70,254✔
1353
                                _block[3] = raw_bits & storageMask;
70,254✔
1354
                        }
1355
                        else {
1356
                                _block[0] = raw_bits & storageMask;
1357
                                _block[1] = 0;
1358
                                _block[2] = 0;
1359
                                _block[3] = 0;
1360
                        }
1361
                }
1362
                else {
1363
                        if constexpr (bitsInBlock < 64) {
1364
                                for (unsigned i = 0; i < nrBlocks; ++i) {
730,731✔
1365
                                        _block[i] = raw_bits & storageMask;
640,715✔
1366
                                        raw_bits >>= bitsInBlock;
640,715✔
1367
                                }
1368
                        }
1369
                        else {
1370
                                _block[0] = raw_bits & storageMask;
1371
                                for (unsigned i = 1; i < nrBlocks; ++i) {
1372
                                        _block[i] = 0;
1373
                                }
1374
                        }
1375
                }
1376
                _block[MSU] &= MSU_MASK; // enforce precondition for fast comparison by properly nulling bits that are outside of nbits
307,366,003✔
1377
                return *this;
307,366,003✔
1378
        }
1379
        constexpr void setblock(unsigned b, bt data) noexcept {
542,374✔
1380
                if (b < nrBlocks) _block[b] = data;
542,374✔
1381
        }
542,374✔
1382
        
1383
        // create specific number system values of interest
1384
        constexpr cfloat& maxpos() noexcept {
936,034✔
1385
                if constexpr (isSaturating) {
1386
                        // in a saturating encoding with max-exponent values we are removing the Inf encoding pattern 0b0'11...11'11...10 for a +inf, 
1387
                        // and 0b1'11...11'11...110 for a -inf and using it as a value
1388
                        if constexpr (hasMaxExpValues) {
1389
                                // maximum positive value has this bit pattern: 0-1...1-111...110, that is, sign = 0, e = 11..11, f = 111...110
1390
                                clear();
935,334✔
1391
                                flip();
935,334✔
1392
                                setbit(nbits - 1ull, false); // sign = 0
935,334✔
1393
                                setbit(0ull, false); // bit0 = 0
935,334✔
1394
                        }
1395
                        else {
1396
                                // maximum positive value has this bit pattern: 0-11...10-111...111, that is, sign = 0, e = 11..10, f = 111...111
1397
                                clear();
419✔
1398
                                flip();
419✔
1399
                                setbit(fbits, false); // set least significant exponent bit to 0
419✔
1400
                                setbit(nbits - 1ull, false); // set sign to 0
419✔
1401
                        }
1402
                }
1403
                else {
1404
                        // the Inf encoding is the pattern 0b0'11...11'11...10 for a +inf, and 0b1'11...11'11...110 for a -inf
1405
                        // the maxpos is the encoding before that
1406
                        if constexpr (hasMaxExpValues) {
1407
                                // maximum positive value has this bit pattern: 0-1...1-111...101, that is, sign = 0, e = 11..11, f = 111...101
1408
                                clear();
113✔
1409
                                flip();
113✔
1410
                                setbit(nbits - 1ull, false); // sign = 0
113✔
1411
                                setbit(1ull, false); // bit1 = 0
113✔
1412
                        }
1413
                        else {
1414
                                // maximum positive value has this bit pattern: 0-1...0-111...111, that is, sign = 0, e = 11..10, f = 111...111
1415
                                clear();
168✔
1416
                                flip();
168✔
1417
                                setbit(fbits, false); // set least significant exponent bit to 0
168✔
1418
                                setbit(nbits - 1ull, false); // set sign to 0
168✔
1419
                        }
1420
                }
1421
                return *this;
936,034✔
1422
        }
1423
        constexpr cfloat& minpos() noexcept {
13,290✔
1424
                // minpos encoding is not impacted by saturating encodings, which only affects maxpos and inf
1425
                if constexpr (hasSubnormals) {
1426
                        // minimum positive value has this bit pattern: 0-000-00...01, that is, sign = 0, e = 000, f = 00001
1427
                        clear();
327✔
1428
                        setbit(0);
327✔
1429
                }
1430
                else {
1431
                        // minimum positive value has this bit pattern: 0-001-00...0, that is, sign = 0, e = 001, f = 0000
1432
                        clear();
12,963✔
1433
                        setbit(fbits);
12,963✔
1434
                }
1435
                return *this;
13,290✔
1436
        }
1437
        constexpr cfloat& zero() noexcept {
1✔
1438
                // the zero value
1439
                clear();
1✔
1440
                return *this;
1✔
1441
        }
1442
        constexpr cfloat& minneg() noexcept {
14✔
1443
                // minneg encoding is not impacted by saturating encodings, which only affects maxpos and inf
1444
                if constexpr (hasSubnormals) {
1445
                        // minimum negative value has this bit pattern: 1-000-00...01, that is, sign = 1, e = 00, f = 00001
1446
                        clear();
8✔
1447
                        setbit(nbits - 1ull);
8✔
1448
                        setbit(0);
8✔
1449
                }
1450
                else {
1451
                        // minimum negative value has this bit pattern: 1-001-00...0, that is, sign = 1, e = 001, f = 0000
1452
                        clear();
6✔
1453
                        setbit(fbits);
6✔
1454
                        setbit(nbits - 1ull);
6✔
1455
                }
1456
                return *this;
14✔
1457
        }
1458
        constexpr cfloat& maxneg() noexcept {
935,703✔
1459
                if constexpr (isSaturating) {
1460
                        // in a saturating encoding with max-exponent values we are removing the Inf encoding pattern 0b0'11...11'11...10 for a +inf, 
1461
                        // and 0b1'11...11'11...110 for a -inf and using it as a value
1462
                        if constexpr (hasMaxExpValues) {
1463
                                // maximum negative value has this bit pattern: 1-1...1-111...110, that is, sign = 1, e = 1..1, f = 111...110
1464
                                clear();
935,240✔
1465
                                flip();
935,240✔
1466
                                setbit(0ull, false);
935,240✔
1467
                        }
1468
                        else {
1469
                                // maximum negative value has this bit pattern: 1-1...0-111...111, that is, sign = 1, e = 11..10, f = 111...111
1470
                                clear();
402✔
1471
                                flip();
402✔
1472
                                setbit(fbits, false);
402✔
1473
                        }
1474
                }
1475
                else {
1476
                        if constexpr (hasMaxExpValues) {
1477
                                // maximum negative value has this bit pattern: 1-1...1-111...101, that is, sign = 1, e = 1..1, f = 111...101
1478
                                clear();
12✔
1479
                                flip();
12✔
1480
                                setbit(1ull, false);
12✔
1481
                        }
1482
                        else {
1483
                                // maximum negative value has this bit pattern: 1-1...0-111...111, that is, sign = 1, e = 11..10, f = 111...111
1484
                                clear();
49✔
1485
                                flip();
49✔
1486
                                setbit(fbits, false);
49✔
1487
                        }
1488
                }
1489
                return *this;
935,703✔
1490
        }
1491

1492

1493
        /// <summary>
1494
        /// assign the value of the string representation to the cfloat
1495
        /// </summary>
1496
        /// <param name="stringRep">decimal scientific notation of a real number to be assigned</param>
1497
        /// <returns>reference to this cfloat</returns>
1498
        /// Clang doesn't support constexpr yet on string manipulations, so we need to make it conditional
1499
        CONSTEXPRESSION cfloat& assign(const std::string& str) noexcept {
7✔
1500
                clear();
7✔
1501
                unsigned nrChars = static_cast<unsigned>(str.size());
7✔
1502
                unsigned nrBits = 0;
7✔
1503
                unsigned nrDots = 0;
7✔
1504
                std::string bits;
7✔
1505
                if (nrChars > 2) {
7✔
1506
                        if (str[0] == '0' && str[1] == 'b') {
7✔
1507
                                for (unsigned i = 2; i < nrChars; ++i) {
211✔
1508
                                        char c = str[i];
204✔
1509
                                        switch (c) {
204✔
1510
                                        case '0':
186✔
1511
                                        case '1':
1512
                                                ++nrBits;
186✔
1513
                                                bits += c;
186✔
1514
                                                break;
186✔
1515
                                        case '.':
14✔
1516
                                                ++nrDots;
14✔
1517
                                                bits += c;
14✔
1518
                                                break;
14✔
1519
                                        case '\'':
4✔
1520
                                                // consume this delimiting character
1521
                                                break;
4✔
1522
                                        default:
×
1523
                                                std::cerr << "string contained a non-standard character: " << c << '\n';
×
1524
                                                return *this;
×
1525
                                        }
1526
                                }
1527
                        }
1528
                        else {
1529
                                std::cerr << "string must start with 0b: instead input pattern was " << str << '\n';
×
1530
                                return *this;
×
1531
                        }
1532
                }
1533
                else {
1534
                        std::cerr << "string is too short\n";
×
1535
                        return *this;
×
1536
                }
1537

1538
                if (nrBits != nbits) {
7✔
1539
                        std::cerr << "number of bits in the string is " << nrBits << " and needs to be " << nbits << '\n';
×
1540
                        return *this;
×
1541
                }
1542
                if (nrDots != 2) {
7✔
1543
                        std::cerr << "number of segment delimiters in string is " << nrDots << " and needs to be 2 for a cfloat<>\n";
×
1544
                        return *this;
×
1545
                }
1546

1547
                // assign the bits
1548
                int field{ 0 };  // three fields: sign, exponent, mantissa: fields are separated by a '.'
7✔
1549
                int nrExponentBits{ -1 };
7✔
1550
                unsigned bit = nrBits;
7✔
1551
                for (unsigned i = 0; i < bits.size(); ++i) {
207✔
1552
                        char c = bits[i];
200✔
1553
                        if (c == '.') {
200✔
1554
                                ++field;
14✔
1555
                                if (field == 2) { // just finished parsing exponent field: we can now check the number of exponent bits
14✔
1556
                                        if (nrExponentBits != es) {
7✔
1557
                                                std::cerr << "provided binary string representation does not contain " << es << " exponent bits. Found " << nrExponentBits << ". Reset to 0\n";
×
1558
                                                clear();
×
1559
                                                return *this;
×
1560
                                        }
1561
                                }
1562
                        }
1563
                        else {
1564
                                setbit(--bit, c == '1');
186✔
1565
                        }
1566
                        if (field == 1) { // exponent field
200✔
1567
                                ++nrExponentBits;
51✔
1568
                        }
1569
                }
1570
                if (field != 2) {
7✔
1571
                        std::cerr << "provided binary string did not contain three fields separated by '.': Reset to 0\n";
×
1572
                        clear();
×
1573
                        return *this;
×
1574
                }
1575
                return *this;
7✔
1576
        }
7✔
1577

1578
        // selectors
1579
        constexpr bool sign() const noexcept { return (_block[MSU] & SIGN_BIT_MASK) == SIGN_BIT_MASK; }
259,302,611✔
1580
        constexpr int  scale() const noexcept {
62,209,960✔
1581
                int e{ 0 };
62,209,960✔
1582
                if constexpr (MSU_CAPTURES_EXP) {
1583
                        e = static_cast<int>((_block[MSU] & ~SIGN_BIT_MASK) >> EXP_SHIFT);
50,558,381✔
1584
                        if (e == 0) {
50,558,381✔
1585
                                // subnormal scale is determined by fraction
1586
                                // subnormals: (-1)^s * 2^(2-2^(es-1)) * (f/2^fbits))
1587
                                e = (2l - (1l << (es - 1ull))) - 1;
6,564,439✔
1588
                                if constexpr (nbits > 2 + es) {
1589
                                        for (unsigned i = nbits - 2ull - es; i > 0; --i) {
12,635,275✔
1590
                                                if (test(i)) break;
12,529,385✔
1591
                                                --e;
6,093,372✔
1592
                                        }
1593
                                }
1594
                        }
1595
                        else {
1596
                                e -= EXP_BIAS;
43,993,942✔
1597
                        }
1598
                }
1599
                else {
1600
                        blockbinary<es, bt> ebits{};
11,651,930✔
1601
                        exponent(ebits);
11,651,579✔
1602
                        if (ebits.iszero()) {
11,651,579✔
1603
                                // subnormal scale is determined by fraction
1604
                                // subnormals: (-1)^s * 2^(2-2^(es-1)) * (f/2^fbits))
1605
                                e = (2l - (1l << (es - 1ull))) - 1;
1,599,672✔
1606
                                if constexpr (nbits > 2 + es) {
1607
                                        for (unsigned i = nbits - 2ull - es; i > 0; --i) {
3,160,477✔
1608
                                                if (test(i)) break;
3,153,355✔
1609
                                                --e;
1,560,816✔
1610
                                        }
1611
                                }
1612
                        }
1613
                        else {
1614
                                e = static_cast<int>(unsigned(ebits) - EXP_BIAS);
10,051,907✔
1615
                        }
1616
                }
1617
                return e;
62,209,960✔
1618
        }
1619
        constexpr bool isneg() const noexcept {
558✔
1620
                if (isnan()) return false;
558✔
1621
                return sign(); 
541✔
1622
        }
1623
        constexpr bool ispos() const noexcept { 
34,477✔
1624
                if (isnan()) return false;
34,477✔
1625
                return !sign(); 
34,477✔
1626
        }
1627
        constexpr bool iszero() const noexcept {
430,823,623✔
1628
                // NOTE: this is a very specific design that makes the decsion that
1629
                // for subnormal encodings found in a configuration that doesn't
1630
                // support them, we assume that these values map to 0.
1631
                if constexpr (hasSubnormals) {
1632
                        return iszeroencoding();
251,714,026✔
1633
                }
1634
                else { // all subnormals round to 0
1635
                        blockbinary<es, bt> ebits{};
179,110,039✔
1636
                        exponent(ebits);
179,109,597✔
1637
                        if (ebits.iszero()) return true; else return false;
179,109,597✔
1638
                }
1639
        }
1640
        constexpr bool isone() const noexcept {
1641
                // unbiased exponent = scale = 0, fraction = 0
1642
                int s = scale();
1643
                if (s == 0) {
1644
                        blockbinary<fbits, bt> f{};
1645
                        fraction(f);
1646
                        return f.iszero();
1647
                }
1648
                return false;
1649
        }
1650
        constexpr bool isinf(int InfType = INF_TYPE_EITHER) const noexcept {
383,825,671✔
1651
                // the bit pattern encoding of Inf is independent of the max-exponent value configuration
1652
                bool isNegInf = false;
383,825,671✔
1653
                bool isPosInf = false;
383,825,671✔
1654
                if constexpr (0 == nrBlocks) {
1655
                        return false;
1656
                }
1657
                else if constexpr (1 == nrBlocks) {
1658
                        isNegInf = (_block[MSU] & MSU_MASK) == (MSU_MASK ^ LSB_BIT_MASK);
171,946,785✔
1659
                        isPosInf = (_block[MSU] & MSU_MASK) == ((MSU_MASK ^ SIGN_BIT_MASK) ^ LSB_BIT_MASK);
171,946,785✔
1660
                }
1661
                else if constexpr (2 == nrBlocks) {
1662
                        bool isInf = (_block[0] == (BLOCK_MASK ^ LSB_BIT_MASK));
145,286,457✔
1663
                        isNegInf = isInf && ((_block[MSU] & MSU_MASK) == MSU_MASK);
145,286,457✔
1664
                        isPosInf = isInf && (_block[MSU] & MSU_MASK) == (MSU_MASK ^ SIGN_BIT_MASK);
145,286,457✔
1665
                }
1666
                else if constexpr (3 == nrBlocks) {
1667
                        bool isInf = (_block[0] == (BLOCK_MASK ^ LSB_BIT_MASK)) && (_block[1] == BLOCK_MASK);
66,400,023✔
1668
                        isNegInf = isInf && ((_block[MSU] & MSU_MASK) == MSU_MASK);
66,400,023✔
1669
                        isPosInf = isInf && (_block[MSU] & MSU_MASK) == (MSU_MASK ^ SIGN_BIT_MASK);
66,400,023✔
1670
                }
1671
                else if constexpr (4 == nrBlocks) {
1672
                        bool isInf = (_block[0] == (BLOCK_MASK ^ LSB_BIT_MASK)) && (_block[1] == BLOCK_MASK) && (_block[2] == BLOCK_MASK);
100,284✔
1673
                        isNegInf = isInf && ((_block[MSU] & MSU_MASK) == MSU_MASK);
100,284✔
1674
                        isPosInf = isInf && (_block[MSU] & MSU_MASK) == (MSU_MASK ^ SIGN_BIT_MASK);
100,284✔
1675
                }
1676
                else {
1677
                        bool isInf = (_block[0] == (BLOCK_MASK ^ LSB_BIT_MASK));
92,122✔
1678
                        for (unsigned i = 1; i < nrBlocks - 1; ++i) {
93,748✔
1679
                                if (_block[i] != BLOCK_MASK) {
93,439✔
1680
                                        isInf = false;
91,813✔
1681
                                        break;
91,813✔
1682
                                }
1683
                        }
1684
                        isNegInf = isInf && ((_block[MSU] & MSU_MASK) == MSU_MASK);
92,122✔
1685
                        isPosInf = isInf && (_block[MSU] & MSU_MASK) == (MSU_MASK ^ SIGN_BIT_MASK);
92,122✔
1686
                }
1687

1688
                return (InfType == INF_TYPE_EITHER ? (isNegInf || isPosInf) :
442,116,092✔
1689
                        (InfType == INF_TYPE_NEGATIVE ? isNegInf :
87,437,173✔
1690
                                (InfType == INF_TYPE_POSITIVE ? isPosInf : false)));
442,119,175✔
1691
        }
58,290,421✔
1692
        constexpr bool isnan(int NaNType = NAN_TYPE_EITHER) const noexcept {
1,002,444,547✔
1693
                if constexpr (hasMaxExpValues) {
1694
                        return isnanencoding(NaNType);
596,765,886✔
1695
                }
1696
                else {
1697
                        if (ismaxexpvalue()) {
405,678,661✔
1698
                                // all these max-exponent encodings are NANs, except for the encoding representing INF
1699
                                bool isNaN = isinf() ? false : true;
24,520,168✔
1700
                                bool isNegNaN = isNaN && sign();
24,520,168✔
1701
                                bool isPosNaN = isNaN && !sign();
24,520,168✔
1702
                                return (NaNType == NAN_TYPE_EITHER ? (isNaN) :
27,937,416✔
1703
                                        (NaNType == NAN_TYPE_SIGNALLING ? isNegNaN :
4,551,925✔
1704
                                                (NaNType == NAN_TYPE_QUIET ? isPosNaN : false)));
26,789,522✔
1705
                        }
1706
                        else {
1707
                                return false;
381,158,493✔
1708
                        }
1709
                }
1710
        }
24,520,168✔
1711
        // iszeroencoding returns true if it finds a pure -0 or +0 pattern and returns false otherwise
1712
        constexpr bool iszeroencoding() const noexcept {
359,060,386✔
1713
                if constexpr (0 == nrBlocks) {
1714
                        return true;
1715
                }
1716
                else if constexpr (1 == nrBlocks) {
1717
                        return (_block[MSU] & ~SIGN_BIT_MASK) == 0;
138,299,540✔
1718
                }
1719
                else if constexpr (2 == nrBlocks) {
1720
                        return (_block[0] == 0) && (_block[MSU] & ~SIGN_BIT_MASK) == 0;
146,614,358✔
1721
                }
1722
                else if constexpr (3 == nrBlocks) {
1723
                        return (_block[0] == 0) && _block[1] == 0 && (_block[MSU] & ~SIGN_BIT_MASK) == 0;
73,918,361✔
1724
                }
1725
                else if constexpr (4 == nrBlocks) {
1726
                        return (_block[0] == 0) && _block[1] == 0 && _block[2] == 0 && (_block[MSU] & ~SIGN_BIT_MASK) == 0;
135,395✔
1727
                }
1728
                else {
1729
                        for (unsigned i = 0; i < nrBlocks - 1; ++i) if (_block[i] != 0) return false;
338,439✔
1730
                        return (_block[MSU] & ~SIGN_BIT_MASK) == 0;
555✔
1731
                }
1732
        }
1733
        // isminnegencoding returns true if it find the pattern 1.00.00001 and returns false otherwise
1734
        constexpr bool isminnegencoding() const noexcept {
66,947✔
1735
                if constexpr (0 == nrBlocks) {
1736
                        return false;
1737
                }
1738
                else if constexpr (1 == nrBlocks) {
1739
                        return (_block[MSU] & (SIGN_BIT_MASK | 1ul));
1740
                }
1741
                else if constexpr (2 == nrBlocks) {
1742
                        return ((_block[0] == 1ul) && (_block[1] == SIGN_BIT_MASK));
1,408✔
1743
                }
1744
                else if constexpr (3 == nrBlocks) {
1745
                        return ((_block[0] == 1ul) && (_block[1] == 0) && (_block[2] == SIGN_BIT_MASK));
65,536✔
1746
                }
1747
                else if constexpr (4 == nrBlocks) {
1748
                        return ((_block[0] == 1ul) && (_block[1] == 0) && (_block[2] == 0) && (_block[3] == SIGN_BIT_MASK));
3✔
1749
                }
1750
                else {
1751
                        if (_block[0] != 1ul) return false;
×
1752
                        for (unsigned i = 1; i < nrBlocks - 2; ++i) if (_block[i] != 0) return false;
×
1753
                        return (_block[MSU] == SIGN_BIT_MASK);
×
1754
                }
1755
        }
1756
        constexpr bool isnanencoding(int NaNType = NAN_TYPE_EITHER) const noexcept {
600,433,328✔
1757
                // the bit encoding of NaN is independent of the gradual overflow configuration
1758
                bool isNaN = true;
600,433,328✔
1759
                bool isNegNaN = false;
600,433,328✔
1760
                bool isPosNaN = false;
600,433,328✔
1761

1762
                if constexpr (0 == nrBlocks) {
1763
                        return false;
1764
                }
1765
                else if constexpr (1 == nrBlocks) {
1766
                }
1767
                else if constexpr (2 == nrBlocks) {
1768
                        isNaN = (_block[0] == BLOCK_MASK);
223,999,913✔
1769
                }
1770
                else if constexpr (3 == nrBlocks) {
1771
                        isNaN = (_block[0] == BLOCK_MASK) && (_block[1] == BLOCK_MASK);
201,348,544✔
1772
                }
1773
                else if constexpr (4 == nrBlocks) {
1774
                        isNaN = (_block[0] == BLOCK_MASK) && (_block[1] == BLOCK_MASK) && (_block[2] == BLOCK_MASK);
226,938✔
1775
                }
1776
                else {
1777
                        for (unsigned i = 0; i < nrBlocks - 1; ++i) {
270,075✔
1778
                                if (_block[i] != BLOCK_MASK) {
270,033✔
1779
                                        isNaN = false;
269,787✔
1780
                                        break;
269,787✔
1781
                                }
1782
                        }
1783
                }
1784
                isNegNaN = isNaN && ((_block[MSU] & MSU_MASK) == MSU_MASK);
600,433,328✔
1785
                isPosNaN = isNaN && (_block[MSU] & MSU_MASK) == (MSU_MASK ^ SIGN_BIT_MASK);
600,433,328✔
1786

1787
                return (NaNType == NAN_TYPE_EITHER ? (isNegNaN || isPosNaN) :
816,798,582✔
1788
                        (NaNType == NAN_TYPE_SIGNALLING ? isNegNaN :
324,400,260✔
1789
                                (NaNType == NAN_TYPE_QUIET ? isPosNaN : false)));
816,503,340✔
1790
        }
216,365,254✔
1791
        // isnormal returns true if 0 or exponent bits are not all zero or one, false otherwise
1792
        constexpr bool isnormal() const noexcept {
62,054,025✔
1793
                if (iszeroencoding()) return true; // filter out the one special case
62,054,025✔
1794
                blockbinary<es, bt> e{};
62,054,375✔
1795
                exponent(e);
62,054,024✔
1796
                return !e.iszero() && !e.all();
62,054,024✔
1797
        }
1798
        // isdenormal returns true if exponent bits are all zero, false otherwise
1799
        constexpr bool isdenormal() const noexcept {
45,225,377✔
1800
                if (iszeroencoding()) return false; // filter out the one special case
45,225,377✔
1801
                blockbinary<es, bt> e{};
45,216,598✔
1802
                exponent(e);
45,216,598✔
1803
                return e.iszero(); 
45,216,598✔
1804
        }
1805
        // ismaxexpvalue returns true if exponent bits are all one, false otherwise
1806
        constexpr bool ismaxexpvalue() const noexcept {
408,907,710✔
1807
                blockbinary<es, bt> e{};
408,908,763✔
1808
                exponent(e);
408,907,710✔
1809
                return e.all();
408,907,710✔
1810
        }
1811
        // isinteger is TBD
1812
        constexpr bool isinteger() const noexcept { return false; } // return (floor(*this) == *this) ? true : false; }
1813
        
1814
        template<typename NativeReal>
1815
        constexpr bool inrange(NativeReal v) const noexcept {
9,306,296✔
1816
                // the valid range for this cfloat includes the interval between 
1817
                // maxpos and the value that would round down to maxpos
1818
                bool bIsInRange = true;                
9,306,296✔
1819
                if (v > 0) {
9,306,296✔
1820
                        cfloat c(SpecificValue::maxpos);
4,427,047✔
1821
                        cfloat<nbits + 1, es, BlockType, hasSubnormals, hasMaxExpValues, isSaturating> d{};
8,027,370✔
1822
                        d = NativeReal(c);
4,427,047✔
1823
                        ++d;
4,427,047✔
1824
                        if (v >= NativeReal(d)) bIsInRange = false;
4,427,047✔
1825
                }
1826
                else {
1827
                        cfloat c(SpecificValue::maxneg);
4,879,249✔
1828
                        cfloat<nbits + 1, es, BlockType, hasSubnormals, hasMaxExpValues, isSaturating> d{};
8,816,662✔
1829
                        d = NativeReal(c);
4,879,249✔
1830
                        --d;
4,879,249✔
1831
                        if (v <= NativeReal(d)) bIsInRange = false;
4,879,249✔
1832
                }
1833

1834
                return bIsInRange;
9,306,296✔
1835
        }
1836
        constexpr bool test(unsigned bitIndex) const noexcept {
15,698,403✔
1837
                return at(bitIndex);
15,698,403✔
1838
        }
1839
        constexpr bool at(unsigned bitIndex) const noexcept {
1,878,979,210✔
1840
                if (bitIndex < nbits) {
1,878,979,210✔
1841
                        bt word = _block[bitIndex / bitsInBlock];
1,878,979,210✔
1842
                        bt mask = bt(1ull << (bitIndex % bitsInBlock));
1,878,979,210✔
1843
                        return (word & mask);
1,878,979,210✔
1844
                }
1845
                return false;
×
1846
        }
1847
        constexpr uint8_t nibble(unsigned n) const noexcept {
640✔
1848
                if (n < (1 + ((nbits - 1) >> 2))) {
640✔
1849
                        bt word = _block[(n * 4) / bitsInBlock];
640✔
1850
                        int nibbleIndexInWord = int(n % (bitsInBlock >> 2ull));
640✔
1851
                        bt mask = bt(0xF << (nibbleIndexInWord * 4));
640✔
1852
                        bt nibblebits = bt(mask & word);
640✔
1853
                        return uint8_t(nibblebits >> (nibbleIndexInWord * 4));
640✔
1854
                }
1855
                return 0;
×
1856
        }
1857
        constexpr bt block(unsigned b) const noexcept {
1858
                if (b < nrBlocks) {
1859
                        return _block[b];
1860
                }
1861
                return 0;
1862
        }
1863

1864
        constexpr void sign(bool& s) const {
800✔
1865
                s = sign();
800✔
1866
        }
800✔
1867
        constexpr void exponent(blockbinary<es, bt>& e) const {
813,290,448✔
1868
                e.clear();
813,290,448✔
1869
                if constexpr (0 == nrBlocks) return;
1870
                else if constexpr (1 == nrBlocks) {
1871
                        bt ebits = bt(_block[MSU] & ~SIGN_BIT_MASK);
376,749,799✔
1872
                        e.setbits(uint64_t(ebits >> EXP_SHIFT));
376,749,799✔
1873
                }
1874
                else if constexpr (nrBlocks > 1) {
1875
                        if (MSU_CAPTURES_EXP) {
1876
                                bt ebits = bt(_block[MSU] & ~SIGN_BIT_MASK);
250,093,226✔
1877
                                e.setbits(uint64_t(ebits >> ((nbits - 1ull - es) % bitsInBlock)));
250,093,226✔
1878
                        }
1879
                        else {
1880
                                for (unsigned i = 0; i < es; ++i) { e.setbit(i, at(nbits - 1ull - es + i)); }
841,378,003✔
1881
                        }
1882
                }
1883
        }
813,290,448✔
1884
        template<unsigned targetFractionBits>
1885
        constexpr blockbinary<targetFractionBits, bt>& fraction(blockbinary<targetFractionBits, bt>& f) const {
34,084✔
1886
                static_assert(targetFractionBits >= fbits, "target blockbinary is too small and can't receive all fraction bits");
1887
                f.clear();
34,084✔
1888
                if constexpr (0 == nrBlocks) return f;
1889
                else if constexpr (1 == nrBlocks) {
1890
                        bt fraction = bt(_block[MSU] & ~MSU_EXP_MASK);
1,001✔
1891
                        f.setbits(fraction);
1,001✔
1892
                }
1893
                else if constexpr (nrBlocks > 1) {
1894
                        for (unsigned i = 0; i < fbits; ++i) { f.setbit(i, at(i)); }
242,396✔
1895
                }
1896
                return f;
34,084✔
1897
        }
1898
        constexpr uint64_t fraction_ull() const {
60,998,373✔
1899
                uint64_t raw{ 0 };
60,998,373✔
1900
                if constexpr (nbits - es - 1ull < 65ull) { // no-op if precondition doesn't hold
1901
                        if constexpr (1 == nrBlocks) {
1902
                                uint64_t fbitMask = 0xFFFF'FFFF'FFFF'FFFF >> (64 - fbits);
27,943,307✔
1903
                                raw = fbitMask & uint64_t(_block[0]);
27,943,307✔
1904
                        }
1905
                        else if constexpr (2 == nrBlocks) {
1906
                                uint64_t fbitMask = 0xFFFF'FFFF'FFFF'FFFF >> (64 - fbits);
22,782,613✔
1907
                                raw = fbitMask & ((uint64_t(_block[1]) << bitsInBlock) | uint64_t(_block[0]));
22,782,613✔
1908
                        }
1909
                        else if constexpr (3 == nrBlocks) {
1910
                                uint64_t fbitMask = 0xFFFF'FFFF'FFFF'FFFF >> (64 - fbits);
10,249,633✔
1911
                                raw = fbitMask & ((uint64_t(_block[2]) << (2 * bitsInBlock)) | (uint64_t(_block[1]) << bitsInBlock) | uint64_t(_block[0]));
10,249,633✔
1912
                        }
1913
                        else if constexpr (4 == nrBlocks) {
1914
                                uint64_t fbitMask = 0xFFFF'FFFF'FFFF'FFFF >> (64 - fbits);
22,453✔
1915
                                raw = fbitMask & ((uint64_t(_block[3]) << (3 * bitsInBlock)) | (uint64_t(_block[2]) << (2 * bitsInBlock)) | (uint64_t(_block[1]) << bitsInBlock) | uint64_t(_block[0]));
22,453✔
1916
                        }
1917
                        else {
1918
                                uint64_t mask{ 1 };
367✔
1919
                                for (unsigned i = 0; i < fbits; ++i) { 
16,030✔
1920
                                        if (test(i)) {
15,663✔
1921
                                                raw |= mask;
2,329✔
1922
                                        }
1923
                                        mask <<= 1;
15,663✔
1924
                                }
1925
                        }
1926
                }
1927
                return raw;
60,998,373✔
1928
        }
1929
        // construct the significant from the encoding, returns normalization offset
1930
        constexpr unsigned significant(blockbinary<fhbits, bt>& s, bool isNormal = true) const {
23✔
1931
                unsigned shift = 0;
23✔
1932
                if (iszero()) return 0;
23✔
1933
                if constexpr (0 == nrBlocks) return 0;
1934
                else if constexpr (1 == nrBlocks) {
1935
                        bt significant = bt(_block[MSU] & ~MSU_EXP_MASK & ~SIGN_BIT_MASK);
23✔
1936
                        if (isNormal) {
23✔
1937
                                significant |= (bt(0x1ul) << fbits);
×
1938
                        }
1939
                        else {
1940
                                unsigned msb = find_msb(significant);
23✔
1941
//                                std::cout << "msb : " << msb << " : fhbits : " << fhbits << " : " << to_binary(significant, true) << std::endl;
1942
                                shift = fhbits - msb;
23✔
1943
                                significant <<= shift;
23✔
1944
                        }
1945
                        s.setbits(significant);
23✔
1946
                }
1947
                else if constexpr (nrBlocks > 1) {
1948
                        s.clear();
1949
                        // TODO: design and implement a block-oriented algorithm, this sequential algorithm is super slow
1950
                        if (isNormal) {
1951
                                s.setbit(fbits);
1952
                                for (unsigned i = 0; i < fbits; ++i) { s.setbit(i, at(i)); }
1953
                        }
1954
                        else {
1955
                                // Find the MSB of the subnormal: 
1956
                                unsigned msb = 0;
1957
                                for (unsigned i = 0; i < fbits; ++i) { // msb protected from not being assigned through iszero test at prelude of function
1958
                                        msb = fbits - 1ull - i;
1959
                                        if (test(msb)) break;
1960
                                }
1961
                                //      m-----lsb
1962
                                // h00001010101
1963
                                // 101010100000
1964
                                for (unsigned i = 0; i <= msb; ++i) {
1965
                                        s.setbit(fbits - msb + i, at(i));
1966
                                }
1967
                                shift = fhbits - msb;
1968
                        }
1969
                }
1970
                return shift;
23✔
1971
        }
1972
        template<unsigned targetbits>
1973
        constexpr void bits(blockbinary<targetbits, bt>& b) const {
640✔
1974
                unsigned upperbound = (nbits > targetbits ? targetbits : nbits);
640✔
1975
                b.clear();
640✔
1976
                for (unsigned i = 0; i < upperbound; ++i) { b.setbit(i, at(i)); }
3,840✔
1977
        }
640✔
1978

1979
        // casts to native types
1980
        // Float-to-integer conversion is undefined when the (truncated) source value
1981
        // is outside the destination type's representable range [conv.fpint]. Clamp
1982
        // to min/max before the cast. Note: float(INT_MAX) typically rounds up to
1983
        // 2^31, so we use >= (not >) to catch the boundary.
1984
        constexpr int to_int() const {
1985
                if (isnan()) return 0;
1986
                if (isinf()) return sign() ? std::numeric_limits<int>::min() : std::numeric_limits<int>::max();
1987
                float f = to_native<float>();
1988
                if (f >= static_cast<float>(std::numeric_limits<int>::max())) return std::numeric_limits<int>::max();
1989
                if (f <  static_cast<float>(std::numeric_limits<int>::min())) return std::numeric_limits<int>::min();
1990
                return int(f);
1991
        }
1992
        constexpr long to_long() const {
1993
                if (isnan()) return 0;
1994
                if (isinf()) return sign() ? std::numeric_limits<long>::min() : std::numeric_limits<long>::max();
1995
                double d = to_native<double>();
1996
                if (d >= static_cast<double>(std::numeric_limits<long>::max())) return std::numeric_limits<long>::max();
1997
                if (d <  static_cast<double>(std::numeric_limits<long>::min())) return std::numeric_limits<long>::min();
1998
                return long(d);
1999
        }
2000
        constexpr long long to_long_long() const {
1✔
2001
                if (isnan()) return 0;
1✔
2002
                if (isinf()) return sign() ? std::numeric_limits<long long>::min() : std::numeric_limits<long long>::max();
1✔
2003
                double d = to_native<double>();
1✔
2004
                if (d >= static_cast<double>(std::numeric_limits<long long>::max())) return std::numeric_limits<long long>::max();
1✔
2005
                if (d <  static_cast<double>(std::numeric_limits<long long>::min())) return std::numeric_limits<long long>::min();
1✔
2006
                return (long long)(d);
1✔
2007
        }
2008

2009
        // transform an cfloat to a native C++ floating-point. We are using the native
2010
        // precision to compute, which means that all sub-values need to be representable 
2011
        // by the native precision.
2012
        // A more accurate approximation would require an adaptive precision algorithm
2013
        // with a final rounding step.
2014
        template<typename TargetFloat>
2015
        constexpr
2016
        TargetFloat to_native() const { 
113,542,160✔
2017
                TargetFloat v{ 0.0 };
113,542,160✔
2018
                if (iszero()) {
113,542,160✔
2019
                        // the optimizer might destroy the sign
2020
                        return sign() ? -TargetFloat(0) : TargetFloat(0);
904,592✔
2021
                }
2022
                else if (isnan()) {
112,637,568✔
2023
                        v = sign() ? std::numeric_limits<TargetFloat>::signaling_NaN() : std::numeric_limits<TargetFloat>::quiet_NaN();
6,143,260✔
2024
                }
2025
                else if (isinf()) {
106,494,308✔
2026
                        v = sign() ? -std::numeric_limits<TargetFloat>::infinity() : std::numeric_limits<TargetFloat>::infinity();
144,168✔
2027
                }
2028
                else { // TODO: this approach has catastrophic cancellation when nbits is large and native target float is too small
2029
                        TargetFloat f{ 0 };
106,350,140✔
2030
                        TargetFloat fbit{ 0.5 };
106,350,140✔
2031
                        for (int i = static_cast<int>(nbits - 2ull - es); i >= 0; --i) {
1,314,448,005✔
2032
                                f += at(static_cast<unsigned>(i)) ? fbit : TargetFloat(0);
1,208,097,865✔
2033
                                fbit *= TargetFloat(0.5);
1,208,097,865✔
2034
                        }
2035
                        blockbinary<es, bt> ebits;
2036
                        exponent(ebits);
106,350,140✔
2037
                        if constexpr (hasSubnormals) {
2038
                                if (ebits.iszero()) {
66,576,544✔
2039
                                        // subnormals: (-1)^s * 2^(2-2^(es-1)) * (f/2^fbits))
2040
                                        TargetFloat exponentiation = TargetFloat(subnormal_exponent[es]); // precomputed values for 2^(2-2^(es-1))
14,668,890✔
2041
                                        v = exponentiation * f;  // f is already f/2^fbits
14,668,890✔
2042
                                        return sign() ? -v : v;
14,668,890✔
2043
                                }
2044
                        }
2045
                        else {
2046
                                if (ebits.iszero()) { // underflow to 0
39,773,596✔
2047
                                        // compiler fast float optimization might destroy the sign
2048
                                        return sign() ? -TargetFloat(0) : TargetFloat(0);
×
2049
                                }
2050
                        }
2051
                        if constexpr (hasMaxExpValues) {
2052
                                // regular: (-1)^s * 2^(e+1-2^(es-1)) * (1 + f/2^fbits))
2053
                                int exponent = static_cast<int>(unsigned(ebits) - EXP_BIAS);
52,360,645✔
2054
                                if (-64 < exponent && exponent < 64) {
52,360,645✔
2055
                                        TargetFloat exponentiation = (exponent >= 0 ? TargetFloat(1ull << exponent) : (1.0f / TargetFloat(1ull << -exponent)));
52,036,917✔
2056
                                        v = exponentiation * (TargetFloat(1.0) + f);
52,036,917✔
2057
                                }
52,036,917✔
2058
                                else {
2059
                                        double exponentiation = ipow(exponent);
323,728✔
2060
                                        v = TargetFloat(exponentiation * (1.0 + f));
323,728✔
2061
                                }
2062
                        }
2063
                        else {
2064
                                if (ebits.all()) {
39,320,605✔
2065
                                        // max-exponent values are mapped to quiet NaNs
2066
                                        v = std::numeric_limits<TargetFloat>::quiet_NaN();
×
2067
                                        return v;
×
2068
                                }
2069
                                else {
2070
                                        // regular: (-1)^s * 2^(e+1-2^(es-1)) * (1 + f/2^fbits))
2071
                                        int exponent = static_cast<int>(unsigned(ebits) - EXP_BIAS);
39,320,605✔
2072
                                        if (-64 < exponent && exponent < 64) {
39,320,605✔
2073
                                                TargetFloat exponentiation = (exponent >= 0 ? TargetFloat(1ull << exponent) : (1.0f / TargetFloat(1ull << -exponent)));
39,119,410✔
2074
                                                v = exponentiation * (TargetFloat(1.0) + f);
39,119,410✔
2075
                                        }
39,119,410✔
2076
                                        else {
2077
                                                double exponentiation = ipow(exponent);
201,195✔
2078
                                                v = TargetFloat(exponentiation * (1.0 + f));
201,195✔
2079
                                        }
2080
                                }
2081
                        }
2082
                        v = sign() ? -v : v;
91,681,250✔
2083
                }
2084
                return v;
97,968,678✔
2085
        }
2086

2087
        // convert a cfloat to a blocktriple with the fraction format 1.ffff
2088
        // we are using the same block type so that we can use block copies to move bits around.
2089
        // Since we tend to have at least two exponent bits, this will lead to
2090
        // most cfloat<->blocktriple cases being efficient as the block types are aligned.
2091
        // The relationship between the source cfloat and target blocktriple is not
2092
        // arbitrary, enforce it by setting the blocktriple fbits to the cfloat's (nbits - es - 1)
2093
        template<typename TargetBlockType = bt>
2094
        constexpr void normalize(blocktriple<fbits, BlockTripleOperator::REP, TargetBlockType>& tgt) const {
3,163✔
2095
                // test special cases
2096
                if (isnan()) {
3,163✔
2097
                        tgt.setnan();
253✔
2098
                }
2099
                else if (isinf()) {
2,910✔
2100
                        tgt.setinf();
246✔
2101
                }
2102
                else if (iszero()) {
2,664✔
2103
                        tgt.setzero();
316✔
2104
                }
2105
                else {
2106
                        tgt.setnormal(); // a blocktriple is always normalized
2,348✔
2107
                        int scale = this->scale();
2,348✔
2108
                        tgt.setsign(sign());
2,348✔
2109
                        tgt.setscale(scale);
2,348✔
2110
                        // set significant
2111
                        // we are going to unify to the format 01.ffffeeee
2112
                        // where 'f' is a fraction bit, and 'e' is an extension bit
2113
                        // so that normalize can be used to generate blocktriples for add/sub/mul/div/sqrt
2114
                        if (isnormal() || ismaxexpvalue()) {
2,348✔
2115
                                // normal encoding or max-exponent encoding (hasMaxExpValues=true):
2116
                                // significand has a hidden bit at position fbits
2117
                                if constexpr (fbits < 64) { // max 63 bits of fraction to yield 64bit of raw significant bits
2118
                                        uint64_t raw = fraction_ull();
1,679✔
2119
                                        raw |= (1ull << fbits);
1,679✔
2120
                                        tgt.setbits(raw);
1,679✔
2121
                                }
2122
                                else {
2123
                                        blockcopy(tgt);
111✔
2124
                                        tgt.setbit(fbits);
111✔
2125
                                }
2126
                        }
2127
                        else { // it is a subnormal encoding in this target cfloat
2128
                                int shift = MIN_EXP_NORMAL - scale;
558✔
2129
                                if (shift < 0) shift = 0; // guard against negative shifts
558✔
2130
                                // Stored subnormals have no hidden bit, but blocktriple REP expects a normalized 1.ffff payload.
2131
                                // Shift the encoded fraction up until the leading 1 reaches the hidden-bit position, while leaving
2132
                                // scale at the value returned by cfloat::scale(); that preserves the original real value in canonical form.
2133
                                if constexpr (fbits < 64) {
2134
                                        uint64_t raw = fraction_ull();
541✔
2135
                                        raw <<= shift;
541✔
2136
                                        raw |= (1ull << fbits);
541✔
2137
                                        tgt.setbits(raw);
541✔
2138
                                }
2139
                                else {
2140
                                        blockcopy(tgt);
17✔
2141
                                        tgt <<= shift;
17✔
2142
                                        tgt.setbit(fbits);
17✔
2143
                                }
2144
                        }
2145
                }
2146
        }
3,163✔
2147

2148
        // normalize a cfloat to a blocktriple used in add/sub, which has the form 00h.fffff
2149
        // that is 3 + fbits, the 3 extra bits are required to be able to use 2's complement 
2150
        // and capture the largest value of an addition/subtraction.
2151
        // TODO: currently abits = 2*fhbits as the worst case input argument size to
2152
        // capture the smallest normal value in aligned form. There is a faster/smaller
2153
        // implementation where the input is constrainted to just the round, guard, and sticky bits.
2154
        constexpr void normalizeAddition(blocktriple<fbits, BlockTripleOperator::ADD, bt>& tgt) const {
41,174,643✔
2155
                using BlockTripleConfiguration = blocktriple<fbits, BlockTripleOperator::ADD, bt>;
2156
                // test special cases
2157
                if (isnan()) {
41,174,643✔
2158
                        tgt.setnan();
396,494✔
2159
                }
2160
                else if (isinf()) {
40,778,149✔
2161
                        tgt.setinf();
326✔
2162
                }
2163
                else if (iszero()) {
40,777,823✔
2164
                        tgt.setzero();
325,672✔
2165
                }
2166
                else {
2167
                        tgt.setnormal(); // a blocktriple is always normalized
40,452,151✔
2168
                        int scale = this->scale();
40,452,151✔
2169
                        tgt.setsign(sign());
40,452,151✔
2170
                        tgt.setscale(scale);
40,452,151✔
2171
                        // set significant
2172
                        // we are going to unify to the format 001.ffffeeee
2173
                        // where 'f' is a fraction bit, and 'e' is an extension bit
2174
                        // so that normalize can be used to generate blocktriples for add/sub/mul/div/sqrt
2175
                        if (isnormal()) {
40,452,151✔
2176
                                if constexpr (fbits < 64 && BlockTripleConfiguration::rbits < (64 - fbits)) {
2177
                                        uint64_t raw = fraction_ull();
27,642,810✔
2178
                                        raw |= (1ull << fbits); // add the hidden bit
27,642,810✔
2179
                                        //std::cout << "normalize      : " << *this << '\n';
2180
                                        //std::cout << "significant    : " << to_binary(raw, fbits + 2) << '\n';
2181
                                        raw <<= BlockTripleConfiguration::rbits;  // rounding bits required for correct rounding
27,642,810✔
2182
                                        //std::cout << "rounding shift : " << to_binary(raw, fbits + 2 + BlockTripleConfiguration::rbits) << '\n';
2183
                                        tgt.setbits(raw);
27,642,810✔
2184
                                }
2185
                                else {
2186
                                        blockcopy(tgt);
962✔
2187
                                        tgt.setradix();
962✔
2188
                                        tgt.setbit(fbits); // add the hidden bit
962✔
2189
                                        tgt.bitShift(BlockTripleConfiguration::rbits);  // rounding bits required for correct rounding
962✔
2190
                                }
2191
                        }
2192
                        else {
2193
                                if (isdenormal()) { // it is a subnormal encoding in this target cfloat
12,808,379✔
2194
                                        if constexpr (hasSubnormals) {
2195
                                                if constexpr (BlockTripleConfiguration::rbits < (64 - fbits)) {
2196
                                                        uint64_t raw = fraction_ull();
6,516,707✔
2197
                                                        int shift = MIN_EXP_NORMAL - scale;
6,516,707✔
2198
                                                        raw <<= shift; // shift but do NOT add a hidden bit as the MSB of the subnormal is shifted in the hidden bit position
6,516,707✔
2199
                                                        raw <<= BlockTripleConfiguration::rbits;  // rounding bits required for correct rounding
6,516,707✔
2200
                                                        tgt.setbits(raw);
6,516,707✔
2201
                                                }
2202
                                                else {
2203
                                                        blockcopy(tgt);
2204
                                                        tgt.setradix();
2205
                                                        int shift = MIN_EXP_NORMAL - scale;
2206
                                                        tgt.bitShift(shift + BlockTripleConfiguration::rbits);  // rounding bits required for correct rounding
2207
                                                }
2208
                                        }
2209
                                        else {  // this cfloat has no subnormals
2210
                                                tgt.setzero(tgt.sign()); // preserve the sign
×
2211
                                        }
2212
                                }
2213
                                else {
2214
                                        // by design, a cfloat is either normal, subnormal, or max-exponent value, so this else clause is by deduction covering a max-exponent value
2215
                                        if constexpr (hasMaxExpValues) {
2216
                                                if constexpr (fbits < 64 && BlockTripleConfiguration::rbits < (64 - fbits)) {
2217
                                                        uint64_t raw = fraction_ull();
6,291,672✔
2218
                                                        raw |= (1ull << fbits); // add the hidden bit
6,291,672✔
2219
                                                        raw <<= BlockTripleConfiguration::rbits;  // rounding bits required for correct rounding
6,291,672✔
2220
                                                        tgt.setbits(raw);
6,291,672✔
2221
                                                }
2222
                                                else {
2223
                                                        blockcopy(tgt);
×
2224
                                                        tgt.setradix();
×
2225
                                                        tgt.setbit(fbits); // add the hidden bit
×
2226
                                                        tgt.bitShift(BlockTripleConfiguration::rbits);  // rounding bits required for correct rounding
×
2227
                                                }
2228
                                        }
2229
                                        else {  // this cfloat has no max-exponent values and thus this represents a nan, signalling or quiet determined by the sign
2230
                                                tgt.setnan(tgt.sign());
×
2231
                                        }                        
2232
                                }
2233
                        }
2234
                }
2235
                // tgt.setradix(radix);
2236
        }
41,174,643✔
2237

2238
        // Normalize a cfloat to a blocktriple used in mul, which has the form 0'00001.fffff
2239
        // that is 2*fbits, plus 1 overflow bit, and the radix set at <fbits>.
2240
        // The result radix will go to 2*fbits after multiplication.
2241
        constexpr void normalizeMultiplication(blocktriple<fbits, BlockTripleOperator::MUL, bt>& tgt) const {
13,823,164✔
2242
                // test special cases
2243
                if (isnan()) {
13,823,164✔
2244
                        tgt.setnan();
450,696✔
2245
                }
2246
                else if (isinf()) {
13,372,468✔
2247
                        tgt.setinf();
652✔
2248
                }
2249
                else if (iszero()) {
13,371,816✔
2250
                        tgt.setzero();
450,960✔
2251
                }
2252
                else {
2253
                        tgt.setnormal(); // a blocktriple is always normalized
12,920,856✔
2254
                        int scale = this->scale();
12,920,856✔
2255
                        tgt.setsign(sign());
12,920,856✔
2256
                        tgt.setscale(scale);
12,920,856✔
2257

2258
                        // set significant
2259
                        // we are going to unify to the format 01.ffffeeee
2260
                        // where 'f' is a fraction bit, and 'e' is an extension bit
2261
                        // so that normalize can be used to generate blocktriples for add/sub/mul/div/sqrt
2262
                        if (isnormal() || ismaxexpvalue()) {
12,920,856✔
2263
                                if constexpr (fbits < 64) { // max 63 bits of fraction to yield 64bit of raw significant bits
2264
                                        uint64_t raw = fraction_ull();
11,706,712✔
2265
                                        raw |= (1ull << fbits);
11,706,712✔
2266
                                        tgt.setbits(raw);
11,706,712✔
2267
                                }
2268
                                else {
2269
                                        blockcopy(tgt);
832✔
2270
                                        tgt.setradix();
832✔
2271
                                        tgt.setbit(fbits); // add the hidden bit
832✔
2272
                                }
2273
                        }
2274
                        else { 
2275
                                // it is a subnormal encoding in this target cfloat
2276
                                if constexpr (hasSubnormals) {
2277
                                        if constexpr (fbits < 64) {
2278
                                                uint64_t raw = fraction_ull();
1,213,312✔
2279
                                                int shift = MIN_EXP_NORMAL - scale;
1,213,312✔
2280
                                                raw <<= shift;
1,213,312✔
2281
                                                raw |= (1ull << fbits);
1,213,312✔
2282
                                                tgt.setbits(raw);
1,213,312✔
2283
                                        }
2284
                                        else {
2285
                                                blockcopy(tgt);
×
2286
                                                int shift = MIN_EXP_NORMAL - scale;
×
2287
                                                tgt.bitShift(shift);
×
2288
                                                tgt.setbit(fbits);
×
2289
                                        }
2290
                                }
2291
                                else { // this cfloat has no subnormals
2292
                                        tgt.setzero(tgt.sign()); // preserve the sign
×
2293
                                }
2294
                        }
2295
                }
2296
                tgt.setradix(fbits); // override the radix with the input scale for accurate value printing
13,823,164✔
2297
        }
13,823,164✔
2298

2299
        // normalize a cfloat to a blocktriple used in div, which has the form 0'00000'00001.fffff
2300
        // that is 3*fbits, plus 1 overflow bit, and the radix set at <fbits>.
2301
        // the result radix will go to 2*fbits after multiplication.
2302
        // TODO: needs implementation
2303
        constexpr void normalizeDivision(blocktriple<fbits, BlockTripleOperator::DIV, bt>& tgt) const {
8,673,784✔
2304
                constexpr unsigned divshift = blocktriple<fbits, BlockTripleOperator::DIV, bt>::divshift;
8,673,784✔
2305
                // test special cases
2306
                if (isnan()) {
8,673,784✔
2307
                        tgt.setnan();
38✔
2308
                }
2309
                else if (isinf()) {
8,673,746✔
2310
                        tgt.setinf();
6✔
2311
                }
2312
                else if (iszero()) {
8,673,740✔
2313
                        tgt.setzero();
44✔
2314
                }
2315
                else {
2316
                        tgt.setnormal(); // a blocktriple is always normalized
8,673,696✔
2317
                        int scale = this->scale();
8,673,696✔
2318
                        tgt.setsign(sign());
8,673,696✔
2319
                        tgt.setscale(scale);
8,673,696✔
2320
                        // set significant
2321
                        // we are going to unify to the format 01.ffffeeee
2322
                        // where 'f' is a fraction bit, and 'e' is an extension bit
2323
                        // so that normalize can be used to generate blocktriples for add/sub/mul/div/sqrt
2324
                        if (isnormal() || ismaxexpvalue()) {
8,673,696✔
2325
                                if constexpr (fbits < 64 && divshift < (64 - fbits)) {
2326
                                        uint64_t raw = fraction_ull();
7,187,035✔
2327
                                        raw |= (1ull << fbits);
7,187,035✔
2328
                                        raw <<= divshift; // shift the input value to the output radix
7,187,035✔
2329
                                        tgt.setbits(raw);
7,187,035✔
2330
                                }
2331
                                else {
2332
                                        // brute force copy of blocks
2333
                                        blockcopy(tgt);
1,054,322✔
2334
                                        tgt.setbit(fbits);
1,054,322✔
2335
                                        tgt.bitShift(divshift); // shift the input value to the output radix
1,054,322✔
2336
                                }
2337
                        }
2338
                        else { // it is a subnormal encoding in this target cfloat
2339
                                if constexpr (fbits < 64 && divshift < (64 - fbits)) {
2340
                                        uint64_t raw = fraction_ull();
432,337✔
2341
                                        int shift = MIN_EXP_NORMAL - scale;
432,337✔
2342
                                        raw <<= shift;
432,337✔
2343
                                        raw |= (1ull << fbits);
432,337✔
2344
                                        raw <<= divshift; // shift the input value to the output radix
432,337✔
2345
                                        tgt.setbits(raw);
432,337✔
2346
                                }
2347
                                else {
2348
                                        blockcopy(tgt);
2✔
2349
                                        int shift = MIN_EXP_NORMAL - scale;
2✔
2350
                                        tgt.bitShift(shift);
2✔
2351
                                        tgt.setbit(fbits);
2✔
2352
                                        tgt.bitShift(divshift); // shift the input value to the output radix
2✔
2353
                                }
2354
                        }
2355
                }
2356
                tgt.setradix(blocktriple<fbits, BlockTripleOperator::DIV, bt>::radix);
8,673,784✔
2357
        }
8,673,784✔
2358

2359
        // helper debug function
2360
        void constexprClassParameters() const noexcept {
8✔
2361
                std::cout << "-------------------------------------------------------------\n";
8✔
2362
                std::cout << "type              : " << typeid(*this).name() << '\n';
8✔
2363
                std::cout << "nbits             : " << nbits << '\n';
8✔
2364
                std::cout << "es                : " << es << std::endl;
8✔
2365
                std::cout << "hasSubnormals     : " << (hasSubnormals ? "true" : "false") << '\n';
8✔
2366
                std::cout << "hasMaxExpValues   : " << (hasMaxExpValues ? "true" : "false") << '\n';
8✔
2367
                std::cout << "isSaturating      : " << (isSaturating ? "true" : "false") << '\n';
8✔
2368
                std::cout << "ALL_ONES          : " << to_binary(ALL_ONES, 0, true) << '\n';
8✔
2369
                std::cout << "BLOCK_MASK        : " << to_binary(BLOCK_MASK, 0, true) << '\n';
8✔
2370
                std::cout << "nrBlocks          : " << nrBlocks << '\n';
8✔
2371
                std::cout << "bits in MSU       : " << bitsInMSU << '\n';
8✔
2372
                std::cout << "MSU               : " << MSU << '\n';
8✔
2373
                std::cout << "MSU MASK          : " << to_binary(MSU_MASK, 0, true) << '\n';
8✔
2374
                std::cout << "SIGN_BIT_MASK     : " << to_binary(SIGN_BIT_MASK, 0, true) << '\n';
8✔
2375
                std::cout << "LSB_BIT_MASK      : " << to_binary(LSB_BIT_MASK, 0, true) << '\n';
8✔
2376
                std::cout << "MSU CAPTURES_EXP  : " << (MSU_CAPTURES_EXP ? "yes\n" : "no\n");
8✔
2377
                std::cout << "EXP_SHIFT         : " << EXP_SHIFT << '\n';
8✔
2378
                std::cout << "MSU EXP MASK      : " << to_binary(MSU_EXP_MASK, 0, true) << '\n';
8✔
2379
                std::cout << "ALL_ONE_MASK_ES   : " << to_binary(ALL_ONES_ES) << '\n';
8✔
2380
                std::cout << "EXP_BIAS          : " << EXP_BIAS << '\n';
8✔
2381
                std::cout << "MAX_EXP           : " << MAX_EXP << '\n';
8✔
2382
                std::cout << "MIN_EXP_NORMAL    : " << MIN_EXP_NORMAL << '\n';
8✔
2383
                std::cout << "MIN_EXP_SUBNORMAL : " << MIN_EXP_SUBNORMAL << '\n';
8✔
2384
                std::cout << "fraction Blocks   : " << fBlocks << '\n';
8✔
2385
                std::cout << "bits in FSU       : " << bitsInFSU << '\n';
8✔
2386
                std::cout << "FSU               : " << FSU << '\n';
8✔
2387
                std::cout << "FSU MASK          : " << to_binary(FSU_MASK, 0, true) << '\n';
8✔
2388
                std::cout << "topfbits          : " << topfbits << '\n';
8✔
2389
                std::cout << "ALL_ONE_MASK_FR   : " << to_binary(ALL_ONES_FR) << '\n';
8✔
2390
        }
8✔
2391
        void showLimbs() const {
2392
                for (unsigned b = 0; b < nrBlocks; ++b) {
2393
                        std::cout << to_binary(_block[nrBlocks - b - 1], sizeof(bt) * 8) << ' ';
2394
                }
2395
                std::cout << '\n';
2396
        }
2397

2398
protected:
2399
        // HELPER methods
2400

2401
        /// <summary>
2402
        /// 1's complement of the encoding used to set up specific encoding patterns.
2403
        /// This is not an arithmetic operator that makes sense for floating-point numbers.
2404
        /// </summary>
2405
        /// <returns>reference to this cfloat object</returns>
2406
        constexpr cfloat& flip() noexcept { // in-place one's complement
1,871,737✔
2407
                for (unsigned i = 0; i < nrBlocks; ++i) {
7,118,180✔
2408
                        _block[i] = bt(~_block[i]);
5,246,443✔
2409
                }
2410
                _block[MSU] &= MSU_MASK; // assert precondition of properly nulled leading non-bits
1,871,737✔
2411
                return *this;
1,871,737✔
2412
        }
2413

2414
        /// <summary>
2415
        /// shift left is a bit level encoding helper for fast limb-based conversions between different cfloats
2416
        /// </summary>
2417
        /// <param name="bitsToShift"></param>
2418
        void shiftLeft(unsigned bitsToShift) {
60,265✔
2419
                if (bitsToShift == 0) return;
60,265✔
2420
                if (bitsToShift > nbits) {
60,265✔
2421
                        setzero();
×
2422
                }
2423
                if (bitsToShift >= bitsInBlock) {
60,265✔
2424
                        int blockShift = static_cast<int>(bitsToShift / bitsInBlock);
60,265✔
2425
                        for (int i = static_cast<int>(MSU); i >= blockShift; --i) {
140,560✔
2426
                                _block[i] = _block[i - blockShift];
80,295✔
2427
                        }
2428
                        for (int i = blockShift - 1; i >= 0; --i) {
341,518✔
2429
                                _block[i] = bt(0);
281,253✔
2430
                        }
2431
                        // adjust the shift
2432
                        bitsToShift -= blockShift * bitsInBlock;
60,265✔
2433
                        if (bitsToShift == 0) return;
60,265✔
2434
                }
2435
                if constexpr (MSU > 0) {
2436
                        // construct the mask for the upper bits in the block that need to move to the higher word
2437
                        bt mask = 0xFFFFFFFFFFFFFFFF << (bitsInBlock - bitsToShift);
60,219✔
2438
                        for (unsigned i = MSU; i > 0; --i) {
360,920✔
2439
                                _block[i] <<= bitsToShift;
300,701✔
2440
                                // mix in the bits from the right
2441
                                bt bits = bt(mask & _block[i - 1]);
300,701✔
2442
                                _block[i] |= (bits >> (bitsInBlock - bitsToShift));
300,701✔
2443
                        }
2444
                }
2445
                _block[0] <<= bitsToShift;
60,219✔
2446
        }
2447

2448
        // convert an unsigned integer into a cfloat
2449
        // TODO: this method does not protect against being called with a signed integer
2450
        template<typename Ty>
2451
        constexpr cfloat& convert_unsigned_integer(const Ty& rhs) noexcept {
2,795✔
2452
                clear();
2,795✔
2453
                if (0 == rhs) return *this;
2,795✔
2454

2455
                uint64_t raw = static_cast<uint64_t>(rhs);
2,772✔
2456
                int msb = static_cast<int>(find_msb(raw)) - 1; // msb > 0 due to zero test above
2,772✔
2457
                int exponent = msb;
2,772✔
2458
                // remove the MSB as it represents the hidden bit in the cfloat representation
2459
                uint64_t hmask = ~(1ull << msb);
2,772✔
2460
                raw &= hmask;
2,772✔
2461

2462
                constexpr uint32_t sizeInBits = 8 * sizeof(Ty);
2,772✔
2463
                uint32_t shift = sizeInBits - exponent - 1;
2,772✔
2464
                raw <<= shift;
2,772✔
2465
                raw = round<sizeInBits, uint64_t>(raw, exponent);
2,772✔
2466

2467
                // check for exponent overflow after rounding
2468
                if (exponent > MAX_EXP) {
2,772✔
2469
                        if constexpr (isSaturating) {
2470
                                this->maxpos();
×
2471
                        }
2472
                        else {
2473
                                setinf(false);
×
2474
                        }
2475
                        return *this;
×
2476
                }
2477

2478
                // construct the target cfloat
2479
                if constexpr (fbits < (64 - es)) {
2480
                        uint64_t biasedExponent = static_cast<uint64_t>(static_cast<long long>(exponent) + static_cast<long long>(EXP_BIAS));
2,742✔
2481
                        uint64_t bits = 0;
2,742✔
2482
                        bits <<= es;
2,742✔
2483
                        bits |= biasedExponent;
2,742✔
2484
                        bits <<= fbits;
2,742✔
2485
                        bits |= raw;
2,742✔
2486
                        setbits(bits);
2,742✔
2487
                }
2488
                else {
2489
                        setsign(false);
30✔
2490
                        setexponent(exponent);
30✔
2491
                        for (int i = 0; i < exponent; ++i) {
291✔
2492
                                bool bit = (raw >> (sizeInBits - 2 - i)) & 1;
261✔
2493
                                setbit(static_cast<unsigned>(fbits - 1 - i), bit);
261✔
2494
                        }
2495
                }
2496
                // post-rounding cleanup: rounding at the maxpos boundary can
2497
                // produce a NaN encoding; project back to inf or maxpos
2498
                if (isnan()) {
2,772✔
2499
                        if constexpr (isSaturating) {
2500
                                this->maxpos();
2✔
2501
                        }
2502
                        else {
2503
                                setinf(false);
×
2504
                        }
2505
                }
2506
                return *this;
2,772✔
2507
        }
2508
        // convert a signed integer into a cfloat
2509
        // TODO: this method does not protect against being called with a signed integer
2510
        template<typename Ty>
2511
        constexpr cfloat& convert_signed_integer(const Ty& rhs) noexcept {
13,329✔
2512
                clear();
13,329✔
2513
                if (0 == rhs) return *this;
13,329✔
2514
                bool s = (rhs < 0);
10,086✔
2515
                using UnsignedTy = std::make_unsigned_t<Ty>;
2516
                UnsignedTy urhs = static_cast<UnsignedTy>(rhs);
10,086✔
2517
                uint64_t raw = static_cast<uint64_t>(s ? (UnsignedTy(0) - urhs) : urhs);
10,086✔
2518

2519
                int msb = static_cast<int>(find_msb(raw)) - 1; // msb > 0 due to zero test above 
10,086✔
2520
                int exponent = msb;
10,086✔
2521
                // remove the MSB as it represents the hidden bit in the cfloat representation
2522
                uint64_t hmask = ~(1ull << msb);
10,086✔
2523
                raw &= hmask;
10,086✔
2524

2525
                // shift the msb to the msb of the fraction
2526
                constexpr uint32_t sizeInBits = 8 * sizeof(Ty);
10,086✔
2527
                uint32_t shift = sizeInBits - exponent - 1;
10,086✔
2528
                raw <<= shift;
10,086✔
2529
                raw = round<sizeInBits, uint64_t>(raw, exponent);
10,086✔
2530

2531
                // check for exponent overflow after rounding
2532
                if (exponent > MAX_EXP) {
10,086✔
2533
                        if constexpr (isSaturating) {
2534
                                if (s) this->maxneg(); else this->maxpos();
×
2535
                        }
2536
                        else {
2537
                                setinf(s);
×
2538
                        }
2539
                        return *this;
×
2540
                }
2541

2542
                // construct the target cfloat
2543
                if constexpr (fbits < (64 - es)) {
2544
                        uint64_t biasedExponent = static_cast<uint64_t>(static_cast<long long>(exponent) + static_cast<long long>(EXP_BIAS));
9,715✔
2545
                        uint64_t bits = (s ? 1ull : 0ull);
9,715✔
2546
                        bits <<= es;
9,715✔
2547
                        bits |= biasedExponent;
9,715✔
2548
                        bits <<= fbits;
9,715✔
2549
                        bits |= raw;
9,715✔
2550
                        setbits(bits);
9,715✔
2551
                }
2552
                else {
2553
                        setsign(s);
371✔
2554
                        setexponent(exponent);
371✔
2555
                        for (int i = 0; i < exponent; ++i) {
3,120✔
2556
                                bool bit = (raw >> (sizeInBits - 2 - i)) & 1;
2,749✔
2557
                                setbit(static_cast<unsigned>(fbits - 1 - i), bit);
2,749✔
2558
                        }
2559
                }
2560
                // post-rounding cleanup: rounding at the maxpos boundary can
2561
                // produce a NaN encoding; project back to inf or maxpos/maxneg
2562
                if (isnan()) {
10,086✔
2563
                        if constexpr (isSaturating) {
2564
                                if (s) this->maxneg(); else this->maxpos();
4✔
2565
                        }
2566
                        else {
2567
                                setinf(s);
×
2568
                        }
2569
                }
2570
                return *this;
10,086✔
2571
        }
2572

2573
public:
2574
        template<typename Real>
2575
        CONSTEXPRESSION cfloat& convert_ieee754(Real rhs) noexcept {
138,278,576✔
2576
                if constexpr (nbits == 32 && es == 8 && sizeof(Real) == 4) {
2577
                        // we CANNOT use the native conversion to float as cfloats have max-exponent values
2578
                        // which IEEE-754 does not have and thus a native conversion would destroy
2579
                        // only if the Real type is a float can we use the direct conversion
2580

2581
                        // when our cfloat is a perfect match to single precision IEEE-754
2582
                        bool s{ false };
65,744✔
2583
                        uint64_t rawExponent{ 0 };
65,744✔
2584
                        uint64_t rawFraction{ 0 };
65,744✔
2585
                        uint64_t bits{ 0 };
65,744✔
2586
                        extractFields(rhs, s, rawExponent, rawFraction, bits);
65,744✔
2587
                        if (rawExponent == ieee754_parameter<Real>::eallset) { // nan and inf need to be remapped
65,744✔
2588
                                if (rawFraction == (ieee754_parameter<Real>::fmask & ieee754_parameter<Real>::snanmask) ||
24✔
2589
                                        rawFraction == (ieee754_parameter<Real>::fmask & (ieee754_parameter<Real>::qnanmask | ieee754_parameter<Real>::snanmask))) {
18✔
2590
                                        // 1.11111111.00000000.......00000001 signalling nan
2591
                                        // 0.11111111.00000000000000000000001 signalling nan
2592
                                        // MSVC
2593
                                        // 1.11111111.10000000.......00000001 signalling nan
2594
                                        // 0.11111111.10000000.......00000001 signalling nan
2595
                                        setnan(NAN_TYPE_SIGNALLING);
6✔
2596
                                        //setsign(s);  a cfloat encodes a signalling nan with sign = 1, and a quiet nan with sign = 0
2597
                                        return *this;
6✔
2598
                                }
2599
                                if (rawFraction == (ieee754_parameter<Real>::fmask & ieee754_parameter<Real>::qnanmask)) {
18✔
2600
                                        // 1.11111111.10000000.......00000000 quiet nan
2601
                                        // 0.11111111.10000000.......00000000 quiet nan
2602
                                        setnan(NAN_TYPE_QUIET);
5✔
2603
                                        //setsign(s);  a cfloat encodes a signalling nan with sign = 1, and a quiet nan with sign = 0
2604
                                        return *this;
5✔
2605
                                }
2606
                                if (rawFraction == 0ull) {
13✔
2607
                                        // 1.11111111.0000000.......000000000 -inf
2608
                                        // 0.11111111.0000000.......000000000 +inf
2609
                                        setinf(s);
13✔
2610
                                        return *this;
13✔
2611
                                }
2612
                        }
2613
                        uint64_t raw{ s ? 1ull : 0ull };
65,720✔
2614
                        raw <<= 31;
65,720✔
2615
                        raw |= (rawExponent << fbits);
65,720✔
2616
                        raw |= rawFraction;
65,720✔
2617
                        setbits(raw);
65,720✔
2618
                        return *this;
65,720✔
2619
                }
2620
                else if constexpr (nbits == 64 && es == 11 && sizeof(Real) == 8) {
2621
                        // when our cfloat is a perfect match to double precision IEEE-754
2622
                        bool s{ false };
12,376✔
2623
                        uint64_t rawExponent{ 0 };
12,376✔
2624
                        uint64_t rawFraction{ 0 };
12,376✔
2625
                        uint64_t bits{ 0 };
12,376✔
2626
                        extractFields(rhs, s, rawExponent, rawFraction, bits);
12,376✔
2627
                        if (rawExponent == ieee754_parameter<Real>::eallset) { // nan and inf need to be remapped
12,376✔
2628
                                if (rawFraction == (ieee754_parameter<Real>::fmask & ieee754_parameter<Real>::snanmask) ||
23✔
2629
                                        rawFraction == (ieee754_parameter<Real>::fmask & (ieee754_parameter<Real>::qnanmask | ieee754_parameter<Real>::snanmask))) {
18✔
2630
                                        // 1.11111111.00000000.......00000001 signalling nan
2631
                                        // 0.11111111.00000000000000000000001 signalling nan
2632
                                        // MSVC
2633
                                        // 1.11111111.10000000.......00000001 signalling nan
2634
                                        // 0.11111111.10000000.......00000001 signalling nan
2635
                                        setnan(NAN_TYPE_SIGNALLING);
5✔
2636
                                        //setsign(s);  a cfloat encodes a signalling nan with sign = 1, and a quiet nan with sign = 0
2637
                                        return *this;
5✔
2638
                                }
2639
                                if (rawFraction == (ieee754_parameter<Real>::fmask & ieee754_parameter<Real>::qnanmask)) {
18✔
2640
                                        // 1.11111111.10000000.......00000000 quiet nan
2641
                                        // 0.11111111.10000000.......00000000 quiet nan
2642
                                        setnan(NAN_TYPE_QUIET);
6✔
2643
                                        //setsign(s);  a cfloat encodes a signalling nan with sign = 1, and a quiet nan with sign = 0
2644
                                        return *this;
6✔
2645
                                }
2646
                                if (rawFraction == 0ull) {
12✔
2647
                                        // 1.11111111.0000000.......000000000 -inf
2648
                                        // 0.11111111.0000000.......000000000 +inf
2649
                                        setinf(s);
12✔
2650
                                        return *this;
12✔
2651
                                }
2652
                        }
2653
                        // normal and subnormal handling
2654
                        uint64_t raw{ s ? 1ull : 0ull };
12,353✔
2655
                        raw <<= 63;
12,353✔
2656
                        raw |= (rawExponent << fbits);
12,353✔
2657
                        raw |= rawFraction;
12,353✔
2658
                        setbits(raw);
12,353✔
2659
                        return *this;
12,353✔
2660
                }
2661
                else {
2662
                        clear();
138,200,456✔
2663
                        // extract raw IEEE-754 bits
2664
                        bool s{ false };
138,200,456✔
2665
                        uint64_t rawExponent{ 0 };
138,200,456✔
2666
                        uint64_t rawFraction{ 0 };
138,200,456✔
2667
                        uint64_t bits{ 0 };
138,200,456✔
2668
                        extractFields(rhs, s, rawExponent, rawFraction, bits);
138,200,456✔
2669
                        // special case handling
2670
                        if (rawExponent == ieee754_parameter<Real>::eallset) { // nan and inf
138,200,456✔
2671
                                if (rawFraction == (ieee754_parameter<Real>::fmask & ieee754_parameter<Real>::snanmask) ||
5,105,653✔
2672
                                        rawFraction == (ieee754_parameter<Real>::fmask & (ieee754_parameter<Real>::qnanmask | ieee754_parameter<Real>::snanmask))) {
2,589,618✔
2673
                                        // 1.11111111.00000000.......00000001 signalling nan
2674
                                        // 0.11111111.00000000000000000000001 signalling nan
2675
                                        // MSVC
2676
                                        // 1.11111111.10000000.......00000001 signalling nan
2677
                                        // 0.11111111.10000000.......00000001 signalling nan
2678
                                        setnan(NAN_TYPE_SIGNALLING);
2,523,243✔
2679
                                        //setsign(s);  a cfloat encodes a signalling nan with sign = 1, and a quiet nan with sign = 0
2680
                                        return *this;
2,523,243✔
2681
                                }
2682
                                if (rawFraction == (ieee754_parameter<Real>::fmask & ieee754_parameter<Real>::qnanmask)) {
2,582,410✔
2683
                                        // 1.11111111.10000000.......00000000 quiet nan
2684
                                        // 0.11111111.10000000.......00000000 quiet nan
2685
                                        setnan(NAN_TYPE_QUIET);
2,553,518✔
2686
                                        //setsign(s);  a cfloat encodes a signalling nan with sign = 1, and a quiet nan with sign = 0
2687
                                        return *this;
2,553,518✔
2688
                                }
2689
                                if (rawFraction == 0ull) {
28,892✔
2690
                                        // 1.11111111.0000000.......000000000 -inf
2691
                                        // 0.11111111.0000000.......000000000 +inf
2692
                                        setinf(s);
28,868✔
2693
                                        return *this;
28,868✔
2694
                                }
2695
                        }
2696
                        if (rhs == 0.0) { // IEEE rule: this is valid for + and - 0.0
133,094,827✔
2697
                                setbit(nbits - 1ull, s);
578,637✔
2698
                                return *this;
578,637✔
2699
                        }
2700
        
2701
                        // normal number consists of fbits fraction bits and one hidden bit
2702
                        // subnormal number has no hidden bit
2703
                        int exponent = static_cast<int>(rawExponent) - ieee754_parameter<Real>::bias;  // unbias the exponent
132,516,190✔
2704

2705
                        // normalize subnormal source values so downstream code can treat them as normal
2706
                        // A subnormal has rawExponent == 0 and value = 0.fraction * 2^(1-bias).
2707
                        // We find the MSB of the fraction, shift it into the hidden bit position,
2708
                        // mask it off (like a normal's implicit 1), and adjust the exponent.
2709
                        // Setting rawExponent = 1 causes all downstream if(rawExponent != 0) branches
2710
                        // to take the normal-number path, which already handles normal-to-normal and
2711
                        // normal-to-subnormal target conversions correctly.
2712
                        if (rawExponent == 0 && rawFraction != 0) {
132,516,190✔
2713
                                exponent = 1 - ieee754_parameter<Real>::bias; // effective exponent for subnormals
1,154✔
2714
                                unsigned msb_pos = find_msb(rawFraction); // 1-indexed position of MSB
1,154✔
2715
                                unsigned normalizeShift = static_cast<unsigned>(ieee754_parameter<Real>::fbits) + 1u - msb_pos;
1,154✔
2716
                                rawFraction = (rawFraction << normalizeShift) & static_cast<uint64_t>(ieee754_parameter<Real>::fmask);
1,154✔
2717
                                exponent -= static_cast<int>(normalizeShift);
1,154✔
2718
                                rawExponent = 1; // mark as normalized so downstream paths treat this as a normal number
1,154✔
2719
                        }
2720

2721
                        // check special case of
2722
                        //  1- saturating to maxpos/maxneg, or 
2723
                        //  2- projecting to +-inf 
2724
                        // if the value is out of range.
2725
                        // 
2726
                        // One problem here is that at the rounding cusps of maxpos <-> inf <-> nan
2727
                        // you need to go through the rounding logic to know which encoding you end up
2728
                        // with. 
2729
                        // For each specific cfloat configuration, you can work out these rounding cusps
2730
                        // but they need to go through the value transformation to map them back to native
2731
                        // IEEE-754. That is a complex computation to do as a static constexpr as you need
2732
                        // to construct the value, then evaluate it, and store it.
2733
                        // 
2734
                        // The algorithm used here is to check for the obvious out of range values by
2735
                        // comparing their scale to the max scale this cfloat encoding can represent.
2736
                        // For the rounding cusps, we go through the rounding logic, and then clean up
2737
                        // after rounding using the observation that no conversion from a value can ever
2738
                        // yield the NaN encoding.
2739
                        //
2740
                        // The rounding logic will correctly sort between maxpos and inf, and we clean
2741
                        // up any NaN encodings by projecting back to the configuration's saturation rule.
2742
                        //
2743
                        // We could improve on this by creating the database of rounding cusps and
2744
                        // referencing them with a direct value comparison with the input. That would be
2745
                        // the most performant implementation.
2746
                        if (exponent > MAX_EXP) {
132,516,190✔
2747
                                if constexpr (isSaturating) {
2748
                                        if (s) this->maxneg(); else this->maxpos(); // saturate to maxpos or maxneg
934,372✔
2749
                                }
2750
                                else {
2751
                                        setinf(s);
971,631✔
2752
                                }
2753
                                return *this;
1,906,003✔
2754
                        }
2755
                        if constexpr (hasSubnormals) {
2756
                                if (exponent < MIN_EXP_SUBNORMAL - 1) { 
88,991,674✔
2757
                                        // map to +-0 any values that have a scale less than (MIN_EXP_SUBMORNAL - 1)
2758
                                        this->setbit(nbits - 1, s);
145,475✔
2759
                                        return *this;
145,475✔
2760
                                }
2761
                        }
2762
                        else {
2763
                                if (exponent < MIN_EXP_NORMAL - 1) {
41,618,513✔
2764
                                        // map to +-0 any values that have a scale less than (MIN_EXP_MORNAL - 1)
2765
                                        this->setbit(nbits - 1, s);
273,183✔
2766
                                        return *this;
273,183✔
2767
                                }
2768
                        }
2769

2770
                        /////////////////  
2771
                        /// end of special case processing, move on to value sampling and rounding
2772

2773
#if TRACE_CONVERSION
2774
                        std::cout << '\n';
2775
                        std::cout << "value             : " << rhs << '\n';
2776
                        std::cout << "segments          : " << to_binary(rhs) << '\n';
2777
                        std::cout << "sign     bit      : " << (s ? '1' : '0') << '\n';
2778
                        std::cout << "exponent bits     : " << to_binary(rawExponent, ieee754_parameter<Real>::ebits, true) << '\n';
2779
                        std::cout << "fraction bits     : " << to_binary(rawFraction, ieee754_parameter<Real>::fbits, true) << '\n';
2780
                        std::cout << "exponent value    : " << exponent << '\n';
2781
#endif
2782

2783
                        // do the following scenarios have different rounding bits?
2784
                        // input is normal, cfloat is normal           <-- rounding can happen with native ieee-754 bits
2785
                        // input is normal, cfloat is subnormal
2786
                        // input is subnormal, cfloat is normal
2787
                        // input is subnormal, cfloat is subnormal
2788

2789
                        // The first condition is the relationship between the number 
2790
                        // of fraction bits from the source and the number of fraction bits 
2791
                        // in the target cfloat: these are constexpressions and guard the shifts
2792
                        // input fbits >= cfloat fbits                 <-- need to round
2793
                        // input fbits < cfloat fbits                  <-- no need to round
2794

2795
                        // quick check if we are truncating to 0 for all subnormal values
2796
                        if constexpr (!hasSubnormals) {
2797
                                if (exponent < MIN_EXP_NORMAL) {
41,345,330✔
2798
                                        setsign(s); // rest of the bits, exponent and fraction, are already set correctly
124,352✔
2799
                                        return *this;
124,352✔
2800
                                }
2801
                        }
2802
                        if constexpr (fbits < ieee754_parameter<Real>::fbits) {
2803
                                // this is the common case for cfloats that are smaller in size compared to single and double precision IEEE-754
2804
                                constexpr int rightShift = ieee754_parameter<Real>::fbits - fbits; // this is the bit shift to get the MSB of the src to the MSB of the tgt
129,807,702✔
2805
                                uint32_t biasedExponent{ 0 };
129,807,702✔
2806
                                int adjustment{ 0 }; // right shift adjustment for subnormal representation
129,807,702✔
2807
                                uint64_t mask;
2808
                                if (rawExponent != 0) {
129,807,702✔
2809
                                        // the source real is a normal number, 
2810
//                                        if (exponent >= (MIN_EXP_SUBNORMAL - 1) && exponent < MIN_EXP_NORMAL) {
2811
                                        if (exponent < MIN_EXP_NORMAL) {
129,807,702✔
2812
//                                                exponent = (exponent < MIN_EXP_SUBNORMAL ? MIN_EXP_SUBNORMAL : exponent); // clip to the smallest subnormal exponent, otherwise the adjustment is off
2813
                                                // the value is a subnormal number in this representation: biasedExponent = 0
2814
                                                // add the hidden bit to the fraction bits so the denormalization has the correct MSB
2815
                                                rawFraction |= ieee754_parameter<Real>::hmask;
41,279,076✔
2816

2817
                                                // fraction processing: we have 1 hidden + 23 explicit fraction bits 
2818
                                                // f = 1.ffff 2^exponent * 2^fbits * 2^-(2-2^(es-1)) = 1.ff...ff >> (23 - (-exponent + fbits - (2 -2^(es-1))))
2819
                                                // -exponent because we are right shifting and exponent in this range is negative
2820
                                                adjustment = -(exponent + subnormal_reciprocal_shift[es]);
41,279,076✔
2821
                                                // this is the right shift adjustment required for subnormal representation due 
2822
                                                // to the scale of the input number, i.e. the exponent of 2^-adjustment
2823
                                        }
2824
                                        else {
2825
                                                // the value is a normal number in this representation: common case
2826
                                                biasedExponent = static_cast<uint32_t>(exponent + EXP_BIAS); // project the exponent into the target 
88,528,626✔
2827
                                                // fraction processing
2828
                                                // float structure is: seee'eeee'efff'ffff'ffff'ffff'ffff'ffff, s = sign, e - exponent bit, f = fraction bit
2829
                                                // target structure is for example cfloat<8,2>: seef'ffff
2830
                                                // since both are normals, we can shift the incoming fraction to the target structure bits, and round
2831
                                                // MSB of source = 23 - 1, MSB of target = fbits - 1: shift = MSB of src - MSB of tgt => 23 - fbits
2832
                                                adjustment = 0;
88,528,626✔
2833
                                        }
2834
                                        if constexpr (rightShift > 0) {
2835
                                                // if true we need to round
2836
                                                // round-to-even logic
2837
                                                //  ... lsb | guard  round sticky   round
2838
                                                //       x     0       x     x       down
2839
                                                //       0     1       0     0       down  round to even
2840
                                                //       1     1       0     0        up   round to even
2841
                                                //       x     1       0     1        up
2842
                                                //       x     1       1     0        up
2843
                                                //       x     1       1     1        up
2844
                                                // collect lsb, guard, round, and sticky bits
2845

2846

2847
#if TRACE_CONVERSION
2848
                                                std::cout << "fraction bits     : " << to_binary(rawFraction, ieee754_parameter<Real>::nbits, true) << '\n';
2849
                                                std::cout << "lsb mask bits     : " << to_binary(mask, ieee754_parameter<Real>::nbits, true) << '\n';
2850
#endif
2851
                                                mask = (1ull << (rightShift + adjustment)); // bit mask for the lsb bit
129,807,702✔
2852
                                                bool lsb = (mask & rawFraction);
129,807,702✔
2853
                                                mask >>= 1;
129,807,702✔
2854
                                                bool guard = (mask & rawFraction);
129,807,702✔
2855
                                                mask >>= 1;
129,807,702✔
2856
                                                bool round = (mask & rawFraction);
129,807,702✔
2857
                                                if ((rightShift + adjustment) > 1) {
129,807,702✔
2858
                                                        mask = (0xFFFF'FFFF'FFFF'FFFFull << (rightShift + adjustment - 2));
129,807,702✔
2859
                                                        mask = ~mask;
129,807,702✔
2860
                                                }
2861
                                                else {
2862
                                                        mask = 0;
×
2863
                                                }
2864
#if TRACE_CONVERSION
2865
                                                std::cout << "right shift       : " << rightShift << '\n';
2866
                                                std::cout << "adjustment        : " << adjustment << '\n';
2867
                                                std::cout << "shift to LSB      : " << (rightShift + adjustment) << '\n';
2868
                                                std::cout << "fraction bits     : " << to_binary(rawFraction, ieee754_parameter<Real>::nbits, true) << '\n';
2869
                                                std::cout << "sticky mask bits  : " << to_binary(mask, ieee754_parameter<Real>::nbits, true) << '\n';
2870
#endif
2871
                                                bool sticky = (mask & rawFraction);
129,807,702✔
2872
                                                rawFraction >>= (static_cast<int64_t>(rightShift) + static_cast<int64_t>(adjustment));
129,807,702✔
2873

2874
                                                // execute rounding operation
2875
                                                if (guard) {
129,807,702✔
2876
                                                        if (lsb && (!round && !sticky)) ++rawFraction; // round to even
26,459,851✔
2877
                                                        if (round || sticky) ++rawFraction;
26,459,851✔
2878
                                                        if (rawFraction == (1ull << fbits)) { // overflow
26,459,851✔
2879
                                                                if (biasedExponent == ALL_ONES_ES) { // overflow to INF == .111..01
451,391✔
2880
                                                                        rawFraction = INF_ENCODING;
594✔
2881
                                                                }
2882
                                                                else {
2883
                                                                        ++biasedExponent;
450,797✔
2884
                                                                        rawFraction = 0;
450,797✔
2885
                                                                }
2886
                                                        }
2887
                                                }
2888
#if TRACE_CONVERSION
2889
                                                std::cout << "lsb               : " << (lsb ? "1\n" : "0\n");
2890
                                                std::cout << "guard             : " << (guard ? "1\n" : "0\n");
2891
                                                std::cout << "round             : " << (round ? "1\n" : "0\n");
2892
                                                std::cout << "sticky            : " << (sticky ? "1\n" : "0\n");
2893
                                                std::cout << "rounding decision : " << (lsb && (!round && !sticky) ? "round to even\n" : "-\n");
2894
                                                std::cout << "rounding direction: " << (round || sticky ? "round up\n" : "round down\n");
2895
#endif
2896
                                        }
2897
                                        else { // all bits of the float go into this representation and need to be shifted up, no rounding necessary
2898
                                                int shiftLeft = fbits - ieee754_parameter<Real>::fbits;
2899
                                                rawFraction <<= shiftLeft;
2900
                                        }
2901
#if TRACE_CONVERSION
2902
                                        std::cout << "biased exponent   : " << biasedExponent << " : 0x" << std::hex << biasedExponent << std::dec << '\n';
2903
                                        std::cout << "right shift       : " << rightShift << '\n';
2904
                                        std::cout << "adjustment shift  : " << adjustment << '\n';
2905
                                        std::cout << "sticky bit mask   : " << to_binary(mask, 32, true) << '\n';
2906
                                        std::cout << "fraction bits     : " << to_binary(rawFraction, 32, true) << '\n';
2907
#endif
2908
                                        // construct the target cfloat
2909
                                        bits = (s ? 1ull : 0ull);
129,807,702✔
2910
                                        bits <<= es;
129,807,702✔
2911
                                        bits |= biasedExponent;
129,807,702✔
2912
                                        bits <<= fbits;
129,807,702✔
2913
                                        bits |= rawFraction;
129,807,702✔
2914
#if TRACE_CONVERSION
2915
                                        std::cout << "sign bit          : " << (s ? '1' : '0') << '\n';
2916
                                        std::cout << "biased exponent   : " << biasedExponent << " : 0x" << std::hex << biasedExponent << std::dec << '\n';
2917
                                        std::cout << "fraction bits     : " << to_binary(rawFraction, 32, true) << '\n';
2918
                                        std::cout << "cfloat bits       : " << to_binary(bits, nbits, true) << '\n';
2919
#endif
2920
                                        setbits(bits);
129,807,702✔
2921
                                }
2922
                                else {
2923
                                        // the source real is a subnormal number                                
2924
                                        mask = 0x00FF'FFFFu >> (fbits + exponent + subnormal_reciprocal_shift[es] + 1); // mask for sticky bit 
×
2925

2926
                                        // fraction processing: we have fbits+1 bits = 1 hidden + fbits explicit fraction bits 
2927
                                        // f = 1.ffff  2^exponent * 2^fbits * 2^-(2-2^(es-1)) = 1.ff...ff >> (23 - (-exponent + fbits - (2 -2^(es-1))))
2928
                                        // -exponent because we are right shifting and exponent in this range is negative
2929
                                        adjustment = -(exponent + subnormal_reciprocal_shift[es]); // this is the right shift adjustment due to the scale of the input number, i.e. the exponent of 2^-adjustment
×
2930
#if TRACE_CONVERSION                                        
2931
                                        std::cout << "source is subnormal: TBD\n";
2932
                                        std::cout << "shift to LSB    " << (rightShift + adjustment) << '\n';
2933
                                        std::cout << "adjustment      " << adjustment << '\n';
2934
                                        std::cout << "exponent        " << exponent << '\n';
2935
                                        std::cout << "subnormal shift " << subnormal_reciprocal_shift[es] << '\n';
2936
#endif
2937
                                        if (exponent >= (MIN_EXP_SUBNORMAL - 1) && exponent < MIN_EXP_NORMAL) {
2938
                                                // the value is a subnormal number in this representation
2939
                                        }
2940
                                        else {
2941
                                                // the value is a normal number in this representation
2942
                                        }
2943
                                }
2944
                        }
2945
                        else {
2946
                                // no need to round, but we need to shift left to deliver the bits
2947
                                // cfloat<40,  8> = float
2948
                                // cfloat<48,  9> = float
2949
                                // cfloat<56, 10> = float
2950
                                // cfloat<64, 11> = float
2951
                                // cfloat<64, 10> = double 
2952
                                // can we go from an input subnormal to a cfloat normal? 
2953
                                // yes, for example a cfloat<64,11> assigned to a subnormal float
2954

2955
                                // map exponent into target cfloat encoding
2956
                                constexpr int upshift = fbits - ieee754_parameter<Real>::fbits;
259,475✔
2957
                                uint64_t biasedExponent{ 0 };
259,475✔
2958
                                bool subnormalInTarget = (exponent < MIN_EXP_NORMAL);
259,475✔
2959
                                int denormShift = 0;
259,475✔
2960
                                if (subnormalInTarget) {
259,475✔
2961
                                        biasedExponent = 0;
640✔
2962
                                        denormShift = MIN_EXP_NORMAL - exponent;
640✔
2963
                                }
2964
                                else {
2965
                                        biasedExponent = static_cast<uint64_t>(static_cast<int64_t>(exponent) + EXP_BIAS);
258,835✔
2966
                                }
2967
                                // output processing
2968
                                if constexpr (nbits < 65) {
2969
                                        // we can compose the bits in a native 64-bit unsigned integer
2970
                                        // common case: normal to normal
2971
                                        // reference example: nbits = 40, es = 8, fbits = 31: rhs = float fbits = 23; shift left by (31 - 23) = 8
2972

2973
                                        if (rawExponent != 0) {
199,210✔
2974
                                                // rhs is a normal encoding (or a normalized former-subnormal)
2975
                                                uint64_t raw{ s ? 1ull : 0ull };
199,210✔
2976
                                                raw <<= es;
199,210✔
2977
                                                raw |= biasedExponent;
199,210✔
2978
                                                raw <<= fbits;
199,210✔
2979
                                                if (subnormalInTarget) {
199,210✔
2980
                                                        // add the hidden bit and denormalize the fraction
2981
                                                        uint64_t significand = rawFraction | ieee754_parameter<Real>::hmask;
623✔
2982
                                                        int netShift = upshift - denormShift;
623✔
2983
                                                        if (netShift >= 0) {
623✔
2984
                                                                rawFraction = significand << netShift;
623✔
2985
                                                        }
2986
                                                        else {
2987
                                                                // right shift with round-to-nearest-even
2988
                                                                int rshift = -netShift;
×
2989
                                                                uint64_t lsbMask = (1ull << rshift);
×
2990
                                                                bool lsb    = (significand & lsbMask) != 0;
×
2991
                                                                bool guard  = (rshift >= 1) && ((significand & (lsbMask >> 1)) != 0);
×
2992
                                                                bool round  = (rshift >= 2) && ((significand & (lsbMask >> 2)) != 0);
×
2993
                                                                bool sticky = (rshift >= 3) && ((significand & ((lsbMask >> 2) - 1)) != 0);
×
2994
                                                                rawFraction = significand >> rshift;
×
2995
                                                                if (guard) {
×
2996
                                                                        if (lsb && (!round && !sticky)) ++rawFraction; // round to even
×
2997
                                                                        if (round || sticky) ++rawFraction;
×
2998
                                                                }
2999
                                                        }
3000
                                                }
3001
                                                else {
3002
                                                        rawFraction <<= upshift;
198,587✔
3003
                                                }
3004
                                                raw |= rawFraction;
199,210✔
3005
                                                setbits(raw);
199,210✔
3006
                                        }
3007
                                        else {
3008
                                                // rhs is a subnormal (unreachable after normalization above, kept for safety)
3009
                                        }
3010
                                }
3011
                                else {
3012
                                        // we need to write and shift bits into place
3013
                                        // use cases are cfloats like cfloat<80, 11, bt>
3014
                                        // even though the bits that come in are single or double precision
3015
                                        // we need to write the fields and then shifting them in place
3016
                                        // 
3017
                                        // common case: normal to normal
3018
                                        if constexpr (bitsInBlock < 64) {
3019
                                                if (rawExponent != 0) {
60,265✔
3020
                                                        // reference example: nbits = 128, es = 15, fbits = 112: rhs = float: shift left by (112 - 23) = 89
3021
                                                        setbits(biasedExponent);
60,265✔
3022
                                                        shiftLeft(fbits);
60,265✔
3023
                                                        // if subnormal in target, add hidden bit before copying
3024
                                                        uint64_t fractionToCopy = subnormalInTarget
60,265✔
3025
                                                                ? (rawFraction | ieee754_parameter<Real>::hmask)
60,265✔
3026
                                                                : rawFraction;
3027
                                                        // shift fraction bits
3028
                                                        int bitsToShift = subnormalInTarget ? (upshift - denormShift) : upshift;
60,265✔
3029
                                                        // for subnormal targets near minpos, bitsToShift can be negative (right shift)
3030
                                                        // apply rounding on the 64-bit significand before splitting into blocks
3031
                                                        if (bitsToShift < 0) {
60,265✔
3032
                                                                int rshift = -bitsToShift;
×
3033
                                                                uint64_t lsbMask = (1ull << rshift);
×
3034
                                                                bool lsb    = (fractionToCopy & lsbMask) != 0;
×
3035
                                                                bool guard  = (rshift >= 1) && ((fractionToCopy & (lsbMask >> 1)) != 0);
×
3036
                                                                bool round  = (rshift >= 2) && ((fractionToCopy & (lsbMask >> 2)) != 0);
×
3037
                                                                bool sticky = (rshift >= 3) && ((fractionToCopy & ((lsbMask >> 2) - 1)) != 0);
×
3038
                                                                fractionToCopy >>= rshift;
×
3039
                                                                if (guard) {
×
3040
                                                                        if (lsb && (!round && !sticky)) ++fractionToCopy; // round to even
×
3041
                                                                        if (round || sticky) ++fractionToCopy;
×
3042
                                                                }
3043
                                                                bitsToShift = 0;
×
3044
                                                        }
3045
                                                        bt fractionBlock[nrBlocks]{ 0 };
60,265✔
3046
                                                        // copy fraction bits
3047
                                                        unsigned blocksRequired = (8 * sizeof(fractionToCopy) + 1) / bitsInBlock;
60,265✔
3048
                                                        unsigned maxBlockNr = (blocksRequired < nrBlocks ? blocksRequired : nrBlocks);
60,265✔
3049
                                                        uint64_t mask = static_cast<uint64_t>(ALL_ONES); // set up the block mask
60,265✔
3050
                                                        unsigned shift = 0;
60,265✔
3051
                                                        for (unsigned i = 0; i < maxBlockNr; ++i) {
340,893✔
3052
                                                                fractionBlock[i] = bt((mask & fractionToCopy) >> shift);
280,628✔
3053
                                                                mask <<= bitsInBlock;
280,628✔
3054
                                                                shift += bitsInBlock;
280,628✔
3055
                                                        }
3056
                                                        if (bitsToShift >= static_cast<int>(bitsInBlock)) {
60,265✔
3057
                                                                int blockShift = static_cast<int>(bitsToShift / bitsInBlock);
50,235✔
3058
                                                                for (int i = MSU; i >= blockShift; --i) {
271,095✔
3059
                                                                        fractionBlock[i] = fractionBlock[i - blockShift];
220,860✔
3060
                                                                }
3061
                                                                for (int i = blockShift - 1; i >= 0; --i) {
160,821✔
3062
                                                                        fractionBlock[i] = bt(0);
110,586✔
3063
                                                                }
3064
                                                                // adjust the shift
3065
                                                                bitsToShift -= blockShift * bitsInBlock;
50,235✔
3066
                                                        }
3067
                                                        if (bitsToShift > 0) {
60,265✔
3068
                                                                // construct the mask for the upper bits in the block that need to move to the higher word
3069
                                                                bt bitsToMoveMask = bt(ALL_ONES << (bitsInBlock - bitsToShift));
40,241✔
3070
                                                                for (unsigned i = MSU; i > 0; --i) {
211,283✔
3071
                                                                        fractionBlock[i] <<= bitsToShift;
171,042✔
3072
                                                                        // mix in the bits from the right
3073
                                                                        bt fracbits = static_cast<bt>(bitsToMoveMask & fractionBlock[i - 1]); // operator & yields an int
171,042✔
3074
                                                                        fractionBlock[i] |= (fracbits >> (bitsInBlock - bitsToShift));
171,042✔
3075
                                                                }
3076
                                                                fractionBlock[0] <<= bitsToShift;
40,241✔
3077
                                                        }
3078
                                                        // OR the bits in
3079
                                                        for (unsigned i = 0; i <= MSU; ++i) {
421,813✔
3080
                                                                _block[i] |= fractionBlock[i];
361,548✔
3081
                                                        }
3082
                                                        // enforce precondition for fast comparison by properly nulling bits that are outside of nbits
3083
                                                        _block[MSU] &= MSU_MASK;
60,265✔
3084
                                                        // finally, set the sign bit
3085
                                                        setsign(s);
60,265✔
3086
                                                }
3087
                                                else {
3088
                                                        // rhs is a subnormal
3089
                //                                        std::cerr << "rhs is a subnormal : " << to_binary(rhs) << " : " << rhs << '\n';
3090
                                                }
3091
                                        }
3092
                                        else {
3093
                                                // BlockType is incorrect
3094
                                        }
3095
                                }
3096
                        }
3097
                        // post-processing results to implement saturation and projection after rounding logic
3098
                        if constexpr (isSaturating) {
3099
                                if (isinf(INF_TYPE_POSITIVE) || isnan(NAN_TYPE_QUIET)) {
20,168,757✔
3100
                                        maxpos();
890✔
3101
                                }
3102
                                else if (isinf(INF_TYPE_NEGATIVE) || isnan(NAN_TYPE_SIGNALLING)) {
20,167,867✔
3103
                                        maxneg();
890✔
3104
                                }
3105
                        }
3106
                        else {
3107
                                if (isnan(NAN_TYPE_QUIET)) {
109,898,420✔
3108
                                        setinf(false);
301,658✔
3109
                                }
3110
                                else if (isnan(NAN_TYPE_SIGNALLING)) {
109,596,762✔
3111
                                        setinf(true);
300,610✔
3112
                                }
3113
                        }
3114
                        return *this;  // TODO: unreachable in some configurations        
130,067,177✔
3115
                }
3116
        }
3117

3118
        // post-processing results to implement saturation and projection after rounding logic
3119
        // arithmetic bit operations can't produce NaN encodings, so we need to re-interpret
3120
        // these encodings and 'project' them to the proper values.
3121
        void constexpr post_process() noexcept {
3122
                if constexpr (isSaturating) {
3123
                        if (isinf(INF_TYPE_POSITIVE) || isnan(NAN_TYPE_QUIET)) {
3124
                                maxpos();
3125
                        }
3126
                        else if (isinf(INF_TYPE_NEGATIVE) || isnan(NAN_TYPE_SIGNALLING)) {
3127
                                maxneg();
3128
                        }
3129
                }
3130
                else {
3131
                        if (isnan(NAN_TYPE_QUIET)) {
3132
                                setinf(false);
3133
                        }
3134
                        else if (isnan(NAN_TYPE_SIGNALLING)) {
3135
                                setinf(true);
3136
                        }
3137
                }
3138
        }
3139

3140
protected:
3141

3142
        /// <summary>
3143
        /// round a set of source bits to the present representation.
3144
        /// srcbits is the number of bits of significant in the source representation
3145
        /// </summary>
3146
        /// <typeparam name="StorageType"></typeparam>
3147
        /// <param name="raw"></param>
3148
        /// <returns></returns>
3149
        template<unsigned srcbits, typename StorageType>
3150
        constexpr uint64_t round(StorageType raw, int& exponent) noexcept {
12,858✔
3151
                if constexpr (fhbits < srcbits) {
3152
                        // round to even: lsb guard round sticky
3153
                    // collect guard, round, and sticky bits
3154
                    // this same logic will work for the case where
3155
                    // we only have a guard bit and no round and sticky bits
3156
                    // because the mask logic will make round and sticky both 0
3157
                        constexpr uint32_t shift = srcbits - fhbits - 1ull;
9,875✔
3158
                        StorageType mask = (StorageType(1ull) << shift);
9,875✔
3159
                        bool guard = (mask & raw);
9,875✔
3160
                        mask >>= 1;
9,875✔
3161
                        bool round = (mask & raw);
9,875✔
3162
                        if constexpr (shift > 1u) { // protect against a negative shift
3163
                                StorageType allones(StorageType(~0));
9,875✔
3164
                                mask = StorageType(allones << (shift - 1));
9,875✔
3165
                                mask = ~mask;
9,875✔
3166
                        }
3167
                        else {
3168
                                mask = 0;
3169
                        }
3170
                        bool sticky = (mask & raw);
9,875✔
3171

3172
                        raw >>= (shift + 1);  // shift out the bits we are rounding away
9,875✔
3173
                        bool lsb = (raw & 0x1u);
9,875✔
3174
                        //  ... lsb | guard  round sticky   round
3175
                        //       x     0       x     x       down
3176
                        //       0     1       0     0       down  round to even
3177
                        //       1     1       0     0        up   round to even
3178
                        //       x     1       0     1        up
3179
                        //       x     1       1     0        up
3180
                        //       x     1       1     1        up
3181
                        if (guard) {
9,875✔
3182
                                if (lsb && (!round && !sticky)) ++raw; // round to even
3,117✔
3183
                                if (round || sticky) ++raw;
3,117✔
3184
                                if (raw == (1ull << fbits)) { // overflow into next power of two
3,117✔
3185
                                        ++exponent;
135✔
3186
                                        raw = 0; // fraction is zero after carry into hidden bit
135✔
3187
                                }
3188
                        }
3189
                }
3190
                else {
3191
                        // Target has more precision than source - need to left-shift to align
3192
                        // For large types where fhbits > 64, the fraction bits cannot fit in
3193
                        // a 64-bit raw after shifting. In this case, skip the shift and let
3194
                        // convert_signed/unsigned_integer place bits using setbit().
3195
                        // The caller positions fraction bits at the top of srcbits, and we
3196
                        // need to ensure they don't overflow 64 bits after our shift.
3197
                        constexpr unsigned shift = fhbits - srcbits;
2,983✔
3198
                        if constexpr (fhbits <= 64 && shift < 64) {
3199
                                raw <<= shift;
2,582✔
3200
                        }
3201
                        // else: raw stays as-is; caller will extract bits and place them
3202
                }
3203
                uint64_t significant = raw;
12,858✔
3204
                return significant;
12,858✔
3205
        }
3206

3207
        template<typename ArgumentBlockType>
3208
        constexpr void copyBits(ArgumentBlockType v) {
3209
                unsigned blocksRequired = (8 * sizeof(v) + 1 ) / bitsInBlock;
3210
                unsigned maxBlockNr = (blocksRequired < nrBlocks ? blocksRequired : nrBlocks);
3211
                bt b{ 0ul }; b = bt(~b);
3212
                ArgumentBlockType mask = ArgumentBlockType(b);
3213
                unsigned shift = 0;
3214
                for (unsigned i = 0; i < maxBlockNr; ++i) {
3215
                        _block[i] = bt((mask & v) >> shift);
3216
                        mask <<= bitsInBlock;
3217
                        shift += bitsInBlock;
3218
                }
3219
        }
3220
        void shiftLeft(int leftShift) {
3221
                if (leftShift == 0) return;
3222
                if (leftShift < 0) return shiftRight(-leftShift);
3223
                if (leftShift > long(nbits)) leftShift = nbits; // clip to max
3224
                if (leftShift >= long(bitsInBlock)) {
3225
                        int blockShift = leftShift / static_cast<int>(bitsInBlock);
3226
                        for (signed i = signed(MSU); i >= blockShift; --i) {
3227
                                _block[i] = _block[i - blockShift];
3228
                        }
3229
                        for (signed i = blockShift - 1; i >= 0; --i) {
3230
                                _block[i] = bt(0);
3231
                        }
3232
                        // adjust the shift
3233
                        leftShift -= (long)(blockShift * bitsInBlock);
3234
                        if (leftShift == 0) return;
3235
                }
3236
                // construct the mask for the upper bits in the block that need to move to the higher word
3237
//                bt mask = static_cast<bt>(0xFFFFFFFFFFFFFFFFull << (bitsInBlock - leftShift));
3238
                bt mask = ALL_ONES;
3239
                mask <<= (bitsInBlock - leftShift);
3240
                for (unsigned i = MSU; i > 0; --i) {
3241
                        _block[i] <<= leftShift;
3242
                        // mix in the bits from the right
3243
                        bt bits = static_cast<bt>(mask & _block[i - 1]);
3244
                        _block[i] |= (bits >> (bitsInBlock - leftShift));
3245
                }
3246
                _block[0] <<= leftShift;
3247
        }
3248
        void shiftRight(int rightShift) {
3249
                if (rightShift == 0) return;
3250
                if (rightShift < 0) return shiftLeft(-rightShift);
3251
                if (rightShift >= long(nbits)) {
3252
                        setzero();
3253
                        return;
3254
                }
3255
                bool signext = sign();
3256
                unsigned blockShift = 0;
3257
                if (rightShift >= long(bitsInBlock)) {
3258
                        blockShift = rightShift / bitsInBlock;
3259
                        if (MSU >= blockShift) {
3260
                                // shift by blocks
3261
                                for (unsigned i = 0; i <= MSU - blockShift; ++i) {
3262
                                        _block[i] = _block[i + blockShift];
3263
                                }
3264
                        }
3265
                        // adjust the shift
3266
                        rightShift -= (long)(blockShift * bitsInBlock);
3267
                        if (rightShift == 0) {
3268
                                // fix up the leading zeros if we have a negative number
3269
                                if (signext) {
3270
                                        // rightShift is guaranteed to be less than nbits
3271
                                        rightShift += (long)(blockShift * bitsInBlock);
3272
                                        for (unsigned i = nbits - rightShift; i < nbits; ++i) {
3273
                                                this->setbit(i);
3274
                                        }
3275
                                }
3276
                                else {
3277
                                        // clean up the blocks we have shifted clean
3278
                                        rightShift += (long)(blockShift * bitsInBlock);
3279
                                        for (unsigned i = nbits - rightShift; i < nbits; ++i) {
3280
                                                this->setbit(i, false);
3281
                                        }
3282
                                }
3283
                                return;  // shift was aligned to block boundary, no per-bit shift needed
3284
                        }
3285
                }
3286

3287
                bt mask = ALL_ONES;
3288
                mask >>= (bitsInBlock - rightShift); // this is a mask for the lower bits in the block that need to move to the lower word
3289
                for (unsigned i = 0; i < MSU; ++i) {  // TODO: can this be improved? we should not have to work on the upper blocks in case we block shifted
3290
                        _block[i] >>= rightShift;
3291
                        // mix in the bits from the left
3292
                        bt bits = static_cast<bt>(mask & _block[i + 1]); // & operator returns an int
3293
                        _block[i] |= (bits << (bitsInBlock - rightShift));
3294
                }
3295
                _block[MSU] >>= rightShift;
3296

3297
                // fix up the leading zeros if we have a negative number
3298
                if (signext) {
3299
                        // bitsToShift is guaranteed to be less than nbits
3300
                        rightShift += (long)(blockShift * bitsInBlock);
3301
                        for (unsigned i = nbits - rightShift; i < nbits; ++i) {
3302
                                this->setbit(i);
3303
                        }
3304
                }
3305
                else {
3306
                        // clean up the blocks we have shifted clean
3307
                        rightShift += (long)(blockShift * bitsInBlock);
3308
                        for (unsigned i = nbits - rightShift; i < nbits; ++i) {
3309
                                this->setbit(i, false);
3310
                        }
3311
                }
3312

3313
                // enforce precondition for fast comparison by properly nulling bits that are outside of nbits
3314
                _block[MSU] &= MSU_MASK;
3315
        }
3316

3317
        // calculate the integer power 2 ^ b using exponentiation by squaring
3318
        constexpr double ipow(int exponent) const {
524,923✔
3319
                bool negative = (exponent < 0);
524,923✔
3320
                exponent = negative ? -exponent : exponent;
524,923✔
3321
                double result(1.0);
524,923✔
3322
                double base = 2.0;
524,923✔
3323
                for (;;) {
3324
                        if (exponent % 2) result *= base;
3,846,180✔
3325
                        exponent >>= 1;
3,846,180✔
3326
                        if (exponent == 0) break;
3,846,180✔
3327
                        base *= base;
3,321,257✔
3328
                }
3329
                return (negative ? (1.0 / result) : result);
524,923✔
3330
        }
3331

3332
        template<BlockTripleOperator btop, typename TargetBlockType = bt>
3333
        constexpr void blockcopy(blocktriple<fbits, btop, TargetBlockType>& tgt) const {
1,056,246✔
3334
                // brute force copy of blocks
3335
                if constexpr (1 == fBlocks) {
3336
                        tgt.setblock(0, static_cast<TargetBlockType>(_block[0] & FSU_MASK));
1,050,734✔
3337
                }
3338
                else if constexpr (2 == fBlocks) {
3339
                        tgt.setblock(0, static_cast<TargetBlockType>(_block[0]));
3,028✔
3340
                        tgt.setblock(1, static_cast<TargetBlockType>(_block[1] & FSU_MASK));
3,028✔
3341
                }
3342
                else if constexpr (3 == fBlocks) {
3343
                        tgt.setblock(0, static_cast<TargetBlockType>(_block[0]));
204✔
3344
                        tgt.setblock(1, static_cast<TargetBlockType>(_block[1]));
204✔
3345
                        tgt.setblock(2, static_cast<TargetBlockType>(_block[2] & FSU_MASK));
204✔
3346
                }
3347
                else if constexpr (4 == fBlocks) {
3348
                        tgt.setblock(0, static_cast<TargetBlockType>(_block[0]));
1,500✔
3349
                        tgt.setblock(1, static_cast<TargetBlockType>(_block[1]));
1,500✔
3350
                        tgt.setblock(2, static_cast<TargetBlockType>(_block[2]));
1,500✔
3351
                        tgt.setblock(3, static_cast<TargetBlockType>(_block[3] & FSU_MASK));
1,500✔
3352
                }
3353
                else if constexpr (5 == fBlocks) {
3354
                        tgt.setblock(0, static_cast<TargetBlockType>(_block[0]));
×
3355
                        tgt.setblock(1, static_cast<TargetBlockType>(_block[1]));
×
3356
                        tgt.setblock(2, static_cast<TargetBlockType>(_block[2]));
×
3357
                        tgt.setblock(3, static_cast<TargetBlockType>(_block[3]));
×
3358
                        tgt.setblock(4, static_cast<TargetBlockType>(_block[4] & FSU_MASK));
×
3359
                }
3360
                else if constexpr (6 == fBlocks) {
3361
                        tgt.setblock(0, static_cast<TargetBlockType>(_block[0]));
3362
                        tgt.setblock(1, static_cast<TargetBlockType>(_block[1]));
3363
                        tgt.setblock(2, static_cast<TargetBlockType>(_block[2]));
3364
                        tgt.setblock(3, static_cast<TargetBlockType>(_block[3]));
3365
                        tgt.setblock(4, static_cast<TargetBlockType>(_block[4]));
3366
                        tgt.setblock(5, static_cast<TargetBlockType>(_block[5] & FSU_MASK));
3367
                }
3368
                else if constexpr (7 == fBlocks) {
3369
                        tgt.setblock(0, static_cast<TargetBlockType>(_block[0]));
8✔
3370
                        tgt.setblock(1, static_cast<TargetBlockType>(_block[1]));
8✔
3371
                        tgt.setblock(2, static_cast<TargetBlockType>(_block[2]));
8✔
3372
                        tgt.setblock(3, static_cast<TargetBlockType>(_block[3]));
8✔
3373
                        tgt.setblock(4, static_cast<TargetBlockType>(_block[4]));
8✔
3374
                        tgt.setblock(5, static_cast<TargetBlockType>(_block[5]));
8✔
3375
                        tgt.setblock(6, static_cast<TargetBlockType>(_block[6] & FSU_MASK));
8✔
3376
                }
3377
                else if constexpr (8 == fBlocks) {
3378
                        tgt.setblock(0, static_cast<TargetBlockType>(_block[0]));
370✔
3379
                        tgt.setblock(1, static_cast<TargetBlockType>(_block[1]));
370✔
3380
                        tgt.setblock(2, static_cast<TargetBlockType>(_block[2]));
370✔
3381
                        tgt.setblock(3, static_cast<TargetBlockType>(_block[3]));
370✔
3382
                        tgt.setblock(4, static_cast<TargetBlockType>(_block[4]));
370✔
3383
                        tgt.setblock(5, static_cast<TargetBlockType>(_block[5]));
370✔
3384
                        tgt.setblock(6, static_cast<TargetBlockType>(_block[6]));
370✔
3385
                        tgt.setblock(7, static_cast<TargetBlockType>(_block[7] & FSU_MASK));
370✔
3386
                }
3387
                else {
3388
                        for (unsigned i = 0; i < FSU; ++i) {
4,127✔
3389
                                tgt.setblock(i, static_cast<TargetBlockType>(_block[i]));
3,725✔
3390
                        }
3391
                        tgt.setblock(FSU, static_cast<TargetBlockType>(_block[FSU] & FSU_MASK));
402✔
3392
                }
3393
        }
1,056,246✔
3394

3395
private:
3396
        bt _block[nrBlocks];
3397

3398
        //////////////////////////////////////////////////////////////////////////////
3399
        // friend functions
3400

3401
        // template parameters need names different from class template parameters (for gcc and clang)
3402
        template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3403
        friend std::ostream& operator<< (std::ostream& ostr, const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& r);
3404
        template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3405
        friend std::istream& operator>> (std::istream& istr, cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& r);
3406

3407
        template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3408
        friend constexpr bool operator==(const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& lhs, const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& rhs);
3409
        template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3410
        friend constexpr bool operator!=(const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& lhs, const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& rhs);
3411
        template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3412
        friend constexpr bool operator< (const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& lhs, const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& rhs);
3413
        template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3414
        friend constexpr bool operator> (const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& lhs, const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& rhs);
3415
        template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3416
        friend constexpr bool operator<=(const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& lhs, const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& rhs);
3417
        template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3418
        friend constexpr bool operator>=(const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& lhs, const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& rhs);
3419
};
3420

3421
///////////////////////////// IOSTREAM operators ///////////////////////////////////////////////
3422

3423
// convert cfloat to decimal fixpnt string, i.e. "-1234.5678"
3424
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3425
std::string to_decimal_fixpnt_string(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& value, long long precision) {
3426
        constexpr unsigned fbits = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>::fbits;
3427
        constexpr unsigned bias = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>::EXP_BIAS;
3428
        std::stringstream str;
3429
        if (value.iszero()) {
3430
                str << '0';
3431
                return str.str();
3432
        }
3433
        if (value.sign()) str << '-';
3434

3435
        // construct the discretization levels of the fraction part
3436
        support::decimal range, discretizationLevels, step;
3437
        // create the decimal range we are discretizing
3438
        range.setdigit(1);
3439
        range.shiftLeft(fbits); // the decimal range of the fraction
3440
        discretizationLevels.powerOf2(fbits); // calculate the discretization levels of this range
3441
        step = div(range, discretizationLevels);
3442
        // now construct the value of this range by adding the fraction samples
3443
        support::decimal partial, multiplier;
3444
        partial.setzero();  // if you just want the fraction
3445
        multiplier.setdigit(1);
3446
        // convert the fraction part
3447
        for (unsigned i = 0; i < fbits; ++i) {
3448
                if (value.at(i)) {
3449
                        support::add(partial, multiplier);
3450
                }
3451
                support::add(multiplier, multiplier);
3452
        }
3453
        if (value.isdenormal()) {
3454
                support::mul(partial, step);
3455
                support::decimal scale;
3456
                scale.powerOf2(bias - 1ull);
3457
                partial = support::div(partial, scale);
3458
        } 
3459
        else {
3460
                support::add(partial, multiplier); // add the hidden bit
3461
                support::mul(partial, step);
3462
                support::decimal scale;
3463
                int exponent = value.scale();
3464
                if (exponent < 0) {
3465
                        scale.powerOf2(static_cast<unsigned>(-exponent));
3466
                        partial = support::div(partial, scale);
3467
                }
3468
                else {
3469
                        scale.powerOf2(static_cast<unsigned>(exponent));
3470
                        support::mul(partial, scale);
3471
                }
3472
        }
3473

3474
        // the radix is at fbits
3475
        // The partial represents the parts in the range, so we can deduce
3476
        // the number of leading zeros by comparing to the length of range
3477
        int nrLeadingZeros = static_cast<int>(range.size()) - static_cast<int>(partial.size()) - 1;
3478
        if (nrLeadingZeros >= 0) str << "0.";
3479
        for (int i = 0; i < nrLeadingZeros; ++i) str << '0';
3480
        int digitsWritten = (nrLeadingZeros > 0) ? nrLeadingZeros : 0;
3481
        int position = static_cast<int>(partial.size()) - 1;
3482
        for (support::decimal::const_reverse_iterator rit = partial.rbegin(); rit != partial.rend(); ++rit) {
3483
                str << (int)*rit;
3484
                ++digitsWritten;
3485
                if (position == fbits) str << '.';
3486
                --position;
3487
        }
3488
        if (digitsWritten < precision) { // deal with trailing 0s
3489
                for (unsigned i = static_cast<unsigned>(digitsWritten); i < fbits; ++i) {
3490
                        str << '0';
3491
                }
3492
        }
3493

3494
        return str.str();
3495
}
3496

3497
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3498
std::string to_string(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& value, long long precision) {
3499
        constexpr unsigned fbits = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>::fbits;
3500
        std::stringstream str;
3501
        if (value.iszero()) {
3502
                str << '0';
3503
                return str.str();
3504
        }
3505
        if (value.sign()) str << '-';
3506

3507
        // denormalize the number to gain access to the most sigificant digits
3508
        // 1.ffff^e
3509
        // scale is e
3510
        // lsbScale is e - fbits
3511
        // shift to get lsb to position 2^0 = (e - fbits)
3512
        std::int64_t scale = value.scale();
3513
//        std::int64_t shift = scale + fbits; // we want the lsb at 2^0
3514
        std::int64_t lsbScale = scale - fbits;  // scale of the lsb
3515
        support::decimal partial, multiplier;
3516
        partial.setzero();
3517

3518
        multiplier.powerOf2(lsbScale);
3519

3520
        // convert the fraction bits 
3521
        for (unsigned i = 0; i < fbits; ++i) {
3522
                if (value.at(i)) {
3523
                        support::add(partial, multiplier);
3524
                }
3525
                support::add(multiplier, multiplier);
3526
        }
3527
        if (!value.isdenormal()) {
3528
                support::add(partial, multiplier); // add the hidden bit
3529
        }
3530
        str << partial;
3531
        return str.str();
3532
}
3533

3534
//////////////////////////////////////////////////////////////////////////////////////////////
3535
/// stream operators
3536

3537
// ostream output generates an ASCII format for the floating-point argument
3538
// Uses native binary-to-decimal conversion via blocktriple::to_string()
3539
// to produce exact output for all cfloat sizes without double conversion.
3540
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3541
inline std::ostream& operator<<(std::ostream& ostr, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& v) {
3,126✔
3542
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
3543
        constexpr unsigned cfbits = Cfloat::fbits;
3,126✔
3544

3545
        std::streamsize prec  = ostr.precision();
3,126✔
3546
        std::streamsize width = ostr.width();
3,126✔
3547
        std::ios_base::fmtflags ff = ostr.flags();
3,126✔
3548
        bool bFixed      = (ff & std::ios_base::fixed) == std::ios_base::fixed;
3,126✔
3549
        bool bScientific = (ff & std::ios_base::scientific) == std::ios_base::scientific;
3,126✔
3550
        bool bShowpos    = (ff & std::ios_base::showpos) != 0;
3,126✔
3551
        bool bUppercase  = (ff & std::ios_base::uppercase) != 0;
3,126✔
3552
        bool bInternal   = (ff & std::ios_base::internal) != 0;
3,126✔
3553
        bool bLeft       = (ff & std::ios_base::left) != 0;
3,126✔
3554
        char fillChar    = ostr.fill();
3,126✔
3555

3556
        if constexpr (cfbits == 0) {
3557
                // degenerate cfloat with no fraction bits: fall back to double
3558
                std::ostringstream oss;
3559
                oss.precision(prec);
3560
                if (bFixed) oss << std::fixed;
3561
                if (bScientific) oss << std::scientific;
3562
                if (bUppercase) oss << std::uppercase;
3563
                if (bShowpos) oss << std::showpos;
3564
                oss << static_cast<double>(v);
3565
                std::string s = oss.str();
3566
                if (width > 0 && s.length() < static_cast<size_t>(width)) {
3567
                        size_t pad = static_cast<size_t>(width) - s.length();
3568
                        if (bLeft) { s.append(pad, fillChar); }
3569
                        else { s.insert(0u, pad, fillChar); }
3570
                }
3571
                return ostr << s;
3572
        } else {
3573
                blocktriple<cfbits, BlockTripleOperator::REP, bt> a;
3,126✔
3574
                v.normalize(a);
3,126✔
3575
                return ostr << a.to_string(prec, width, bFixed, bScientific,
6,252✔
3576
                                           bInternal, bLeft, bShowpos, bUppercase, fillChar);
6,252✔
3577
        }
3578
}
3579

3580
// parse a cfloat from a string in either cfloat hex format (nbits.esxHEXVALUEc)
3581
// or a decimal floating-point representation
3582
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3583
bool parse(const std::string& txt, cfloat<nbits,es,bt,hasSubnormals,hasMaxExpValues,isSaturating>& v) {
24✔
3584
        // check if the txt is of the native cfloat form: nbits.esX[0x]hexvaluec
3585
        std::regex cfloat_regex(R"(^[0-9]+\.[0-9]+[xX](0[xX])?[0-9A-Fa-f]+c?$)");
24✔
3586
        if (std::regex_match(txt, cfloat_regex)) {
24✔
3587
                // found a cfloat representation: parse nbits.esxHEXVALUEc
3588
                std::string nbitsStr, esStr, bitStr;
3✔
3589
                auto it = txt.begin();
3✔
3590
                for (; it != txt.end(); ++it) {
9✔
3591
                        if (*it == '.') break;
9✔
3592
                        nbitsStr.append(1, *it);
6✔
3593
                }
3594
                for (++it; it != txt.end(); ++it) {
6✔
3595
                        if (*it == 'x' || *it == 'X') break;
6✔
3596
                        esStr.append(1, *it);
3✔
3597
                }
3598
                for (++it; it != txt.end(); ++it) {
21✔
3599
                        if (*it == 'c') break;
21✔
3600
                        bitStr.append(1, *it);
18✔
3601
                }
3602
                unsigned nbits_in = 0;
3✔
3603
                unsigned es_in = 0;
3✔
3604
                {
3605
                        std::istringstream ss(nbitsStr);
3✔
3606
                        ss >> nbits_in;
3✔
3607
                        if (ss.fail()) return false;
3✔
3608
                }
3✔
3609
                {
3610
                        std::istringstream ss(esStr);
3✔
3611
                        ss >> es_in;
3✔
3612
                        if (ss.fail()) return false;
3✔
3613
                }
3✔
3614
                // native cfloat form must match target configuration
3615
                if (nbits_in != nbits || es_in != es) return false;
3✔
3616
                uint64_t raw = 0;
2✔
3617
                std::istringstream ss(bitStr);
2✔
3618
                ss >> std::hex >> raw;
2✔
3619
                if (ss.fail()) return false;
2✔
3620
                ss >> std::ws;
2✔
3621
                if (!ss.eof()) return false;
2✔
3622
                v.setbits(raw);
2✔
3623
                return true;
2✔
3624
        }
3✔
3625
        else {
3626
                // assume it is a float/double/long double representation
3627
                std::istringstream ss(txt);
21✔
3628
                double d;
3629
                ss >> d;
21✔
3630
                if (ss.fail()) return false;
21✔
3631
                ss >> std::ws;
21✔
3632
                if (!ss.eof()) return false;
21✔
3633
                v = d;
20✔
3634
                return true;
20✔
3635
        }
21✔
3636
}
24✔
3637

3638
// read an ASCII float or cfloat format: nbits.esxNN...NNc, for example: 16.5x7C00c
3639
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3640
inline std::istream& operator>>(std::istream& istr, cfloat<nbits,es,bt,hasSubnormals,hasMaxExpValues,isSaturating>& v) {
17✔
3641
        std::string txt;
17✔
3642
        istr >> txt;
17✔
3643
        if (!parse(txt, v)) {
17✔
3644
                std::cerr << "unable to parse -" << txt << "- into a cfloat value\n";
×
3645
        }
3646
        return istr;
17✔
3647
}
17✔
3648

3649
// encoding helpers
3650

3651
// return the Unit in the Last Position
3652
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3653
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> ulp(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& a) {
49✔
3654
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> b(a);
49✔
3655
        return ++b - a;
86✔
3656
}
3657

3658
// transform cfloat to a binary representation
3659
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3660
inline std::string to_binary(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& number, bool nibbleMarker = false) {
1,481✔
3661
        std::stringstream s;
1,481✔
3662
        s << "0b";
1,481✔
3663
        unsigned index = nbits;
1,481✔
3664
        s << (number.at(--index) ? '1' : '0') << '.';
1,481✔
3665

3666
        for (int i = int(es) - 1; i >= 0; --i) {
10,195✔
3667
                s << (number.at(--index) ? '1' : '0');
8,714✔
3668
                if (i > 0 && (i % 4) == 0 && nibbleMarker) s << '\'';
8,714✔
3669
        }
3670

3671
        s << '.';
1,481✔
3672

3673
        constexpr int fbits = nbits - 1ull - es;
1,481✔
3674
        for (int i = fbits - 1; i >= 0; --i) {
31,135✔
3675
                s << (number.at(--index) ? '1' : '0');
29,654✔
3676
                if (i > 0 && (i % 4) == 0 && nibbleMarker) s << '\'';
29,654✔
3677
        }
3678

3679
        return s.str();
2,962✔
3680
}
1,481✔
3681

3682
// native semantic representation: radix-2, delegates to to_binary
3683
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3684
inline std::string to_native(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& number, bool nibbleMarker = false) {
3685
        return to_binary(number, nibbleMarker);
3686
}
3687

3688
// transform a cfloat into a triple representation
3689
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3690
inline std::string to_triple(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& number, bool nibbleMarker = true) {
3✔
3691
        std::stringstream s;
3✔
3692
        blocktriple<cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>::fbits, BlockTripleOperator::REP, bt> triple;
3✔
3693
        number.normalize(triple);
3✔
3694
        s << to_triple(triple, nibbleMarker);
3✔
3695
        return s.str();
6✔
3696
}
3✔
3697

3698
// Magnitude of a cfloat (equivalent to turning the sign bit off).
3699
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3700
cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>
3701
abs(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& v) {
16,939✔
3702
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> a(v);
16,939✔
3703
        a.setsign(false);
16,939✔
3704
        return a;
16,939✔
3705
}
3706

3707
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3708
cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>
3709
fabs(cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> v) {
40✔
3710
        return abs(v);
40✔
3711
}
3712

3713
////////////////////// debug helpers
3714

3715
// convenience method to gain access to the values of the constexpr variables that govern the cfloat behavior
3716
template<unsigned nbits, unsigned es, typename bt = uint8_t, bool hasSubnormals = false, bool hasMaxExpValues = false, bool isSaturating = false>
3717
void ReportCfloatClassParameters() {
3718
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> a;
3719
        a.constexprClassParameters();
3720
}
3721

3722
//////////////////////////////////////////////////////
3723
/// cfloat - cfloat binary logic operators
3724

3725
template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3726
constexpr inline bool operator==(const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& lhs, const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& rhs) {
127,146,806✔
3727
        if (lhs.isnan() || rhs.isnan()) return false;
127,146,806✔
3728
        if (lhs.iszero() && rhs.iszero()) return true; // +0 == -0 per IEEE-754 §5.11
125,491,723✔
3729
        for (unsigned i = 0; i < lhs.nrBlocks; ++i) {
385,823,456✔
3730
                if (lhs._block[i] != rhs._block[i]) {
263,585,248✔
3731
                        return false;
2,104,957✔
3732
                }
3733
        }
3734
        return true;
122,238,208✔
3735
}
3736
template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3737
constexpr inline bool operator!=(const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& lhs, const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& rhs) { return !operator==(lhs, rhs); }
125,744,364✔
3738
template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3739
constexpr inline bool operator< (const cfloat<nnbits, nes, nbt, nsub, nsup, nsat>& lhs, const cfloat<nnbits, nes, nbt, nsub, nsup, nsat>& rhs) {
5,942,967✔
3740
        if (lhs.isnan() || rhs.isnan()) return false;
5,942,967✔
3741
        // need this as arithmetic difference is defined as snan(indeterminate)
3742
        if (lhs.isinf(INF_TYPE_NEGATIVE) && rhs.isinf(INF_TYPE_NEGATIVE)) return false;
5,827,458✔
3743
        if (lhs.isinf(INF_TYPE_POSITIVE) && rhs.isinf(INF_TYPE_POSITIVE)) return false;
5,827,444✔
3744
        if constexpr (nsub) {
3745
                cfloat<nnbits, nes, nbt, nsub, nsup, nsat> diff = (lhs - rhs);
5,758,474✔
3746
                return (!diff.iszero() && diff.sign()) ? true : false;  // got to guard against -0
5,758,474✔
3747
        }
3748
        else {
3749
                if (lhs.iszero() && rhs.iszero()) return false;  // we need to 'collapse' all zero encodings
68,937✔
3750
                if (lhs.sign() && !rhs.sign()) return true;
68,929✔
3751
                if (!lhs.sign() && rhs.sign()) return false;
51,703✔
3752
                bool positive = lhs.ispos();
34,477✔
3753
                if (positive) {
34,477✔
3754
                        if (lhs.scale() < rhs.scale()) return true;
17,257✔
3755
                        if (lhs.scale() > rhs.scale()) return false;
12,786✔
3756
                }
3757
                else {
3758
                        if (lhs.scale() > rhs.scale()) return true;
17,220✔
3759
                        if (lhs.scale() < rhs.scale()) return false;
12,770✔
3760
                }
3761
                // sign and scale are the same
3762
                if (lhs.scale() == rhs.scale()) {
16,642✔
3763
                        // compare fractions: we do not have subnormals, so we can ignore the hidden bit
3764
                        blockbinary<nnbits - 1ull - nes, nbt> l, r;
3765
                        lhs.fraction(l);
16,642✔
3766
                        rhs.fraction(r);
16,642✔
3767
                        blockbinary<nnbits - nes, nbt> ll, rr; // fbits + 1 so we can 0 extend to honor 2's complement encoding of blockbinary
3768
                        ll.assignWithoutSignExtend(l);
16,642✔
3769
                        rr.assignWithoutSignExtend(r);
16,642✔
3770
                        return (positive ? (ll < rr) : (ll > rr));
16,642✔
3771
                }
3772
                return false;
×
3773
        }
3774
}
3775
template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3776
constexpr inline bool operator> (const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& lhs, const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& rhs) { 
3,144,623✔
3777
        if (lhs.isnan() || rhs.isnan()) return false;
3,144,623✔
3778
        // need this as arithmetic difference is defined as snan(indeterminate)
3779
        if (lhs.isinf(INF_TYPE_NEGATIVE) && rhs.isinf(INF_TYPE_NEGATIVE)) return false;
3,136,509✔
3780
        if (lhs.isinf(INF_TYPE_POSITIVE) && rhs.isinf(INF_TYPE_POSITIVE)) return false;
3,136,495✔
3781
        return  operator< (rhs, lhs); 
3,136,479✔
3782
}
3783
template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3784
constexpr inline bool operator<=(const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& lhs, const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& rhs) { 
1,747,159✔
3785
        if (lhs.isnan() || rhs.isnan()) return false;
1,747,159✔
3786
        return !operator> (lhs, rhs); 
1,739,059✔
3787
}
3788
template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3789
constexpr inline bool operator>=(const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& lhs, const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& rhs) {
1,530,053✔
3790
        if (lhs.isnan() || rhs.isnan()) return false;
1,530,053✔
3791
        return !operator< (lhs, rhs); 
1,521,947✔
3792
}
3793

3794
//////////////////////////////////////////////////////
3795
/// cfloat - cfloat binary arithmetic operators
3796

3797
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3798
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator+(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
10,323,668✔
3799
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> sum(lhs);
10,323,668✔
3800
        sum += rhs;
10,323,668✔
3801
        return sum;
10,323,668✔
3802
}
3803
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3804
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator-(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
8,798,369✔
3805
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> diff(lhs);
8,798,369✔
3806
        diff -= rhs;
8,798,369✔
3807
        return diff;
8,798,369✔
3808
}
3809
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3810
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator*(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
4,170,183✔
3811
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> mul(lhs);
4,170,183✔
3812
        mul *= rhs;
4,170,183✔
3813
        return mul;
4,170,183✔
3814
}
3815
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3816
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator/(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
5,022,193✔
3817
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> ratio(lhs);
5,022,193✔
3818
        ratio /= rhs;
5,022,193✔
3819
        return ratio;
5,022,192✔
3820
}
3821

3822
/// binary cfloat - literal arithmetic operators
3823

3824
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3825
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator+(float lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3826
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> sum(lhs);
3827
        sum += rhs;
3828
        return sum;
3829
}
3830
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3831
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator-(float lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3832
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> diff(lhs);
3833
        diff -= rhs;
3834
        return diff;
3835
}
3836
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3837
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator*(float lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3838
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> mul(lhs);
3839
        mul *= rhs;
3840
        return mul;
3841
}
3842
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3843
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator/(float lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
2✔
3844
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> ratio(lhs);
2✔
3845
        ratio /= rhs;
2✔
3846
        return ratio;
2✔
3847
}
3848

3849
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3850
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator+(double lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
4✔
3851
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> sum(lhs);
4✔
3852
        sum += rhs;
4✔
3853
        return sum;
4✔
3854
}
3855
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3856
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator-(double lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3857
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> diff(lhs);
3858
        diff -= rhs;
3859
        return diff;
3860
}
3861
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3862
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator*(double lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
16✔
3863
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> mul(lhs);
16✔
3864
        mul *= rhs;
16✔
3865
        return mul;
16✔
3866
}
3867
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3868
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator/(double lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3869
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> ratio(lhs);
3870
        ratio /= rhs;
3871
        return ratio;
3872
}
3873

3874
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3875
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator+(int lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
×
3876
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> sum(lhs);
×
3877
        sum += rhs;
×
3878
        return sum;
×
3879
}
3880
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3881
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator-(int lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3882
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> diff(lhs);
3883
        diff -= rhs;
3884
        return diff;
3885
}
3886
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3887
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator*(int lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
38✔
3888
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> mul(lhs);
38✔
3889
        mul *= rhs;
38✔
3890
        return mul;
38✔
3891
}
3892
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3893
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator/(int lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
374✔
3894
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> ratio(lhs);
374✔
3895
        ratio /= rhs;
374✔
3896
        return ratio;
374✔
3897
}
3898

3899
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3900
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator+(unsigned int lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3901
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> sum(lhs);
3902
        sum += rhs;
3903
        return sum;
3904
}
3905
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3906
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator-(unsigned int lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3907
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> diff(lhs);
3908
        diff -= rhs;
3909
        return diff;
3910
}
3911
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3912
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator*(unsigned int lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3913
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> mul(lhs);
3914
        mul *= rhs;
3915
        return mul;
3916
}
3917
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3918
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator/(unsigned int lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3919
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> ratio(lhs);
3920
        ratio /= rhs;
3921
        return ratio;
3922
}
3923

3924
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3925
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator+(long long lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3926
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> sum(lhs);
3927
        sum += rhs;
3928
        return sum;
3929
}
3930
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3931
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator-(long long lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3932
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> diff(lhs);
3933
        diff -= rhs;
3934
        return diff;
3935
}
3936
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3937
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator*(long long lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3938
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> mul(lhs);
3939
        mul *= rhs;
3940
        return mul;
3941
}
3942
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3943
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator/(long long lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3944
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> ratio(lhs);
3945
        ratio /= rhs;
3946
        return ratio;
3947
}
3948

3949
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3950
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator+(unsigned long long lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3951
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> sum(lhs);
3952
        sum += rhs;
3953
        return sum;
3954
}
3955
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3956
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator-(unsigned long long lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3957
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> diff(lhs);
3958
        diff -= rhs;
3959
        return diff;
3960
}
3961
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3962
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator*(unsigned long long lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3963
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> mul(lhs);
3964
        mul *= rhs;
3965
        return mul;
3966
}
3967
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3968
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator/(unsigned long long lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3969
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> ratio(lhs);
3970
        ratio /= rhs;
3971
        return ratio;
3972
}
3973

3974
///  binary cfloat - literal arithmetic operators
3975

3976
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3977
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator+(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, float rhs) {
3978
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
3979
        Cfloat sum(lhs);
3980
        sum += Cfloat(rhs);
3981
        return sum;
3982
}
3983
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3984
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator-(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, float rhs) {
3985
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
3986
        Cfloat diff(lhs);
3987
        diff -= rhs;
3988
        return diff;
3989
}
3990
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3991
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator*(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, float rhs) {
3992
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
3993
        Cfloat mul(lhs);
3994
        mul *= Cfloat(rhs);
3995
        return mul;
3996
}
3997
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3998
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator/(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, float rhs) {
3999
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
4000
        Cfloat ratio(lhs);
4001
        ratio /= Cfloat(rhs);
4002
        return ratio;
4003
}
4004

4005
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4006
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator+(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, double rhs) {
4007
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
4008
        Cfloat sum(lhs);
4009
        sum += Cfloat(rhs);
4010
        return sum;
4011
}
4012
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4013
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator-(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, double rhs) {
4✔
4014
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
4015
        Cfloat diff(lhs);
4✔
4016
        diff -= rhs;
4✔
4017
        return diff;
4✔
4018
}
4019
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4020
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator*(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, double rhs) {
4021
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
4022
        Cfloat mul(lhs);
4023
        mul *= Cfloat(rhs);
4024
        return mul;
4025
}
4026
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4027
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator/(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, double rhs) {
4028
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
4029
        Cfloat ratio(lhs);
4030
        ratio /= Cfloat(rhs);
4031
        return ratio;
4032
}
4033

4034
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4035
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator+(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, int rhs) {
4036
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
4037
        Cfloat sum(lhs);
4038
        sum += Cfloat(rhs);
4039
        return sum;
4040
}
4041
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4042
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator-(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, int rhs) {
4✔
4043
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
4044
        Cfloat diff(lhs);
4✔
4045
        diff -= rhs;
4✔
4046
        return diff;
4✔
4047
}
4048
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4049
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator*(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, int rhs) {
4050
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
4051
        Cfloat mul(lhs);
4052
        mul *= Cfloat(rhs);
4053
        return mul;
4054
}
4055
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4056
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator/(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, int rhs) {
4057
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
4058
        Cfloat ratio(lhs);
4059
        ratio /= Cfloat(rhs);
4060
        return ratio;
4061
}
4062

4063
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4064
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator+(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, unsigned int rhs) {
4065
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
4066
        Cfloat sum(lhs);
4067
        sum += Cfloat(rhs);
4068
        return sum;
4069
}
4070
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4071
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator-(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, unsigned int rhs) {
4072
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
4073
        Cfloat diff(lhs);
4074
        diff -= rhs;
4075
        return diff;
4076
}
4077
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4078
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator*(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, unsigned int rhs) {
4079
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
4080
        Cfloat mul(lhs);
4081
        mul *= Cfloat(rhs);
4082
        return mul;
4083
}
4084
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4085
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator/(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, unsigned int rhs) {
4086
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
4087
        Cfloat ratio(lhs);
4088
        ratio /= Cfloat(rhs);
4089
        return ratio;
4090
}
4091

4092
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4093
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator+(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long long rhs) {
4094
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
4095
        Cfloat sum(lhs);
4096
        sum += Cfloat(rhs);
4097
        return sum;
4098
}
4099
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4100
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator-(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long long rhs) {
4101
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
4102
        Cfloat diff(lhs);
4103
        diff -= rhs;
4104
        return diff;
4105
}
4106
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4107
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator*(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long long rhs) {
4108
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
4109
        Cfloat mul(lhs);
4110
        mul *= Cfloat(rhs);
4111
        return mul;
4112
}
4113
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4114
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator/(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long long rhs) {
4115
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
4116
        Cfloat ratio(lhs);
4117
        ratio /= Cfloat(rhs);
4118
        return ratio;
4119
}
4120

4121
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4122
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator+(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, unsigned long long rhs) {
4123
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
4124
        Cfloat sum(lhs);
4125
        sum += Cfloat(rhs);
4126
        return sum;
4127
}
4128
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4129
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator-(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, unsigned long long rhs) {
4130
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
4131
        Cfloat diff(lhs);
4132
        diff -= rhs;
4133
        return diff;
4134
}
4135
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4136
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator*(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, unsigned long long rhs) {
4137
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
4138
        Cfloat mul(lhs);
4139
        mul *= Cfloat(rhs);
4140
        return mul;
4141
}
4142
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4143
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator/(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, unsigned long long rhs) {
4144
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
4145
        Cfloat ratio(lhs);
4146
        ratio /= Cfloat(rhs);
4147
        return ratio;
4148
}
4149

4150
///////////////////////////////////////////////////////////////////////
4151
///   binary logic literal comparisons
4152

4153
// cfloat - literal float logic operators
4154
// Promote rhs into the cfloat domain rather than narrowing lhs to native float;
4155
// otherwise cfloat<80, 11>(1.0 + 1ulp) == 1.0f reports true because float(lhs)
4156
// rounds the wider value into single precision. Comparisons must happen in the
4157
// shared cfloat domain to preserve precision.
4158
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4159
constexpr inline bool operator==(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, float rhs) {
1✔
4160
        return operator==(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
1✔
4161
}
4162
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4163
constexpr inline bool operator!=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, float rhs) {
1✔
4164
        return operator!=(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
1✔
4165
}
4166
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4167
constexpr inline bool operator< (const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, float rhs) {
2✔
4168
        return operator< (lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
2✔
4169
}
4170
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4171
constexpr inline bool operator> (const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, float rhs) {
1✔
4172
        return operator> (lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
1✔
4173
}
4174
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4175
constexpr inline bool operator<=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, float rhs) {
1✔
4176
        return operator<=(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
1✔
4177
}
4178
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4179
constexpr inline bool operator>=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, float rhs) {
1✔
4180
        return operator>=(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
1✔
4181
}
4182
// cfloat - literal double logic operators (same promotion strategy)
4183
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4184
constexpr inline bool operator==(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, double rhs) {
1✔
4185
        return operator==(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
1✔
4186
}
4187
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4188
constexpr inline bool operator!=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, double rhs) {
1✔
4189
        return operator!=(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
1✔
4190
}
4191
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4192
constexpr inline bool operator< (const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, double rhs) {
336✔
4193
        return operator< (lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
336✔
4194
}
4195
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4196
constexpr inline bool operator> (const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, double rhs) {
892✔
4197
        return operator> (lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
892✔
4198
}
4199
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4200
constexpr inline bool operator<=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, double rhs) {
1✔
4201
        return operator<=(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
1✔
4202
}
4203
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4204
constexpr inline bool operator>=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, double rhs) {
1✔
4205
        return operator>=(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
1✔
4206
}
4207

4208
#if LONG_DOUBLE_SUPPORT
4209
// cfloat - literal long double logic operators (same promotion strategy)
4210
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4211
constexpr inline bool operator==(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long double rhs) {
1✔
4212
        return operator==(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
1✔
4213
}
4214
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4215
constexpr inline bool operator!=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long double rhs) {
1✔
4216
        return operator!=(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
1✔
4217
}
4218
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4219
constexpr inline bool operator< (const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long double rhs) {
1✔
4220
        return operator< (lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
1✔
4221
}
4222
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4223
constexpr inline bool operator> (const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long double rhs) {
1✔
4224
        return operator> (lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
1✔
4225
}
4226
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4227
constexpr inline bool operator<=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long double rhs) {
1✔
4228
        return operator<=(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
1✔
4229
}
4230
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4231
constexpr inline bool operator>=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long double rhs) {
1✔
4232
        return operator>=(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
1✔
4233
}
4234
#endif
4235

4236
// cfloat - literal int logic operators
4237
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4238
constexpr inline bool operator==(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, int rhs) {
11✔
4239
        return operator==(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
11✔
4240
}
4241
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4242
constexpr inline bool operator!=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, int rhs) {
1✔
4243
        return operator!=(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
1✔
4244
}
4245
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4246
constexpr inline bool operator< (const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, int rhs) {
240✔
4247
        return operator<(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
240✔
4248
}
4249
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4250
constexpr inline bool operator> (const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, int rhs) {
666✔
4251
        return operator<(cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs), lhs);
666✔
4252
}
4253
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4254
constexpr inline bool operator<=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, int rhs) {
1✔
4255
        return operator<(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs)) || operator==(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
1✔
4256
}
4257
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4258
constexpr inline bool operator>=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, int rhs) {
1✔
4259
        return !operator<(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
1✔
4260
}
4261

4262
// cfloat - long long logic operators
4263
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4264
constexpr inline bool operator==(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long long rhs) {
4265
        return operator==(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
4266
}
4267
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4268
constexpr inline bool operator!=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long long rhs) {
4269
        return operator!=(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
4270
}
4271
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4272
constexpr inline bool operator< (const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long long rhs) {
4273
        return operator<(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
4274
}
4275
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4276
constexpr inline bool operator> (const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long long rhs) {
4277
        return operator<(cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs), lhs);
4278
}
4279
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4280
constexpr inline bool operator<=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long long rhs) {
4281
        return operator<(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs)) || operator==(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
4282
}
4283
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4284
constexpr inline bool operator>=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long long rhs) {
4285
        return !operator<(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
4286
}
4287

4288
// standard library functions for floating point
4289

4290
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4291
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> frexp(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& x, int* exp) {
765✔
4292
        *exp = x.scale();
765✔
4293
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> fraction(x);
765✔
4294
        fraction.setexponent(0);
765✔
4295
        return fraction;
765✔
4296
}
4297

4298
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4299
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> ldexp(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& x, int exp) {
765✔
4300
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> result(x);
765✔
4301
        int xexp = x.scale();
765✔
4302
        result.setexponent(xexp + exp);  // TODO: this does not work for subnormals
765✔
4303
        return result;
765✔
4304
}
4305

4306
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4307
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> 
4308
fma(cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> x,
3✔
4309
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> y,
4310
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> z) {
4311
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> fused{ 0 };
3✔
4312
        constexpr unsigned FBITS = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>::fbits;
3✔
4313
        constexpr unsigned EXTRA_FBITS = FBITS+2;
3✔
4314
        constexpr unsigned EXTENDED_PRECISION = nbits + EXTRA_FBITS;
3✔
4315
        // the C++ fma spec indicates that the x*y+z is evaluated in 'infinite' precision
4316
        // with only a single rounding event. The minimum finite precision that would behave like this
4317
        // is the precision where the product x*y does not need to be rounded, which will
4318
        // need at least 2*(fbits+1) mantissa bits to capture all bits that can be
4319
        // generated by the product.
4320
        cfloat<EXTENDED_PRECISION, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> preciseX(x), preciseY(y), preciseZ(z);
3✔
4321
//        ReportValue(preciseX, "extended precision x");
4322
//        ReportValue(preciseY, "extended precision y");
4323
//        ReportValue(preciseZ, "extended precision z");
4324
        cfloat<EXTENDED_PRECISION, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> product = preciseX * preciseY;
3✔
4325
//        ReportValue(product, "extended precision p");
4326
        fused = product + preciseZ;
3✔
4327
        return fused;
3✔
4328
}
4329

4330
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4331
cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>&
4332
minpos(cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& c) {
4333
        return c.minpos();
4334
}
4335
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4336
cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>&
4337
maxpos(cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& c) {
4338
        return c.maxpos();
4339
}
4340
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4341
cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>&
4342
minneg(cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& c) {
4343
        return c.minneg();
4344
}
4345
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4346
cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>&
4347
maxneg(cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& c) {
4348
        return c.maxneg();
4349
}
4350

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