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

stillwater-sc / universal / 26074879672

19 May 2026 03:44AM UTC coverage: 84.213% (-0.01%) from 84.225%
26074879672

push

github

web-flow
Merge pull request #871 from stillwater-sc/fix/dfloat-parse-and-print

fix(dfloat): parsing and printing embellishment

28 of 31 new or added lines in 2 files covered. (90.32%)

13 existing lines in 3 files now uncovered.

46931 of 55729 relevant lines covered (84.21%)

6398778.3 hits per line

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

93.63
/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 <cctype>
25
#include <limits>
26
#include <regex>
27
#include <sstream>
28
#include <string_view>
29
#include <type_traits>
30
#include <universal/native/ieee754.hpp>
31
#include <universal/native/subnormal.hpp>
32
#include <universal/utility/find_msb.hpp>
33
#include <universal/utility/decimal_to_binary.hpp>
34
#include <universal/number/shared/nan_encoding.hpp>
35
#include <universal/number/shared/infinite_encoding.hpp>
36
#include <universal/number/shared/specific_value_encoding.hpp>
37
// arithmetic tracing options
38
#include <universal/number/algorithm/trace_constants.hpp>
39
// cfloat exception structure
40
#include <universal/number/cfloat/exceptions.hpp>
41
// composition types used by cfloat
42
#include <universal/internal/blockbinary/blockbinary.hpp>
43
#include <universal/internal/blocktriple/blocktriple.hpp>
44
#include <universal/number/support/decimal.hpp>
45

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

50
#ifndef TRACE_CONVERSION
51
#define TRACE_CONVERSION 0
52
#endif
53

54
namespace sw { namespace universal {
55

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

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

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

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

200
                // our value needs to go through rounding to be correctly interpreted
201
                // 
202
                // tgt.clear();  // no need as all bits are going to be set by the code below
203

204
                // exponent construction
205
                int adjustment{ 0 };
74,342,730✔
206
                // construct exponent
207
                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,730✔
208
//                        std::cout << "exponent         " << to_binary(biasedExponent) << '\n';        
209
                if constexpr (hasSubnormals) {
210
                        //if (exponent >= cfloatType::MIN_EXP_SUBNORMAL && exponent < cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>::MIN_EXP_NORMAL) {
211
                        if (exponent < cfloatType::MIN_EXP_NORMAL) {
52,232,184✔
212
                                // the value is in the subnormal range of the cfloat
213
                                biasedExponent = 0;
35,478,803✔
214
                                // -exponent because we are right shifting and exponent in this range is negative
215
                                adjustment = -(exponent + subnormal_reciprocal_shift[es]);
35,478,803✔
216
                                // this is the right shift adjustment required for subnormal representation due 
217
                                // to the scale of the input number, i.e. the exponent of 2^-adjustment
218
                        }
219
                        else {
220
                                // the value is in the normal range of the cfloat
221
                                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,381✔
222
                        }
223
                }
224
                else {
225
                        if (exponent < cfloatType::MIN_EXP_NORMAL) biasedExponent = 1ull; // fixup biasedExponent if we are in the subnormal region
22,110,546✔
226
                }
227

228

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

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

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

294
                        // apply rounding (matches the bfbits < 65 path above)
295
                        if (roundup) fracbits.increment();
531,884✔
296

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

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

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

355

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

381
        static constexpr unsigned nbits = _nbits;
382
        static constexpr unsigned es = _es;
383
        static constexpr unsigned fbits  = nbits - 1u - es;    // number of fraction bits excluding the hidden bit
384
        static constexpr unsigned fhbits = nbits - es;           // number of fraction bits including the hidden bit
385

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

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

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

414
        static constexpr bool     hasSubnormals   = _hasSubnormals;
415
        static constexpr bool     hasMaxExpValues = _hasMaxExpValues;
416
        static constexpr bool     isSaturating    = _isSaturating;
417
        typedef bt BlockType;
418

419
        // constructors
420
        cfloat() = default;
421
        cfloat(const cfloat&) = default;
422
        cfloat& operator=(const cfloat&) = default;
423

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

447
                                bool srcSign = rhs.sign();
4,968✔
448
                                int srcScale = rhs.scale();
4,968✔
449

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

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

588
        constexpr cfloat(signed char iv)                    noexcept : _block{} { *this = iv; }
589
        constexpr cfloat(short iv)                          noexcept : _block{} { *this = iv; }
590
        constexpr cfloat(int iv)                            noexcept : _block{} { *this = iv; }
7,846✔
591
        constexpr cfloat(long iv)                           noexcept : _block{} { *this = iv; }
592
        constexpr cfloat(long long iv)                      noexcept : _block{} { *this = iv; }
1✔
593
        constexpr cfloat(char iv)                           noexcept : _block{} { *this = iv; }
594
        constexpr cfloat(unsigned short iv)                 noexcept : _block{} { *this = iv; }
595
        constexpr cfloat(unsigned int iv)                   noexcept : _block{} { *this = iv; }
596
        constexpr cfloat(unsigned long iv)                  noexcept : _block{} { *this = iv; }
100✔
597
        constexpr cfloat(unsigned long long iv)             noexcept : _block{} { *this = iv; }
598
        CONSTEXPRESSION cfloat(float iv)                    noexcept : _block{} { *this = iv; }
340,809✔
599
        CONSTEXPRESSION cfloat(double iv)                   noexcept : _block{} { *this = iv; }
475,838✔
600

601
        // assignment operators
602
        constexpr cfloat& operator=(signed char rhs)        noexcept { return convert_signed_integer(rhs); }
603
        constexpr cfloat& operator=(short rhs)              noexcept { return convert_signed_integer(rhs); }
604
        constexpr cfloat& operator=(int rhs)                noexcept { return convert_signed_integer(rhs); }
13,328✔
605
        constexpr cfloat& operator=(long rhs)               noexcept { return convert_signed_integer(rhs); }
606
        constexpr cfloat& operator=(long long rhs)          noexcept { return convert_signed_integer(rhs); }
1✔
607

608
        constexpr cfloat& operator=(char rhs)               noexcept { return convert_unsigned_integer(rhs); }
609
        constexpr cfloat& operator=(unsigned short rhs)     noexcept { return convert_unsigned_integer(rhs); }
610
        constexpr cfloat& operator=(unsigned int rhs)       noexcept { return convert_unsigned_integer(rhs); }
2,685✔
611
        constexpr cfloat& operator=(unsigned long rhs)      noexcept { return convert_unsigned_integer(rhs); }
110✔
612
        constexpr cfloat& operator=(unsigned long long rhs) noexcept { return convert_unsigned_integer(rhs); }
613

614
        BIT_CAST_CONSTEXPR cfloat& operator=(float rhs)     noexcept { return convert_ieee754(rhs); }
32,488,684✔
615
        BIT_CAST_CONSTEXPR cfloat& operator=(double rhs)    noexcept { return convert_ieee754(rhs); }
105,789,820✔
616

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

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

631
        // arithmetic operators
632
        // prefix operator
633
        constexpr inline cfloat operator-() const {
8,724,118✔
634
                cfloat tmp(*this);
8,724,118✔
635
                tmp._block[MSU] ^= SIGN_BIT_MASK;
8,724,118✔
636
                return tmp;
8,724,118✔
637
        }
638

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

682
                if (iszero()) {
18,601,438✔
683
                        *this = rhs;
214,866✔
684
                        return *this;
214,866✔
685
                }
686
                if (rhs.iszero()) return *this;
18,386,572✔
687

688
                // arithmetic operation
689
                blocktriple<fbits, BlockTripleOperator::ADD, bt> a, b, sum;
18,217,711✔
690

691
                // transform the inputs into (sign,scale,significant) 
692
                normalizeAddition(a); 
18,217,711✔
693
                rhs.normalizeAddition(b);
18,217,711✔
694
                sum.add(a, b);
18,217,711✔
695

696
                convert(sum, *this);
18,217,711✔
697

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

756
                if (iszero() || rhs.iszero()) {                        
3,914,277✔
757
                        setzero();
1,816,893✔
758
                        setsign(resultSign); // deal with negative 0
1,816,893✔
759
                        return *this;
1,816,893✔
760
                }
761

762
                // arithmetic operation
763
                blocktriple<fbits, BlockTripleOperator::MUL, bt> a, b, product;
2,097,384✔
764

765
                // transform the inputs into (sign,scale,significant) 
766
                // triples of the correct width
767
                normalizeMultiplication(a);
2,097,384✔
768
                rhs.normalizeMultiplication(b);
2,097,384✔
769
                product.mul(a, b);
2,097,384✔
770
                convert(product, *this);
2,097,384✔
771

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

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

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

839
                if (iszero()) {
4,476,191✔
840
                        setzero();
139,452✔
841
                        setsign(resultSign); // deal with negative 0
139,452✔
842
                        return *this;
139,452✔
843
                }
844

845
                // arithmetic operation
846
                using BlockTriple = blocktriple<fbits, BlockTripleOperator::DIV, bt>;
847
                BlockTriple a, b, quotient;
4,336,739✔
848

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

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

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

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

1496

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

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

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

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

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

1766
                if constexpr (0 == nrBlocks) {
1767
                        return false;
1768
                }
1769
                else if constexpr (1 == nrBlocks) {
1770
                }
1771
                else if constexpr (2 == nrBlocks) {
1772
                        isNaN = (_block[0] == BLOCK_MASK);
224,000,014✔
1773
                }
1774
                else if constexpr (3 == nrBlocks) {
1775
                        isNaN = (_block[0] == BLOCK_MASK) && (_block[1] == BLOCK_MASK);
201,348,401✔
1776
                }
1777
                else if constexpr (4 == nrBlocks) {
1778
                        isNaN = (_block[0] == BLOCK_MASK) && (_block[1] == BLOCK_MASK) && (_block[2] == BLOCK_MASK);
226,746✔
1779
                }
1780
                else {
1781
                        for (unsigned i = 0; i < nrBlocks - 1; ++i) {
269,996✔
1782
                                if (_block[i] != BLOCK_MASK) {
269,956✔
1783
                                        isNaN = false;
269,733✔
1784
                                        break;
269,733✔
1785
                                }
1786
                        }
1787
                }
1788
                isNegNaN = isNaN && ((_block[MSU] & MSU_MASK) == MSU_MASK);
600,433,020✔
1789
                isPosNaN = isNaN && (_block[MSU] & MSU_MASK) == (MSU_MASK ^ SIGN_BIT_MASK);
600,433,020✔
1790

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

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

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

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

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

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

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

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

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

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

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

2402
protected:
2403
        // HELPER methods
2404

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

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

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

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

2466
                constexpr uint32_t sizeInBits = 8 * sizeof(Ty);
2,772✔
2467
                uint32_t shift = sizeInBits - exponent - 1;
2,772✔
2468
                raw <<= shift;
2,772✔
2469
                raw = round<sizeInBits, uint64_t>(raw, exponent);
2,772✔
2470

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

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

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

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

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

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

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

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

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

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

2774
                        /////////////////  
2775
                        /// end of special case processing, move on to value sampling and rounding
2776

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

2787
                        // do the following scenarios have different rounding bits?
2788
                        // input is normal, cfloat is normal           <-- rounding can happen with native ieee-754 bits
2789
                        // input is normal, cfloat is subnormal
2790
                        // input is subnormal, cfloat is normal
2791
                        // input is subnormal, cfloat is subnormal
2792

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

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

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

2850

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

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

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

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

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

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

3144
protected:
3145

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

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

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

3291
                bt mask = ALL_ONES;
3292
                mask >>= (bitsInBlock - rightShift); // this is a mask for the lower bits in the block that need to move to the lower word
3293
                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
3294
                        _block[i] >>= rightShift;
3295
                        // mix in the bits from the left
3296
                        bt bits = static_cast<bt>(mask & _block[i + 1]); // & operator returns an int
3297
                        _block[i] |= (bits << (bitsInBlock - rightShift));
3298
                }
3299
                _block[MSU] >>= rightShift;
3300

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

3317
                // enforce precondition for fast comparison by properly nulling bits that are outside of nbits
3318
                _block[MSU] &= MSU_MASK;
3319
        }
3320

3321
        // calculate the integer power 2 ^ b using exponentiation by squaring
3322
        constexpr double ipow(int exponent) const {
525,624✔
3323
                bool negative = (exponent < 0);
525,624✔
3324
                exponent = negative ? -exponent : exponent;
525,624✔
3325
                double result(1.0);
525,624✔
3326
                double base = 2.0;
525,624✔
3327
                for (;;) {
3328
                        if (exponent % 2) result *= base;
3,850,781✔
3329
                        exponent >>= 1;
3,850,781✔
3330
                        if (exponent == 0) break;
3,850,781✔
3331
                        base *= base;
3,325,157✔
3332
                }
3333
                return (negative ? (1.0 / result) : result);
525,624✔
3334
        }
3335

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

3399
private:
3400
        bt _block[nrBlocks];
3401

3402
        //////////////////////////////////////////////////////////////////////////////
3403
        // friend functions
3404

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

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
        template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3420
        friend constexpr bool operator<=(const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& lhs, const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& rhs);
3421
        template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3422
        friend constexpr bool operator>=(const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& lhs, const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& rhs);
3423
};
3424

3425
///////////////////////////// IOSTREAM operators ///////////////////////////////////////////////
3426

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

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

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

3498
        return str.str();
3499
}
3500

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

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

3522
        multiplier.powerOf2(lsbScale);
3523

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

3538
//////////////////////////////////////////////////////////////////////////////////////////////
3539
/// stream operators
3540

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

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

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

3584
// parse a cfloat from a string in either cfloat hex format (nbits.esxHEXVALUEc)
3585
// or a decimal floating-point representation
3586
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3587
bool parse(const std::string& txt, cfloat<nbits,es,bt,hasSubnormals,hasMaxExpValues,isSaturating>& v) {
75✔
3588
        // check if the txt is of the native cfloat form: nbits.esX[0x]hexvaluec
3589
        std::regex cfloat_regex(R"(^[0-9]+\.[0-9]+[xX](0[xX])?[0-9A-Fa-f]+c?$)");
75✔
3590
        if (std::regex_match(txt, cfloat_regex)) {
75✔
3591
                // found a cfloat representation: parse nbits.esxHEXVALUEc
3592
                std::string nbitsStr, esStr, bitStr;
6✔
3593
                auto it = txt.begin();
6✔
3594
                for (; it != txt.end(); ++it) {
18✔
3595
                        if (*it == '.') break;
18✔
3596
                        nbitsStr.append(1, *it);
12✔
3597
                }
3598
                for (++it; it != txt.end(); ++it) {
12✔
3599
                        if (*it == 'x' || *it == 'X') break;
12✔
3600
                        esStr.append(1, *it);
6✔
3601
                }
3602
                for (++it; it != txt.end(); ++it) {
42✔
3603
                        if (*it == 'c') break;
42✔
3604
                        bitStr.append(1, *it);
36✔
3605
                }
3606
                unsigned nbits_in = 0;
6✔
3607
                unsigned es_in = 0;
6✔
3608
                {
3609
                        std::istringstream ss(nbitsStr);
6✔
3610
                        ss >> nbits_in;
6✔
3611
                        if (ss.fail()) return false;
6✔
3612
                }
6✔
3613
                {
3614
                        std::istringstream ss(esStr);
6✔
3615
                        ss >> es_in;
6✔
3616
                        if (ss.fail()) return false;
6✔
3617
                }
6✔
3618
                // native cfloat form must match target configuration
3619
                if (nbits_in != nbits || es_in != es) return false;
6✔
3620
                uint64_t raw = 0;
4✔
3621
                std::istringstream ss(bitStr);
4✔
3622
                ss >> std::hex >> raw;
4✔
3623
                if (ss.fail()) return false;
4✔
3624
                ss >> std::ws;
4✔
3625
                if (!ss.eof()) return false;
4✔
3626
                v.setbits(raw);
4✔
3627
                return true;
4✔
3628
        }
6✔
3629
        else {
3630
                // Decimal floating-point representation.
3631
                // Route through the high-precision decimal_to_binary utility so that
3632
                // wide cfloat configurations (nbits > 64, including IEEE quad and
3633
                // posit-killer formats) don't lose precision through an intermediate
3634
                // double. The utility delivers a normalized mantissa with
3635
                // target_mantissa_bits bits plus guard/sticky; we package that as a
3636
                // blocktriple and hand it to convert(blocktriple, cfloat), which
3637
                // handles all IEEE-754 edge cases (subnormals, saturation, etc.).
3638
                // Special-value tokens (nan / inf in any common spelling) are
3639
                // recognised directly so callers don't depend on locale-sensitive
3640
                // std::istringstream parsing of those literals.
3641
                using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
3642
                {
3643
                        std::string t; t.reserve(txt.size());
69✔
3644
                        for (char c : txt) t.push_back(static_cast<char>(std::tolower(static_cast<unsigned char>(c))));
509✔
3645
                        bool negative = !t.empty() && t.front() == '-';
69✔
3646
                        std::string body = t;
69✔
3647
                        if (!body.empty() && (body.front() == '+' || body.front() == '-')) body.erase(0, 1);
69✔
3648
                        if (body == "nan") {
69✔
3649
                                v.setnan(NAN_TYPE_QUIET);
4✔
3650
                                return true;
4✔
3651
                        }
3652
                        if (body == "inf" || body == "infinity") {
65✔
3653
                                v.setinf(negative);
12✔
3654
                                return true;
12✔
3655
                        }
3656
                }
85✔
3657
                // We pack the d2b result into a blocktriple<fbits, MUL, bt>. The MUL
3658
                // layout has bfbits = 2*fbits + 2 and radix = 2*fbits, which gives us
3659
                // exactly fbits of headroom below the cfloat fraction for guard/
3660
                // sticky -- enough room for cfloat's convert() to make a correct
3661
                // round-to-nearest-even decision. (The REP layout has no such
3662
                // headroom: its fraction is exactly cfloat::fbits wide.)
3663
                using BT = blocktriple<Cfloat::fbits, BlockTripleOperator::MUL, bt>;
3664
                constexpr unsigned radix_pos          = static_cast<unsigned>(BT::radix);
53✔
3665
                constexpr unsigned target_mantissa_bits = radix_pos + 1u;
53✔
3666
                auto d = ::sw::universal::decimal_to_binary::convert(
53✔
3667
                        std::string_view{txt}, target_mantissa_bits);
3668
                if (!d.valid) return false;
53✔
3669
                if (d.is_zero) {
49✔
3670
                        v.setzero();
7✔
3671
                        v.setsign(d.negative);
7✔
3672
                        return true;
7✔
3673
                }
3674
                BT bt_val;
42✔
3675
                bt_val.setnormal();
42✔
3676
                bt_val.setsign(d.negative);
42✔
3677
                bt_val.setscale(static_cast<int>(d.binary_scale));
42✔
3678
                // d2b mantissa has its MSB at position radix_pos (the hidden bit);
3679
                // below it are radix_pos extra precision bits. Copy bit-for-bit
3680
                // into the blocktriple significand aligned to the same radix.
3681
                for (unsigned i = 0; i <= radix_pos; ++i) {
1,898✔
3682
                        if (d.mantissa.at(i)) bt_val.setbit(i, true);
1,856✔
3683
                }
3684
                // Fold d2b's residual guard/sticky into the lowest bit so cfloat's
3685
                // own rounding decision picks them up as sticky tail.
3686
                if (d.guard_bit || d.sticky_bit) bt_val.setbit(0, true);
42✔
3687
                convert(bt_val, v);
42✔
3688
                return true;
42✔
3689
        }
3690
}
75✔
3691

3692
// read an ASCII float or cfloat format: nbits.esxNN...NNc, for example: 16.5x7C00c
3693
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3694
inline std::istream& operator>>(std::istream& istr, cfloat<nbits,es,bt,hasSubnormals,hasMaxExpValues,isSaturating>& v) {
18✔
3695
        std::string txt;
18✔
3696
        istr >> txt;
18✔
3697
        if (!parse(txt, v)) {
18✔
3698
                std::cerr << "unable to parse -" << txt << "- into a cfloat value\n";
1✔
3699
                istr.setstate(std::ios::failbit);
1✔
3700
        }
3701
        return istr;
18✔
3702
}
18✔
3703

3704
// encoding helpers
3705

3706
// return the Unit in the Last Position
3707
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3708
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> ulp(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& a) {
49✔
3709
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> b(a);
49✔
3710
        return ++b - a;
86✔
3711
}
3712

3713
// transform cfloat to a binary representation
3714
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3715
inline std::string to_binary(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& number, bool nibbleMarker = false) {
1,455✔
3716
        std::stringstream s;
1,455✔
3717
        s << "0b";
1,455✔
3718
        unsigned index = nbits;
1,455✔
3719
        s << (number.at(--index) ? '1' : '0') << '.';
1,455✔
3720

3721
        for (int i = int(es) - 1; i >= 0; --i) {
9,871✔
3722
                s << (number.at(--index) ? '1' : '0');
8,416✔
3723
                if (i > 0 && (i % 4) == 0 && nibbleMarker) s << '\'';
8,416✔
3724
        }
3725

3726
        s << '.';
1,455✔
3727

3728
        constexpr int fbits = nbits - 1ull - es;
1,455✔
3729
        for (int i = fbits - 1; i >= 0; --i) {
29,481✔
3730
                s << (number.at(--index) ? '1' : '0');
28,026✔
3731
                if (i > 0 && (i % 4) == 0 && nibbleMarker) s << '\'';
28,026✔
3732
        }
3733

3734
        return s.str();
2,910✔
3735
}
1,455✔
3736

3737
// native semantic representation: radix-2, delegates to to_binary
3738
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3739
inline std::string to_native(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& number, bool nibbleMarker = false) {
3740
        return to_binary(number, nibbleMarker);
3741
}
3742

3743
// transform a cfloat into a triple representation
3744
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3745
inline std::string to_triple(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& number, bool nibbleMarker = true) {
3✔
3746
        std::stringstream s;
3✔
3747
        blocktriple<cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>::fbits, BlockTripleOperator::REP, bt> triple;
3✔
3748
        number.normalize(triple);
3✔
3749
        s << to_triple(triple, nibbleMarker);
3✔
3750
        return s.str();
6✔
3751
}
3✔
3752

3753
// Magnitude of a cfloat (equivalent to turning the sign bit off).
3754
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3755
cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>
3756
abs(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& v) {
16,939✔
3757
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> a(v);
16,939✔
3758
        a.setsign(false);
16,939✔
3759
        return a;
16,939✔
3760
}
3761

3762
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3763
cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>
3764
fabs(cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> v) {
40✔
3765
        return abs(v);
40✔
3766
}
3767

3768
////////////////////// debug helpers
3769

3770
// convenience method to gain access to the values of the constexpr variables that govern the cfloat behavior
3771
template<unsigned nbits, unsigned es, typename bt = uint8_t, bool hasSubnormals = false, bool hasMaxExpValues = false, bool isSaturating = false>
3772
void ReportCfloatClassParameters() {
3773
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> a;
3774
        a.constexprClassParameters();
3775
}
3776

3777
//////////////////////////////////////////////////////
3778
/// cfloat - cfloat binary logic operators
3779

3780
template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3781
constexpr inline bool operator==(const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& lhs, const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& rhs) {
127,146,834✔
3782
        if (lhs.isnan() || rhs.isnan()) return false;
127,146,834✔
3783
        if (lhs.iszero() && rhs.iszero()) return true; // +0 == -0 per IEEE-754 §5.11
125,491,749✔
3784
        for (unsigned i = 0; i < lhs.nrBlocks; ++i) {
385,823,644✔
3785
                if (lhs._block[i] != rhs._block[i]) {
263,585,379✔
3786
                        return false;
2,104,957✔
3787
                }
3788
        }
3789
        return true;
122,238,265✔
3790
}
3791
template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3792
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,391✔
3793
template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3794
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✔
3795
        if (lhs.isnan() || rhs.isnan()) return false;
5,942,967✔
3796
        // need this as arithmetic difference is defined as snan(indeterminate)
3797
        if (lhs.isinf(INF_TYPE_NEGATIVE) && rhs.isinf(INF_TYPE_NEGATIVE)) return false;
5,827,458✔
3798
        if (lhs.isinf(INF_TYPE_POSITIVE) && rhs.isinf(INF_TYPE_POSITIVE)) return false;
5,827,444✔
3799
        if constexpr (nsub) {
3800
                cfloat<nnbits, nes, nbt, nsub, nsup, nsat> diff = (lhs - rhs);
5,758,474✔
3801
                return (!diff.iszero() && diff.sign()) ? true : false;  // got to guard against -0
5,758,474✔
3802
        }
3803
        else {
3804
                if (lhs.iszero() && rhs.iszero()) return false;  // we need to 'collapse' all zero encodings
68,937✔
3805
                if (lhs.sign() && !rhs.sign()) return true;
68,929✔
3806
                if (!lhs.sign() && rhs.sign()) return false;
51,703✔
3807
                bool positive = lhs.ispos();
34,477✔
3808
                if (positive) {
34,477✔
3809
                        if (lhs.scale() < rhs.scale()) return true;
17,257✔
3810
                        if (lhs.scale() > rhs.scale()) return false;
12,786✔
3811
                }
3812
                else {
3813
                        if (lhs.scale() > rhs.scale()) return true;
17,220✔
3814
                        if (lhs.scale() < rhs.scale()) return false;
12,770✔
3815
                }
3816
                // sign and scale are the same
3817
                if (lhs.scale() == rhs.scale()) {
16,642✔
3818
                        // compare fractions: we do not have subnormals, so we can ignore the hidden bit
3819
                        blockbinary<nnbits - 1ull - nes, nbt> l, r;
3820
                        lhs.fraction(l);
16,642✔
3821
                        rhs.fraction(r);
16,642✔
3822
                        blockbinary<nnbits - nes, nbt> ll, rr; // fbits + 1 so we can 0 extend to honor 2's complement encoding of blockbinary
3823
                        ll.assignWithoutSignExtend(l);
16,642✔
3824
                        rr.assignWithoutSignExtend(r);
16,642✔
3825
                        return (positive ? (ll < rr) : (ll > rr));
16,642✔
3826
                }
3827
                return false;
×
3828
        }
3829
}
3830
template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3831
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✔
3832
        if (lhs.isnan() || rhs.isnan()) return false;
3,144,623✔
3833
        // need this as arithmetic difference is defined as snan(indeterminate)
3834
        if (lhs.isinf(INF_TYPE_NEGATIVE) && rhs.isinf(INF_TYPE_NEGATIVE)) return false;
3,136,509✔
3835
        if (lhs.isinf(INF_TYPE_POSITIVE) && rhs.isinf(INF_TYPE_POSITIVE)) return false;
3,136,495✔
3836
        return  operator< (rhs, lhs); 
3,136,479✔
3837
}
3838
template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3839
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✔
3840
        if (lhs.isnan() || rhs.isnan()) return false;
1,747,159✔
3841
        return !operator> (lhs, rhs); 
1,739,059✔
3842
}
3843
template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3844
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✔
3845
        if (lhs.isnan() || rhs.isnan()) return false;
1,530,053✔
3846
        return !operator< (lhs, rhs); 
1,521,947✔
3847
}
3848

3849
//////////////////////////////////////////////////////
3850
/// cfloat - cfloat binary arithmetic operators
3851

3852
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3853
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✔
3854
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> sum(lhs);
10,323,668✔
3855
        sum += rhs;
10,323,668✔
3856
        return sum;
10,323,668✔
3857
}
3858
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3859
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,368✔
3860
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> diff(lhs);
8,798,368✔
3861
        diff -= rhs;
8,798,368✔
3862
        return diff;
8,798,368✔
3863
}
3864
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3865
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✔
3866
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> mul(lhs);
4,170,183✔
3867
        mul *= rhs;
4,170,183✔
3868
        return mul;
4,170,183✔
3869
}
3870
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3871
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✔
3872
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> ratio(lhs);
5,022,193✔
3873
        ratio /= rhs;
5,022,193✔
3874
        return ratio;
5,022,192✔
3875
}
3876

3877
/// binary cfloat - literal arithmetic operators
3878

3879
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3880
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator+(float lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3881
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> sum(lhs);
3882
        sum += rhs;
3883
        return sum;
3884
}
3885
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3886
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator-(float lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3887
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> diff(lhs);
3888
        diff -= rhs;
3889
        return diff;
3890
}
3891
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3892
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator*(float lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3893
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> mul(lhs);
3894
        mul *= rhs;
3895
        return mul;
3896
}
3897
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3898
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator/(float lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
2✔
3899
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> ratio(lhs);
2✔
3900
        ratio /= rhs;
2✔
3901
        return ratio;
2✔
3902
}
3903

3904
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3905
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator+(double lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
4✔
3906
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> sum(lhs);
4✔
3907
        sum += rhs;
4✔
3908
        return sum;
4✔
3909
}
3910
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3911
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator-(double lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3912
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> diff(lhs);
3913
        diff -= rhs;
3914
        return diff;
3915
}
3916
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3917
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator*(double lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
16✔
3918
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> mul(lhs);
16✔
3919
        mul *= rhs;
16✔
3920
        return mul;
16✔
3921
}
3922
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3923
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator/(double lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3924
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> ratio(lhs);
3925
        ratio /= rhs;
3926
        return ratio;
3927
}
3928

3929
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3930
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator+(int lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
×
3931
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> sum(lhs);
×
3932
        sum += rhs;
×
3933
        return sum;
×
3934
}
3935
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3936
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator-(int lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3937
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> diff(lhs);
3938
        diff -= rhs;
3939
        return diff;
3940
}
3941
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3942
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator*(int lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
38✔
3943
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> mul(lhs);
38✔
3944
        mul *= rhs;
38✔
3945
        return mul;
38✔
3946
}
3947
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3948
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator/(int lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
374✔
3949
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> ratio(lhs);
374✔
3950
        ratio /= rhs;
374✔
3951
        return ratio;
374✔
3952
}
3953

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

3979
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3980
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator+(long long lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3981
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> sum(lhs);
3982
        sum += rhs;
3983
        return sum;
3984
}
3985
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3986
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator-(long long lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3987
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> diff(lhs);
3988
        diff -= rhs;
3989
        return diff;
3990
}
3991
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3992
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator*(long long lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3993
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> mul(lhs);
3994
        mul *= 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/(long long lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3999
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> ratio(lhs);
4000
        ratio /= rhs;
4001
        return ratio;
4002
}
4003

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

4029
///  binary cfloat - literal arithmetic operators
4030

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

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

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

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

4147
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4148
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator+(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long long rhs) {
4149
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
4150
        Cfloat sum(lhs);
4151
        sum += Cfloat(rhs);
4152
        return sum;
4153
}
4154
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4155
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator-(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long long rhs) {
4156
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
4157
        Cfloat diff(lhs);
4158
        diff -= rhs;
4159
        return diff;
4160
}
4161
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4162
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator*(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long long rhs) {
4163
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
4164
        Cfloat mul(lhs);
4165
        mul *= Cfloat(rhs);
4166
        return mul;
4167
}
4168
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4169
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator/(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long long rhs) {
4170
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
4171
        Cfloat ratio(lhs);
4172
        ratio /= Cfloat(rhs);
4173
        return ratio;
4174
}
4175

4176
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4177
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator+(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, unsigned long long rhs) {
4178
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
4179
        Cfloat sum(lhs);
4180
        sum += Cfloat(rhs);
4181
        return sum;
4182
}
4183
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4184
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator-(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, unsigned long long rhs) {
4185
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
4186
        Cfloat diff(lhs);
4187
        diff -= rhs;
4188
        return diff;
4189
}
4190
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4191
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator*(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, unsigned long long rhs) {
4192
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
4193
        Cfloat mul(lhs);
4194
        mul *= Cfloat(rhs);
4195
        return mul;
4196
}
4197
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4198
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator/(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, unsigned long long rhs) {
4199
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
4200
        Cfloat ratio(lhs);
4201
        ratio /= Cfloat(rhs);
4202
        return ratio;
4203
}
4204

4205
///////////////////////////////////////////////////////////////////////
4206
///   binary logic literal comparisons
4207

4208
// cfloat - literal float logic operators
4209
// Promote rhs into the cfloat domain rather than narrowing lhs to native float;
4210
// otherwise cfloat<80, 11>(1.0 + 1ulp) == 1.0f reports true because float(lhs)
4211
// rounds the wider value into single precision. Comparisons must happen in the
4212
// shared cfloat domain to preserve precision.
4213
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4214
constexpr inline bool operator==(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, float rhs) {
1✔
4215
        return operator==(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
1✔
4216
}
4217
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4218
constexpr inline bool operator!=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, float rhs) {
1✔
4219
        return operator!=(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
1✔
4220
}
4221
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4222
constexpr inline bool operator< (const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, float rhs) {
2✔
4223
        return operator< (lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
2✔
4224
}
4225
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4226
constexpr inline bool operator> (const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, float rhs) {
1✔
4227
        return operator> (lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
1✔
4228
}
4229
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4230
constexpr inline bool operator<=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, float rhs) {
1✔
4231
        return operator<=(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
1✔
4232
}
4233
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4234
constexpr inline bool operator>=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, float rhs) {
1✔
4235
        return operator>=(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
1✔
4236
}
4237
// cfloat - literal double logic operators (same promotion strategy)
4238
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4239
constexpr inline bool operator==(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, double rhs) {
1✔
4240
        return operator==(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
1✔
4241
}
4242
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4243
constexpr inline bool operator!=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, double rhs) {
1✔
4244
        return operator!=(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
1✔
4245
}
4246
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4247
constexpr inline bool operator< (const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, double rhs) {
336✔
4248
        return operator< (lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
336✔
4249
}
4250
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4251
constexpr inline bool operator> (const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, double rhs) {
892✔
4252
        return operator> (lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
892✔
4253
}
4254
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4255
constexpr inline bool operator<=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, double rhs) {
1✔
4256
        return operator<=(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
1✔
4257
}
4258
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4259
constexpr inline bool operator>=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, double rhs) {
1✔
4260
        return operator>=(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
1✔
4261
}
4262

4263
#if LONG_DOUBLE_SUPPORT
4264
// cfloat - literal long double logic operators (same promotion strategy)
4265
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4266
constexpr inline bool operator==(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long double rhs) {
1✔
4267
        return operator==(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
1✔
4268
}
4269
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4270
constexpr inline bool operator!=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long double rhs) {
1✔
4271
        return operator!=(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
1✔
4272
}
4273
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4274
constexpr inline bool operator< (const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long double rhs) {
1✔
4275
        return operator< (lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
1✔
4276
}
4277
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4278
constexpr inline bool operator> (const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long double rhs) {
1✔
4279
        return operator> (lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
1✔
4280
}
4281
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4282
constexpr inline bool operator<=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long double rhs) {
1✔
4283
        return operator<=(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
1✔
4284
}
4285
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4286
constexpr inline bool operator>=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long double rhs) {
1✔
4287
        return operator>=(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
1✔
4288
}
4289
#endif
4290

4291
// cfloat - literal int logic operators
4292
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4293
constexpr inline bool operator==(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, int rhs) {
11✔
4294
        return operator==(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
11✔
4295
}
4296
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4297
constexpr inline bool operator!=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, int rhs) {
1✔
4298
        return operator!=(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
1✔
4299
}
4300
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4301
constexpr inline bool operator< (const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, int rhs) {
240✔
4302
        return operator<(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
240✔
4303
}
4304
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4305
constexpr inline bool operator> (const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, int rhs) {
666✔
4306
        return operator<(cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs), lhs);
666✔
4307
}
4308
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4309
constexpr inline bool operator<=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, int rhs) {
1✔
4310
        return operator<(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs)) || operator==(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
1✔
4311
}
4312
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4313
constexpr inline bool operator>=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, int rhs) {
1✔
4314
        return !operator<(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
1✔
4315
}
4316

4317
// cfloat - long long logic operators
4318
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4319
constexpr inline bool operator==(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long long rhs) {
4320
        return operator==(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
4321
}
4322
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4323
constexpr inline bool operator!=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long long rhs) {
4324
        return operator!=(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
4325
}
4326
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4327
constexpr inline bool operator< (const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long long rhs) {
4328
        return operator<(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
4329
}
4330
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4331
constexpr inline bool operator> (const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long long rhs) {
4332
        return operator<(cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs), lhs);
4333
}
4334
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4335
constexpr inline bool operator<=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long long rhs) {
4336
        return operator<(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs)) || operator==(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
4337
}
4338
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4339
constexpr inline bool operator>=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long long rhs) {
4340
        return !operator<(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
4341
}
4342

4343
// standard library functions for floating point
4344

4345
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4346
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> frexp(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& x, int* exp) {
765✔
4347
        *exp = x.scale();
765✔
4348
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> fraction(x);
765✔
4349
        fraction.setexponent(0);
765✔
4350
        return fraction;
765✔
4351
}
4352

4353
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4354
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> ldexp(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& x, int exp) {
765✔
4355
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> result(x);
765✔
4356
        int xexp = x.scale();
765✔
4357
        result.setexponent(xexp + exp);  // TODO: this does not work for subnormals
765✔
4358
        return result;
765✔
4359
}
4360

4361
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4362
constexpr inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> 
4363
fma(cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> x,
3✔
4364
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> y,
4365
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> z) {
4366
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> fused{ 0 };
3✔
4367
        constexpr unsigned FBITS = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>::fbits;
3✔
4368
        constexpr unsigned EXTRA_FBITS = FBITS+2;
3✔
4369
        constexpr unsigned EXTENDED_PRECISION = nbits + EXTRA_FBITS;
3✔
4370
        // the C++ fma spec indicates that the x*y+z is evaluated in 'infinite' precision
4371
        // with only a single rounding event. The minimum finite precision that would behave like this
4372
        // is the precision where the product x*y does not need to be rounded, which will
4373
        // need at least 2*(fbits+1) mantissa bits to capture all bits that can be
4374
        // generated by the product.
4375
        cfloat<EXTENDED_PRECISION, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> preciseX(x), preciseY(y), preciseZ(z);
3✔
4376
//        ReportValue(preciseX, "extended precision x");
4377
//        ReportValue(preciseY, "extended precision y");
4378
//        ReportValue(preciseZ, "extended precision z");
4379
        cfloat<EXTENDED_PRECISION, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> product = preciseX * preciseY;
3✔
4380
//        ReportValue(product, "extended precision p");
4381
        fused = product + preciseZ;
3✔
4382
        return fused;
3✔
4383
}
4384

4385
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4386
cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>&
4387
minpos(cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& c) {
4388
        return c.minpos();
4389
}
4390
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4391
cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>&
4392
maxpos(cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& c) {
4393
        return c.maxpos();
4394
}
4395
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4396
cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>&
4397
minneg(cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& c) {
4398
        return c.minneg();
4399
}
4400
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4401
cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>&
4402
maxneg(cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& c) {
4403
        return c.maxneg();
4404
}
4405

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