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

stillwater-sc / universal / 23597700668

26 Mar 2026 01:46PM UTC coverage: 84.367% (-0.02%) from 84.387%
23597700668

push

github

web-flow
fix(cfloat): add rounding and overflow handling to wide-significand convert path (#608)

* fix(cfloat): handle ALL_ONES_ES round-overflow explicitly in wide path

When rounding carries into the hidden-bit position and the biased
exponent is already ALL_ONES_ES, the wide-significand path left the
result as a no-op, relying on the isnan() remap below. This fails
for two cases:

1. hasMaxExpValues == true: all-ones exponent with zero fraction is a
   valid finite encoding, not NaN, so the remap never fires — producing
   an incorrect mid-range value instead of maxpos/maxneg or inf.

2. isSaturating == true && hasMaxExpValues == false: all-ones exponent
   with zero fraction is infinity, not NaN, so the saturating remap to
   maxpos/maxneg never fires — producing inf instead of maxpos/maxneg.

Fix: explicitly saturate to maxpos/maxneg or set inf and return early,
matching the early overflow handling already in convert().

* test(cfloat): add from_blocktriple regression tests for maxexpvalue conversion

Add missing from_blocktriple.cpp test files to both saturating/maxexpvalue
and nonsaturating/maxexpvalue conversion directories. These exercise the
convert() code path fixed in PR #608, where rounding overflow into the
max-exponent binade was not handled correctly when hasMaxExpValues=true.

- saturating: verifies overflow saturates to maxpos/maxneg (ftt config)
- nonsaturating: verifies overflow maps to +/-inf (ftf config)

Covers es=2 through es=8 across small-to-medium cfloat sizes. The es=8
cases are included unconditionally since the hasMaxExpValues path is
what the fix specifically addressed.

* fix(test): use += to accumulate nrOfFailedTestCases in maxexpvalue from_blocktriple tests

0 of 3 new or added lines in 1 file covered. (0.0%)

9 existing lines in 2 files now uncovered.

44346 of 52563 relevant lines covered (84.37%)

6132421.69 hits per line

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

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

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

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

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

50
namespace sw { namespace universal {
51

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

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

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

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

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

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

224

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

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

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

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

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

316
                        // copy the blocks that contain fraction bits
317
                        tgt.clear();
531,877✔
318
                        for (unsigned b = 0; b < btType::nrBlocks; ++b) {
1,074,254✔
319
                                tgt.setblock(b, fracbits.block(b));
542,377✔
320
                        }
321
                        tgt.setsign(src.sign());
531,877✔
322
                        if (!tgt.setexponent(exponent)) {
531,877✔
323
                                std::cerr << "exponent value is out of range: " << exponent << '\n';
×
324
                        }
325

326
                        // saturation / overflow-to-inf handling (matches the bfbits < 65 path)
327
                        if constexpr (isSaturating) {
328
                                if (tgt.isnan()) {
6✔
329
                                        if (src.sign()) {
×
330
                                                tgt.maxneg();
×
331
                                        }
332
                                        else {
333
                                                tgt.maxpos();
×
334
                                        }
335
                                }
336
                        }
337
                        else {
338
                                if (tgt.isnan()) tgt.setinf(src.sign());
531,871✔
339
                        }
340
                }
341
        }
342
}
343

344

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

370
        static constexpr unsigned nbits = _nbits;
371
        static constexpr unsigned es = _es;
372
        static constexpr unsigned fbits  = nbits - 1u - es;    // number of fraction bits excluding the hidden bit
373
        static constexpr unsigned fhbits = nbits - es;           // number of fraction bits including the hidden bit
374

375
        static constexpr uint64_t  storageMask = (0xFFFFFFFFFFFFFFFFull >> (64ull - bitsInBlock));
376
        static constexpr bt       BLOCK_MASK = bt(~0);
377
        static constexpr bt       ALL_ONES = bt(~0); // block type specific all 1's value
378
        static constexpr uint32_t ALL_ONES_ES = (0xFFFF'FFFFul >> (32 - es));
379
        static constexpr uint64_t topfbits = fbits % 64;
380
        static constexpr uint64_t FR_SHIFT = (topfbits > 0 ? (64 - topfbits) : 0);
381
        static constexpr uint64_t ALL_ONES_FR = (topfbits > 0 ? (0xFFFF'FFFF'FFFF'FFFFull >> FR_SHIFT) : 0ull); // special case for nbits <= 64
382
        static constexpr uint64_t INF_ENCODING = (ALL_ONES_FR & ~1ull);
383

384
        static constexpr unsigned nrBlocks = 1u + ((nbits - 1ull) / bitsInBlock);
385
        static constexpr unsigned MSU = nrBlocks - 1u; // MSU == Most Significant Unit, as MSB is already taken
386
        static constexpr bt       MSU_MASK = (ALL_ONES >> (nrBlocks * bitsInBlock - nbits));
387
        static constexpr unsigned bitsInMSU = bitsInBlock - (nrBlocks * bitsInBlock - nbits);
388
        static constexpr unsigned fBlocks = 1ull + ((fbits - 1ull) / bitsInBlock); // nr of blocks with fraction bits
389
        static constexpr unsigned FSU = fBlocks - 1u;  // FSU = Fraction Significant Unit: the index of the block that contains the most significant fraction bits
390
        static constexpr bt       FSU_MASK = (ALL_ONES >> (fBlocks * bitsInBlock - fbits));
391
        static constexpr unsigned bitsInFSU = bitsInBlock - (fBlocks * bitsInBlock - fbits);
392

393
        static constexpr bt       SIGN_BIT_MASK = bt(bt(1ull) << ((nbits - 1ull) % bitsInBlock));
394
        static constexpr bt       LSB_BIT_MASK = bt(1ull);
395
        static constexpr bool     MSU_CAPTURES_EXP = (1ull + es) <= bitsInMSU;
396
        static constexpr unsigned EXP_SHIFT = (MSU_CAPTURES_EXP ? (1 == nrBlocks ? (nbits - 1ull - es) : (bitsInMSU - 1ull - es)) : 0);
397
        static constexpr bt       MSU_EXP_MASK = ((ALL_ONES << EXP_SHIFT) & ~SIGN_BIT_MASK) & MSU_MASK;
398
        static constexpr int      EXP_BIAS = ((1 << (es - 1u)) - 1l);
399
        static constexpr int      MAX_EXP = (es == 1) ? 1 : ((1 << es) - EXP_BIAS - 1);
400
        static constexpr int      MIN_EXP_NORMAL = 1 - EXP_BIAS;
401
        static constexpr int      MIN_EXP_SUBNORMAL = 1 - EXP_BIAS - int(fbits); // the scale of smallest ULP
402

403
        static constexpr bool     hasSubnormals   = _hasSubnormals;
404
        static constexpr bool     hasMaxExpValues = _hasMaxExpValues;
405
        static constexpr bool     isSaturating    = _isSaturating;
406
        typedef bt BlockType;
407

408
        // constructors
409
        cfloat() = default;
410
        cfloat(const cfloat&) = default;
411
        cfloat& operator=(const cfloat&) = default;
412

413
        // construct a cfloat from another
414
        template<unsigned nnbits, unsigned ees, typename bbt, bool ssub, bool ssup, bool ssat>
415
        cfloat(const cfloat<nnbits, ees, bbt, ssub, ssup, ssat>& rhs) noexcept : _block{} {
6,104✔
416
                if (rhs.isnan()) {
6,104✔
417
                        setnan(rhs.sign() ? NAN_TYPE_SIGNALLING : NAN_TYPE_QUIET);
79✔
418
                }
419
                else if (rhs.isinf()) {
6,025✔
420
                        setinf(rhs.sign());
9✔
421
                }
422
                else if (rhs.iszero()) {
6,016✔
423
                        setzero();
738✔
424
                }
425
                else {
426
                        // TODO: cfloat from another cfloat: marshall through a proper blocktriple
427
                        /*
428
                        if constexpr (std::is_same_v<bt, bbt>) {
429
                                blocktriple<fbits, BlockTripleOperator::REP, bt> value;
430
                                value.setnormal();
431
                                value.setsign(rhs.sign());
432
                                value.setscale(rhs.scale());
433
                                //constexpr unsigned rhsFbits = nnbits - 1ul - ees;
434
                                //blockbinary<rhsFbits, bbt, BinaryNumberType::Signed> fraction;
435
                                //rhs.fraction<rhsFbits>(fraction);
436
                                //std::cout << "fraction : " << to_binary(fraction) << '\n';
437
                                //value.setfraction(fraction);
438
                                convert(value, *this);
439
                        }
440
                        else {
441
                                static_assert(nnbits < 64, "converting constructor marshalls values through native double precision, and rhs has more bits");
442
                                *this = double(rhs); 
443
                        }
444
                        */
445
                        *this = double(rhs);
5,278✔
446
                }
447
        }
6,104✔
448

449
        // converting constructors
450
        constexpr cfloat(const std::string& stringRep) : _block{} { assign(stringRep); }
1✔
451
        // specific value constructor
452
        constexpr cfloat(const SpecificValue code) noexcept : _block{} {
309✔
453
                switch (code) {
309✔
454
                case SpecificValue::maxpos:
95✔
455
                        maxpos();
95✔
456
                        break;
95✔
457
                case SpecificValue::minpos:
111✔
458
                        minpos();
111✔
459
                        break;
111✔
460
                case SpecificValue::zero:
×
461
                default:
462
                        zero();
×
463
                        break;
×
464
                case SpecificValue::minneg:
×
465
                        minneg();
×
466
                        break;
×
467
                case SpecificValue::maxneg:
48✔
468
                        maxneg();
48✔
469
                        break;
48✔
470
                case SpecificValue::infpos:
21✔
471
                        setinf(false);
21✔
472
                        break;
21✔
473
                case SpecificValue::infneg:
×
474
                        setinf(true);
×
475
                        break;
×
476
                case SpecificValue::nar: // approximation as cfloats don't have a NaR
17✔
477
                case SpecificValue::qnan:
478
                        setnan(NAN_TYPE_QUIET);
17✔
479
                        break;
17✔
480
                case SpecificValue::snan:
17✔
481
                        setnan(NAN_TYPE_SIGNALLING);
17✔
482
                        break;
17✔
483
                }
484
        }
309✔
485

486
        constexpr cfloat(signed char iv)                    noexcept : _block{} { *this = iv; }
487
        constexpr cfloat(short iv)                          noexcept : _block{} { *this = iv; }
488
        constexpr cfloat(int iv)                            noexcept : _block{} { *this = iv; }
114,803✔
489
        constexpr cfloat(long iv)                           noexcept : _block{} { *this = iv; }
490
        constexpr cfloat(long long iv)                      noexcept : _block{} { *this = iv; }
1✔
491
        constexpr cfloat(char iv)                           noexcept : _block{} { *this = iv; }
492
        constexpr cfloat(unsigned short iv)                 noexcept : _block{} { *this = iv; }
493
        constexpr cfloat(unsigned int iv)                   noexcept : _block{} { *this = iv; }
494
        constexpr cfloat(unsigned long iv)                  noexcept : _block{} { *this = iv; }
100✔
495
        constexpr cfloat(unsigned long long iv)             noexcept : _block{} { *this = iv; }
496
        CONSTEXPRESSION cfloat(float iv)                    noexcept : _block{} { *this = iv; }
340,804✔
497
        CONSTEXPRESSION cfloat(double iv)                   noexcept : _block{} { *this = iv; }
515,969✔
498

499
        // assignment operators
500
        constexpr cfloat& operator=(signed char rhs)        noexcept { return convert_signed_integer(rhs); }
501
        constexpr cfloat& operator=(short rhs)              noexcept { return convert_signed_integer(rhs); }
502
        constexpr cfloat& operator=(int rhs)                noexcept { return convert_signed_integer(rhs); }
174,971✔
503
        constexpr cfloat& operator=(long rhs)               noexcept { return convert_signed_integer(rhs); }
504
        constexpr cfloat& operator=(long long rhs)          noexcept { return convert_signed_integer(rhs); }
1✔
505

506
        constexpr cfloat& operator=(char rhs)               noexcept { return convert_unsigned_integer(rhs); }
507
        constexpr cfloat& operator=(unsigned short rhs)     noexcept { return convert_unsigned_integer(rhs); }
508
        constexpr cfloat& operator=(unsigned int rhs)       noexcept { return convert_unsigned_integer(rhs); }
30✔
509
        constexpr cfloat& operator=(unsigned long rhs)      noexcept { return convert_unsigned_integer(rhs); }
110✔
510
        constexpr cfloat& operator=(unsigned long long rhs) noexcept { return convert_unsigned_integer(rhs); }
511

512
        BIT_CAST_CONSTEXPR cfloat& operator=(float rhs)     noexcept { return convert_ieee754(rhs); }
32,488,679✔
513
        BIT_CAST_CONSTEXPR cfloat& operator=(double rhs)    noexcept { return convert_ieee754(rhs); }
105,761,056✔
514

515
        // make conversions to native types explicit
516
        explicit operator int()                       const noexcept { return to_int(); }
59,976✔
517
        explicit operator long()                      const noexcept { return to_long(); }
518
        explicit operator long long()                 const noexcept { return to_long_long(); }
1✔
519
        explicit operator float()                     const noexcept { return to_native<float>(); }
35,177,206✔
520
        explicit operator double()                    const noexcept { return to_native<double>(); }
78,237,855✔
521

522
        // guard long double support to enable ARM and RISC-V embedded environments
523
#if LONG_DOUBLE_SUPPORT
524
        explicit operator long double()               const noexcept { return to_native<long double>(); }
54✔
525
        BIT_CAST_CONSTEXPR cfloat(long double iv)           noexcept : _block{} { *this = iv; }
2✔
526
        BIT_CAST_CONSTEXPR cfloat& operator=(long double rhs)  noexcept { return convert_ieee754(rhs); }
74✔
527
#endif
528

529
        // arithmetic operators
530
        // prefix operator
531
        inline cfloat operator-() const {
8,354,860✔
532
                cfloat tmp(*this);
8,354,860✔
533
                tmp._block[MSU] ^= SIGN_BIT_MASK;
8,354,860✔
534
                return tmp;
8,354,860✔
535
        }
536

537
        cfloat& operator+=(const cfloat& rhs) CFLOAT_EXCEPT {
18,485,463✔
538
                if constexpr (_trace_add) std::cout << "---------------------- ADD -------------------" << std::endl;
539
                // special case handling of the inputs
540
#if CFLOAT_THROW_ARITHMETIC_EXCEPTION
541
                if (isnan(NAN_TYPE_SIGNALLING) || rhs.isnan(NAN_TYPE_SIGNALLING)) {
744✔
542
                        throw cfloat_operand_is_nan{};
×
543
                }
544
#else
545
                if (isnan(NAN_TYPE_SIGNALLING) || rhs.isnan(NAN_TYPE_SIGNALLING)) {
18,484,719✔
546
                        setnan(NAN_TYPE_SIGNALLING);
236,540✔
547
                        return *this;
236,540✔
548
                }
549
                if (isnan(NAN_TYPE_QUIET) || rhs.isnan(NAN_TYPE_QUIET)) {
18,248,179✔
550
                        setnan(NAN_TYPE_QUIET);
216,964✔
551
                        return *this;
216,964✔
552
                }
553
#endif
554
                // normal + inf  = inf
555
                // normal + -inf = -inf
556
                // inf + normal = inf
557
                // inf + inf    = inf
558
                // inf + -inf    = ?
559
                // -inf + normal = -inf
560
                // -inf + -inf   = -inf
561
                // -inf + inf    = ?
562
                if (isinf()) {
18,031,959✔
563
                        if (rhs.isinf()) {
60,327✔
564
                                if (sign() != rhs.sign()) {
939✔
565
                                        setnan(NAN_TYPE_SIGNALLING);
514✔
566
                                }
567
                                return *this;
939✔
568
                        }
569
                        else {
570
                                return *this;
59,388✔
571
                        }
572
                }
573
                else {
574
                        if (rhs.isinf()) {
17,971,632✔
575
                                *this = rhs;
58,293✔
576
                                return *this;
58,293✔
577
                        }
578
                }
579

580
                if (iszero()) {
17,913,339✔
581
                        *this = rhs;
206,307✔
582
                        return *this;
206,307✔
583
                }
584
                if (rhs.iszero()) return *this;
17,707,032✔
585

586
                // arithmetic operation
587
                blocktriple<fbits, BlockTripleOperator::ADD, bt> a, b, sum;
17,374,995✔
588

589
                // transform the inputs into (sign,scale,significant) 
590
                normalizeAddition(a); 
17,374,995✔
591
                rhs.normalizeAddition(b);
17,374,995✔
592
                sum.add(a, b);
17,374,995✔
593

594
                convert(sum, *this);
17,374,995✔
595

596
                return *this;
17,374,995✔
597
        }
598
        cfloat& operator+=(double rhs) CFLOAT_EXCEPT {
599
                return *this += cfloat(rhs);
600
        }
601
        cfloat& operator-=(const cfloat& rhs) CFLOAT_EXCEPT {
8,469,305✔
602
                if constexpr (_trace_sub) std::cout << "---------------------- SUB -------------------" << std::endl;
603
                if (rhs.isnan()) 
8,469,305✔
604
                        return *this += rhs;
170,204✔
605
                else 
606
                        return *this += -rhs;
8,299,101✔
607
        }
608
        cfloat& operator-=(double rhs) CFLOAT_EXCEPT {
8✔
609
                return *this -= cfloat(rhs);
8✔
610
        }
611
        cfloat& operator*=(const cfloat& rhs) CFLOAT_EXCEPT {
4,312,845✔
612
                if constexpr (_trace_mul) std::cout << "---------------------- MUL -------------------\n";
322✔
613
                // special case handling of the inputs
614
#if CFLOAT_THROW_ARITHMETIC_EXCEPTION
615
                if (isnan(NAN_TYPE_SIGNALLING) || rhs.isnan(NAN_TYPE_SIGNALLING)) {
612✔
616
                        throw cfloat_operand_is_nan{};
×
617
                }
618
#else
619
                if (isnan(NAN_TYPE_SIGNALLING) || rhs.isnan(NAN_TYPE_SIGNALLING)) {
4,312,233✔
620
                        setnan(NAN_TYPE_SIGNALLING);
124,658✔
621
                        return *this;
124,658✔
622
                }
623
                if (isnan(NAN_TYPE_QUIET) || rhs.isnan(NAN_TYPE_QUIET)) {
4,187,575✔
624
                        setnan(NAN_TYPE_QUIET);
113,354✔
625
                        return *this;
113,354✔
626
                }
627
#endif
628
                //  inf * inf = inf
629
                //  inf * -inf = -inf
630
                // -inf * inf = -inf
631
                // -inf * -inf = inf
632
                //        0 * inf = -nan(ind)
633
                //        inf * 0 = -nan(ind)
634
                bool resultSign = sign() != rhs.sign();
4,074,833✔
635
                if (isinf()) {
4,074,833✔
636
                        if (rhs.iszero()) {
24,457✔
637
                                setnan(NAN_TYPE_QUIET);
1,591✔
638
                        }
639
                        else {
640
                                setsign(resultSign);
22,866✔
641
                        }
642
                        return *this;
24,457✔
643
                }
644
                if (rhs.isinf()) {
4,050,376✔
645
                        if (iszero()) {
25,815✔
646
                                setnan(NAN_TYPE_QUIET);
2,969✔
647
                        }
648
                        else {
649
                                setinf(resultSign);
22,846✔
650
                        }
651
                        return *this;
25,815✔
652
                }
653

654
                if (iszero() || rhs.iszero()) {                        
4,024,561✔
655
                        setzero();
1,876,394✔
656
                        setsign(resultSign); // deal with negative 0
1,876,394✔
657
                        return *this;
1,876,394✔
658
                }
659

660
                // arithmetic operation
661
                blocktriple<fbits, BlockTripleOperator::MUL, bt> a, b, product;
2,148,167✔
662

663
                // transform the inputs into (sign,scale,significant) 
664
                // triples of the correct width
665
                normalizeMultiplication(a);
2,148,167✔
666
                rhs.normalizeMultiplication(b);
2,148,167✔
667
                product.mul(a, b);
2,148,167✔
668
                convert(product, *this);
2,148,167✔
669

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

672
                return *this;
2,148,167✔
673
        }
674
        cfloat& operator*=(double rhs) CFLOAT_EXCEPT {
40✔
675
                return *this *= cfloat(rhs);
40✔
676
        }
677
        cfloat& operator/=(const cfloat& rhs) CFLOAT_EXCEPT {
5,139,192✔
678
                if constexpr (_trace_div) std::cout << "---------------------- DIV -------------------" << std::endl;
679

680
                // special case handling of the inputs
681
                // qnan / qnan = qnan
682
                // qnan / snan = qnan
683
                // snan / qnan = snan
684
                // snan / snan = snan
685
#if CFLOAT_THROW_ARITHMETIC_EXCEPTION
686
                if (rhs.iszero()) throw cfloat_divide_by_zero();
49✔
687
                if (rhs.isnan()) throw cfloat_divide_by_nan();
48✔
688
                if (isnan()) throw cfloat_operand_is_nan();
48✔
689
#else
690
                if (isnan(NAN_TYPE_SIGNALLING) || rhs.isnan(NAN_TYPE_SIGNALLING)) {
5,139,143✔
691
                        setnan(NAN_TYPE_SIGNALLING);
164,429✔
692
                        return *this;
164,429✔
693
                }
694
                if (isnan(NAN_TYPE_QUIET) || rhs.isnan(NAN_TYPE_QUIET)) {
4,974,714✔
695
                        setnan(NAN_TYPE_QUIET);
151,601✔
696
                        return *this;
151,601✔
697
                }
698
                if (rhs.iszero()) {
4,823,113✔
699
                        if (iszero()) {
169,760✔
700
                                // zero divide by zero yields quiet NaN (in MSVC it is labeled -nan(ind) for indeterminate)
701
                                setnan(NAN_TYPE_QUIET);
29,299✔
702
                        }
703
                        else {
704
                                // non-zero divide by zero yields INF
705
                                bool resultSign = sign() != rhs.sign();
140,461✔
706
                                setinf(resultSign);
140,461✔
707
                        }
708
                        return *this;
169,760✔
709
                }
710
#endif
711
                //  inf /  inf = -nan(ind)
712
                //  inf / -inf = -nan(ind)
713
                // -inf /  inf = -nan(ind)
714
                // -inf / -inf = -nan(ind)
715
                //        1.0 /  inf = 0
716
                bool resultSign = sign() != rhs.sign();
4,653,401✔
717
                if (isinf()) {
4,653,401✔
718
                        if (rhs.isinf()) {
31,087✔
719
                                // inf divide by inf yields quiet NaN (in MSVC it is labeled -nan(ind) for indeterminate)
720
                                setnan(NAN_TYPE_QUIET);
559✔
721
                                return *this;
559✔
722
                        }
723
                        else {
724
                                // we stay an infinite but may change sign
725
                                setsign(resultSign);
30,528✔
726
                                return *this;
30,528✔
727
                        }
728
                }
729
                else {
730
                        if (rhs.isinf()) {
4,622,314✔
731
                                setzero();
32,833✔
732
                                setsign(resultSign);
32,833✔
733
                                return *this;
32,833✔
734
                        }
735
                }
736

737
                if (iszero()) {
4,589,481✔
738
                        setzero();
139,452✔
739
                        setsign(resultSign); // deal with negative 0
139,452✔
740
                        return *this;
139,452✔
741
                }
742

743
                // arithmetic operation
744
                using BlockTriple = blocktriple<fbits, BlockTripleOperator::DIV, bt>;
745
                BlockTriple a, b, quotient;
4,450,029✔
746

747
                // transform the inputs into (sign,scale,significant) 
748
                // triples of the correct width
749
                normalizeDivision(a);
4,450,029✔
750
                rhs.normalizeDivision(b);
4,450,029✔
751
                quotient.div(a, b);
4,450,029✔
752
                quotient.setradix(BlockTriple::radix);
4,450,029✔
753
                convert(quotient, *this);
4,450,029✔
754

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

757
                return *this;
4,450,029✔
758
        }
759
        cfloat& operator/=(double rhs) CFLOAT_EXCEPT {
760
                return *this /= cfloat(rhs);
761
        }
762
        cfloat& reciprocal() CFLOAT_EXCEPT {
763
                cfloat c = 1.0 / *this;
764
                return *this = c;
765
        }
766
        /// <summary>
767
        /// move to the next bit encoding modulo 2^nbits
768
        /// </summary>
769
        /// <typeparam name="bt"></typeparam>
770
        cfloat& operator++() {
4,598,642✔
771
                if constexpr (0 == nrBlocks) {
772
                        return *this;
773
                }
774
                else if constexpr (1 == nrBlocks) {
775
                        if (sign()) {
864,255✔
776
                                if (_block[MSU] == (SIGN_BIT_MASK | 1ul)) { // pattern: 1.00.001 = minneg
467✔
777
                                        _block[MSU] = 0; // pattern: 0.00.000 = +0 
7✔
778
                                }
779
                                else {
780
                                        --_block[MSU];
460✔
781
                                }
782
                                if constexpr (!hasSubnormals) {
783
                                        if (isdenormal()) {
198✔
784
                                                // special case, we need to jump past all the subnormal value encodings which puts us on 0
785
                                                _block[MSU] = 0; // pattern: 0.00.000 = +0
3✔
786
                                        }
787
                                }
788
                        }
789
                        else {
790
                                if constexpr (!hasSubnormals) {
791
                                        if (_block[MSU] == 0) {
384,649✔
792
                                                // special case, we need to jump past all the subnormal value encodings minus 1
793
                                                setfraction(0xFFFF'FFFF'FFFF'FFFFull);
4✔
794
                                        }
795
                                }
796
                                if ((_block[MSU] & (MSU_MASK >> 1)) == (MSU_MASK >> 1)) { // pattern: 0.11.111 = nan
863,788✔
797
                                        _block[MSU] |= SIGN_BIT_MASK; // pattern: 1.11.111 = snan : wrap to the other side of the encoding
×
798
                                }
799
                                else {
800
                                        ++_block[MSU];
863,788✔
801
                                }
802
                        }
803
                }
804
                else {
805
                        if (sign()) {
3,734,387✔
806
                                // special case: pattern: 1.00.001 = minneg transitions to pattern: 0.00.000 = +0 
807
                                if (isminnegencoding()) {
66,947✔
808
                                        setzero();
8✔
809
                                }
810
                                else {
811
                                        //  1111 0000
812
                                        //  1110 1111
813
                                        bool borrow = true;
66,939✔
814
                                        for (unsigned i = 0; i < MSU; ++i) {
199,415✔
815
                                                if (borrow) {
132,476✔
816
                                                        if ((_block[i] & storageMask) == 0) { // block will underflow
67,197✔
817
                                                                --_block[i];
261✔
818
                                                                borrow = true;
261✔
819
                                                        }
820
                                                        else {
821
                                                                --_block[i];
66,936✔
822
                                                                borrow = false;
66,936✔
823
                                                        }
824
                                                }
825
                                        }
826
                                        if (borrow) {
66,939✔
827
                                                --_block[MSU];
3✔
828
                                        }
829
                                        if constexpr (!hasSubnormals) {
830
                                                if (isdenormal()) {
385✔
831
                                                        // special case, we need to jump past all the subnormal value encodings which puts us on 0
832
                                                        setzero(); // pattern: 0.00.000 = +0
3✔
833
                                                }
834
                                        }
835
                                }
836
                        }
837
                        else {
838
                                // special case: pattern: 0.11.111 = nan transitions to pattern: 1.11.111 = snan 
839
                                if (isnanencoding()) {
3,667,440✔
840
                                        setnan(NAN_TYPE_SIGNALLING);
×
841
                                }
842
                                else {
843
                                        if constexpr (!hasSubnormals) {
844
                                                if (iszero()) {
1,720,661✔
845
                                                        // 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
846
                                                        setfraction(0xFFFF'FFFF'FFFF'FFFFull);
3✔
847
                                                }
848
                                        }
849
                                        bool carry = true;
3,667,440✔
850
                                        for (unsigned i = 0; i < MSU; ++i) {
7,400,778✔
851
                                                if (carry) {
3,733,338✔
852
                                                        if ((_block[i] & storageMask) == storageMask) { // block will overflow
3,667,695✔
853
                                                                _block[i] = 0;
256✔
854
                                                                carry = true;
256✔
855
                                                        }
856
                                                        else {
857
                                                                ++_block[i];
3,667,439✔
858
                                                                carry = false;
3,667,439✔
859
                                                        }
860
                                                }
861
                                        }
862
                                        if (carry) {
3,667,440✔
863
                                                ++_block[MSU];
1✔
864
                                        }
865
                                }
866
                        }
867
                }
868
                return *this;
4,598,642✔
869
        }
870
        cfloat operator++(int) {
134,800✔
871
                cfloat tmp(*this);
134,800✔
872
                operator++();
134,800✔
873
                return tmp;
134,800✔
874
        }
875
        cfloat& operator--() {
5,014,100✔
876
                if constexpr (0 == nrBlocks) {
877
                        return *this;
878
                }
879
                else if constexpr (1 == nrBlocks) {
880
                        if (sign()) {
942,793✔
881
                                ++_block[MSU];
942,299✔
882
                        }
883
                        else {
884
                                // positive range
885
                                if (_block[MSU] == 0) { // pattern: 0.00.000 = 0
494✔
886
                                        if constexpr (hasSubnormals) {
887
                                                _block[MSU] |= SIGN_BIT_MASK | bt(1u); // pattern: 1.00.001 = minneg 
8✔
888
                                        }
889
                                        else {
890
                                                // special case, we need to jump past all the subnormal value encodings
891
                                                setfraction(0xFFFF'FFFF'FFFF'FFFFull); // set to 0.00.11...11
3✔
892
                                                ++_block[MSU]; // increment into 0.01.0000
3✔
893
                                                _block[MSU] |= SIGN_BIT_MASK; // set to 1.01.0000
3✔
894
                                        }
895
                                }
896
                                else {
897
                                        --_block[MSU];
483✔
898
                                }
899
                                if constexpr (!hasSubnormals) {
900
                                        if (isdenormal()) {
211✔
901
                                                // special case, we need to jump past all the subnormal value encodings which puts us on 0
902
                                                _block[MSU] = 0; // pattern: 0.00.000 = +0
3✔
903
                                        }
904
                                }
905
                        }
906
                }
907
                else {
908
                        if (sign()) {
4,071,307✔
909
                                bool carry = true;
4,004,349✔
910
                                for (unsigned i = 0; i < MSU; ++i) {
8,074,235✔
911
                                        if (carry) {
4,069,886✔
912
                                                if ((_block[i] & storageMask) == storageMask) { // block will overflow
4,004,604✔
913
                                                        _block[i] = 0;
256✔
914
                                                        carry = true;
256✔
915
                                                }
916
                                                else {
917
                                                        ++_block[i];
4,004,348✔
918
                                                        carry = false;
4,004,348✔
919
                                                }
920
                                        }
921
                                }
922
                                if (carry) {
4,004,349✔
923
                                        ++_block[MSU];
1✔
924
                                }
925
                        }
926
                        else {
927
                                // special case: pattern: 0.00.000 = +0 transitions to pattern: 1.00.001 = minneg
928
                                if (iszeroencoding()) {
66,958✔
929
                                        if constexpr (hasSubnormals) {
930
                                                setsign(true);
8✔
931
                                                setbit(0, true);
8✔
932
                                        }
933
                                        else {
934
                                                // special case, we need to jump past all the subnormal value encodings 1.01.0000 = minneg normal
935
                                                setexponent(1 - EXP_BIAS);
2✔
936
                                                setsign(true);
2✔
937
                                        }
938
                                }
939
                                else {
940
                                        bool borrow = true;
66,948✔
941
                                        for (unsigned i = 0; i < MSU; ++i) {
199,441✔
942
                                                if (borrow) {
132,493✔
943
                                                        if ((_block[i] & storageMask) == 0) { // block will underflow
67,209✔
944
                                                                --_block[i];
266✔
945
                                                                borrow = true;
266✔
946
                                                        }
947
                                                        else {
948
                                                                --_block[i];
66,943✔
949
                                                                borrow = false;
66,943✔
950
                                                        }
951
                                                }
952
                                        }
953
                                        if (borrow) {
66,948✔
954
                                                --_block[MSU];
5✔
955
                                        }
956
                                        if constexpr (!hasSubnormals) {
957
                                                if (isdenormal()) {
384✔
958
                                                        // special case, we need to jump past all the subnormal value encodings which puts us on 0
959
                                                        setzero(); // pattern: 0.00.000 = +0
2✔
960
                                                }
961
                                        }
962
                                }
963
                        }
964
                }
965
                return *this;
5,014,100✔
966
        }
967
        cfloat operator--(int) {
134,812✔
968
                cfloat tmp(*this);
134,812✔
969
                operator--();
134,812✔
970
                return tmp;
134,812✔
971
        }
972

973
        // modifiers        
974
        constexpr void clear() noexcept {
143,458,986✔
975
                if constexpr (0 == nrBlocks) {
976
                        return;
977
                }
978
                else if constexpr (1 == nrBlocks) {
979
                        _block[0] = bt(0);
41,294,787✔
980
                }
981
                else if constexpr (2 == nrBlocks) {
982
                        _block[0] = bt(0);
48,056,940✔
983
                        _block[1] = bt(0);
48,056,940✔
984
                }
985
                else if constexpr (3 == nrBlocks) {
986
                        _block[0] = bt(0);
53,973,312✔
987
                        _block[1] = bt(0);
53,973,312✔
988
                        _block[2] = bt(0);
53,973,312✔
989
                }
990
                else if constexpr (4 == nrBlocks) {
991
                        _block[0] = bt(0);
43,259✔
992
                        _block[1] = bt(0);
43,259✔
993
                        _block[2] = bt(0);
43,259✔
994
                        _block[3] = bt(0);
43,259✔
995
                }
996
                else if constexpr (5 == nrBlocks) {
997
                        _block[0] = bt(0);
29,999✔
998
                        _block[1] = bt(0);
29,999✔
999
                        _block[2] = bt(0);
29,999✔
1000
                        _block[3] = bt(0);
29,999✔
1001
                        _block[4] = bt(0);
29,999✔
1002
                }
1003
                else if constexpr (6 == nrBlocks) {
1004
                        _block[0] = bt(0);
10,018✔
1005
                        _block[1] = bt(0);
10,018✔
1006
                        _block[2] = bt(0);
10,018✔
1007
                        _block[3] = bt(0);
10,018✔
1008
                        _block[4] = bt(0);
10,018✔
1009
                        _block[5] = bt(0);
10,018✔
1010
                }
1011
                else if constexpr (7 == nrBlocks) {
1012
                        _block[0] = bt(0);
9,999✔
1013
                        _block[1] = bt(0);
9,999✔
1014
                        _block[2] = bt(0);
9,999✔
1015
                        _block[3] = bt(0);
9,999✔
1016
                        _block[4] = bt(0);
9,999✔
1017
                        _block[5] = bt(0);
9,999✔
1018
                        _block[6] = bt(0);
9,999✔
1019
                }
1020
                else if constexpr (8 == nrBlocks) {
1021
                        _block[0] = bt(0);
20,325✔
1022
                        _block[1] = bt(0);
20,325✔
1023
                        _block[2] = bt(0);
20,325✔
1024
                        _block[3] = bt(0);
20,325✔
1025
                        _block[4] = bt(0);
20,325✔
1026
                        _block[5] = bt(0);
20,325✔
1027
                        _block[6] = bt(0);
20,325✔
1028
                        _block[7] = bt(0);
20,325✔
1029
                }
1030
                else {
1031
                        for (unsigned i = 0; i < nrBlocks; ++i) {
224,339✔
1032
                                _block[i] = bt(0);
203,992✔
1033
                        }
1034
                }
1035
        }
143,458,986✔
1036
        constexpr void setzero() noexcept { clear(); }
2,695,445✔
1037
        constexpr void setinf(bool sign = true) noexcept {
6,555,125✔
1038
                // the Inf encoding is the pattern 0b0'11...11'11...10 for a +inf, and 0b1'11...11'11...110 for a -inf
1039
                if constexpr (0 == nrBlocks) {
1040
                        return;
1041
                }
1042
                else if constexpr (1 == nrBlocks) {
1043
                        _block[MSU] = sign ? bt(MSU_MASK ^ LSB_BIT_MASK) : bt(~SIGN_BIT_MASK & (MSU_MASK ^ LSB_BIT_MASK));
2,198,794✔
1044
                }
1045
                else if constexpr (2 == nrBlocks) {
1046
                        _block[0] = BLOCK_MASK ^ LSB_BIT_MASK;
1,859,348✔
1047
                        _block[MSU] = sign ? MSU_MASK : bt(~SIGN_BIT_MASK & MSU_MASK);
1,859,348✔
1048
                }
1049
                else if constexpr (3 == nrBlocks) {
1050
                        _block[0] = BLOCK_MASK ^ LSB_BIT_MASK;
2,493,952✔
1051
                        _block[1] = BLOCK_MASK;
2,493,952✔
1052
                        _block[MSU] = sign ? MSU_MASK : bt(~SIGN_BIT_MASK & MSU_MASK);
2,493,952✔
1053
                }
1054
                else if constexpr (4 == nrBlocks) {
1055
                        _block[0] = BLOCK_MASK ^ LSB_BIT_MASK;
2,758✔
1056
                        _block[1] = BLOCK_MASK;
2,758✔
1057
                        _block[2] = BLOCK_MASK;
2,758✔
1058
                        _block[MSU] = sign ? MSU_MASK : bt(~SIGN_BIT_MASK & MSU_MASK);
2,758✔
1059
                }
1060
                else if constexpr (5 == nrBlocks) {
1061
                        _block[0] = BLOCK_MASK ^ LSB_BIT_MASK;
74✔
1062
                        _block[1] = BLOCK_MASK;
74✔
1063
                        _block[2] = BLOCK_MASK;
74✔
1064
                        _block[3] = BLOCK_MASK;
74✔
1065
                        _block[MSU] = sign ? MSU_MASK : bt(~SIGN_BIT_MASK & MSU_MASK);
74✔
1066
                }
1067
                else if constexpr (6 == nrBlocks) {
1068
                        _block[0] = BLOCK_MASK ^ LSB_BIT_MASK;
46✔
1069
                        _block[1] = BLOCK_MASK;
46✔
1070
                        _block[2] = BLOCK_MASK;
46✔
1071
                        _block[3] = BLOCK_MASK;
46✔
1072
                        _block[4] = BLOCK_MASK;
46✔
1073
                        _block[MSU] = sign ? MSU_MASK : bt(~SIGN_BIT_MASK & MSU_MASK);
46✔
1074
                }
1075
                else if constexpr (7 == nrBlocks) {
1076
                        _block[0] = BLOCK_MASK ^ LSB_BIT_MASK;
40✔
1077
                        _block[1] = BLOCK_MASK;
40✔
1078
                        _block[2] = BLOCK_MASK;
40✔
1079
                        _block[3] = BLOCK_MASK;
40✔
1080
                        _block[4] = BLOCK_MASK;
40✔
1081
                        _block[5] = BLOCK_MASK;
40✔
1082
                        _block[MSU] = sign ? MSU_MASK : bt(~SIGN_BIT_MASK & MSU_MASK);
40✔
1083
                }
1084
                else if constexpr (8 == nrBlocks) {
1085
                        _block[0] = BLOCK_MASK ^ LSB_BIT_MASK;
78✔
1086
                        _block[1] = BLOCK_MASK;
78✔
1087
                        _block[2] = BLOCK_MASK;
78✔
1088
                        _block[3] = BLOCK_MASK;
78✔
1089
                        _block[4] = BLOCK_MASK;
78✔
1090
                        _block[5] = BLOCK_MASK;
78✔
1091
                        _block[6] = BLOCK_MASK;
78✔
1092
                        _block[MSU] = sign ? MSU_MASK : bt(~SIGN_BIT_MASK & MSU_MASK);
78✔
1093
                }
1094
                else {
1095
                        _block[0] = BLOCK_MASK ^ LSB_BIT_MASK;
35✔
1096
                        for (unsigned i = 1; i < nrBlocks - 1; ++i) {
315✔
1097
                                _block[i] = BLOCK_MASK;
280✔
1098
                        }
1099
                        _block[MSU] = sign ? MSU_MASK : bt(~SIGN_BIT_MASK & MSU_MASK);
35✔
1100
                }        
1101
        }
6,555,125✔
1102
        constexpr void setnan(int NaNType = NAN_TYPE_SIGNALLING) noexcept {
7,120,549✔
1103
                // 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
1104
                if constexpr (0 == nrBlocks) {
1105
                        return;
1106
                }
1107
                else if constexpr (1 == nrBlocks) {
1108
                        // fall through
1109
                }
1110
                else if constexpr (2 == nrBlocks) {
1111
                        _block[0] = BLOCK_MASK;
1,994,646✔
1112
                }
1113
                else if constexpr (3 == nrBlocks) {
1114
                        _block[0] = BLOCK_MASK;
1,048,702✔
1115
                        _block[1] = BLOCK_MASK;
1,048,702✔
1116
                }
1117
                else if constexpr (4 == nrBlocks) {
1118
                        _block[0] = BLOCK_MASK;
25✔
1119
                        _block[1] = BLOCK_MASK;
25✔
1120
                        _block[2] = BLOCK_MASK;
25✔
1121
                }
1122
                else if constexpr (5 == nrBlocks) {
1123
                        _block[0] = BLOCK_MASK;
10✔
1124
                        _block[1] = BLOCK_MASK;
10✔
1125
                        _block[2] = BLOCK_MASK;
10✔
1126
                        _block[3] = BLOCK_MASK;
10✔
1127
                }
1128
                else if constexpr (6 == nrBlocks) {
1129
                        _block[0] = BLOCK_MASK;
4✔
1130
                        _block[1] = BLOCK_MASK;
4✔
1131
                        _block[2] = BLOCK_MASK;
4✔
1132
                        _block[3] = BLOCK_MASK;
4✔
1133
                        _block[4] = BLOCK_MASK;
4✔
1134
                }
1135
                else if constexpr (7 == nrBlocks) {
1136
                        _block[0] = BLOCK_MASK;
4✔
1137
                        _block[1] = BLOCK_MASK;
4✔
1138
                        _block[2] = BLOCK_MASK;
4✔
1139
                        _block[3] = BLOCK_MASK;
4✔
1140
                        _block[4] = BLOCK_MASK;
4✔
1141
                        _block[5] = BLOCK_MASK;
4✔
1142
                }
1143
                else if constexpr (8 == nrBlocks) {
1144
                        _block[0] = BLOCK_MASK;
4✔
1145
                        _block[1] = BLOCK_MASK;
4✔
1146
                        _block[2] = BLOCK_MASK;
4✔
1147
                        _block[3] = BLOCK_MASK;
4✔
1148
                        _block[4] = BLOCK_MASK;
4✔
1149
                        _block[5] = BLOCK_MASK;
4✔
1150
                        _block[6] = BLOCK_MASK;
4✔
1151
                }
1152
                else {
1153
                        for (unsigned i = 0; i < nrBlocks - 1; ++i) {
40✔
1154
                                _block[i] = BLOCK_MASK;
36✔
1155
                        }
1156
                }
1157
                _block[MSU] = NaNType == NAN_TYPE_SIGNALLING ? MSU_MASK : bt(~SIGN_BIT_MASK & MSU_MASK);
7,120,549✔
1158
        }
7,120,549✔
1159
        constexpr void setsign(bool sign = true) {
3,537,417✔
1160
                if (sign) {
3,537,417✔
1161
                        _block[MSU] |= SIGN_BIT_MASK;
631,708✔
1162
                }
1163
                else {
1164
                        _block[MSU] &= ~SIGN_BIT_MASK;
2,905,709✔
1165
                }
1166
        }
3,537,417✔
1167
        constexpr bool setexponent(int scale) {
590,493✔
1168
                if (scale < MIN_EXP_SUBNORMAL || scale > MAX_EXP) return false; // this scale cannot be represented
590,493✔
1169
                if constexpr (nbits < 65) {
1170
                        uint32_t exponentBits = static_cast<uint32_t>(scale + EXP_BIAS);
588,952✔
1171
                        if (scale >= MIN_EXP_SUBNORMAL && scale < MIN_EXP_NORMAL) {
588,952✔
1172
                                // setexponent() only writes the encoding field. In the subnormal interval the true scale is carried
1173
                                // by a zero exponent field plus a left-shifted fraction, so callers must have already denormalized
1174
                                // the significand before asking for this exponent pattern.
1175
                                // we are a subnormal number: all exponent bits are 0
1176
                                exponentBits = 0;
45✔
1177
                        }
1178
                        // TODO: optimize
1179
                        uint32_t mask = (1ul << (es - 1));
588,952✔
1180
                        for (unsigned i = nbits - 2; i > nbits - 2 - es; --i) {
5,083,840✔
1181
                                setbit(i, (mask & exponentBits));
4,494,888✔
1182
                                mask >>= 1;
4,494,888✔
1183
                        }
1184
                }
1185
                else {
1186
                        uint32_t exponentBits = static_cast<uint32_t>(scale + EXP_BIAS);
1,541✔
1187
                        uint32_t mask = (1ul << (es - 1));
1,541✔
1188
                        for (unsigned i = nbits - 2; i > nbits - 2 - es; --i) {
25,156✔
1189
                                setbit(i, (mask & exponentBits));
23,615✔
1190
                                mask >>= 1;
23,615✔
1191
                        }
1192
                }
1193
                return true;
590,493✔
1194
        }
1195
        constexpr void setfraction(uint64_t raw_bits) {
610✔
1196
                // unoptimized as it is not meant to be an end-user API, it is a test API
1197
                // raw_bits is uint64_t so can have at most 64 bits of fraction data
1198
                constexpr unsigned bitsToSet = (fbits < 64) ? fbits : 64;
610✔
1199
                uint64_t mask{ 1ull };
610✔
1200
                for (unsigned i = 0; i < bitsToSet; ++i) {
24,744✔
1201
                        setbit(i, (mask & raw_bits));
24,134✔
1202
                        mask <<= 1;
24,134✔
1203
                }
1204
        }
610✔
1205
        constexpr void setbit(unsigned i, bool v = true) noexcept {
8,380,697✔
1206
                unsigned blockIndex = i / bitsInBlock;
8,380,697✔
1207
                if (blockIndex < nrBlocks) {
8,380,697✔
1208
                        bt block = _block[blockIndex];
8,380,697✔
1209
                        bt null = ~(1ull << (i % bitsInBlock));
8,380,697✔
1210
                        bt bit = bt(v ? 1 : 0);
8,380,697✔
1211
                        bt mask = bt(bit << (i % bitsInBlock));
8,380,697✔
1212
                        _block[blockIndex] = bt((block & null) | mask);
8,380,697✔
1213
                }
1214
        }
8,380,697✔
1215
        constexpr cfloat& setbits(uint64_t raw_bits) noexcept {
306,592,744✔
1216
                if constexpr (0 == nrBlocks) {
1217
                        return *this;
1218
                }
1219
                else if constexpr (1 == nrBlocks) {
1220
                        _block[0] = raw_bits & storageMask;
89,231,233✔
1221
                }
1222
                else if constexpr (2 == nrBlocks) {
1223
                        if constexpr (bitsInBlock < 64) {
1224
                                _block[0] = raw_bits & storageMask;
103,243,945✔
1225
                                raw_bits >>= bitsInBlock;
103,243,945✔
1226
                                _block[1] = raw_bits & storageMask;
103,243,945✔
1227
                        }
1228
                        else {
1229
                                _block[0] = raw_bits & storageMask;
1230
                                _block[1] = 0;
1231
                        }
1232
                }
1233
                else if constexpr (3 == nrBlocks) {
1234
                        if constexpr (bitsInBlock < 64) {
1235
                                _block[0] = raw_bits & storageMask;
113,957,311✔
1236
                                raw_bits >>= bitsInBlock;
113,957,311✔
1237
                                _block[1] = raw_bits & storageMask;
113,957,311✔
1238
                                raw_bits >>= bitsInBlock;
113,957,311✔
1239
                                _block[2] = raw_bits & storageMask;
113,957,311✔
1240
                        }
1241
                        else {
1242
                                _block[0] = raw_bits & storageMask;
1243
                                _block[1] = 0;
1244
                                _block[2] = 0;
1245
                        }
1246
                }
1247
                else if constexpr (4 == nrBlocks) {
1248
                        if constexpr (bitsInBlock < 64) {
1249
                                _block[0] = raw_bits & storageMask;
70,242✔
1250
                                raw_bits >>= bitsInBlock;
70,242✔
1251
                                _block[1] = raw_bits & storageMask;
70,242✔
1252
                                raw_bits >>= bitsInBlock;
70,242✔
1253
                                _block[2] = raw_bits & storageMask;
70,242✔
1254
                                raw_bits >>= bitsInBlock;
70,242✔
1255
                                _block[3] = raw_bits & storageMask;
70,242✔
1256
                        }
1257
                        else {
1258
                                _block[0] = raw_bits & storageMask;
1259
                                _block[1] = 0;
1260
                                _block[2] = 0;
1261
                                _block[3] = 0;
1262
                        }
1263
                }
1264
                else {
1265
                        if constexpr (bitsInBlock < 64) {
1266
                                for (unsigned i = 0; i < nrBlocks; ++i) {
730,740✔
1267
                                        _block[i] = raw_bits & storageMask;
640,727✔
1268
                                        raw_bits >>= bitsInBlock;
640,727✔
1269
                                }
1270
                        }
1271
                        else {
1272
                                _block[0] = raw_bits & storageMask;
1273
                                for (unsigned i = 1; i < nrBlocks; ++i) {
1274
                                        _block[i] = 0;
1275
                                }
1276
                        }
1277
                }
1278
                _block[MSU] &= MSU_MASK; // enforce precondition for fast comparison by properly nulling bits that are outside of nbits
306,592,744✔
1279
                return *this;
306,592,744✔
1280
        }
1281
        constexpr void setblock(unsigned b, bt data) noexcept {
542,377✔
1282
                if (b < nrBlocks) _block[b] = data;
542,377✔
1283
        }
542,377✔
1284
        
1285
        // create specific number system values of interest
1286
        constexpr cfloat& maxpos() noexcept {
936,035✔
1287
                if constexpr (isSaturating) {
1288
                        // in a saturating encoding with max-exponent values we are removing the Inf encoding pattern 0b0'11...11'11...10 for a +inf, 
1289
                        // and 0b1'11...11'11...110 for a -inf and using it as a value
1290
                        if constexpr (hasMaxExpValues) {
1291
                                // maximum positive value has this bit pattern: 0-1...1-111...110, that is, sign = 0, e = 11..11, f = 111...110
1292
                                clear();
935,334✔
1293
                                flip();
935,334✔
1294
                                setbit(nbits - 1ull, false); // sign = 0
935,334✔
1295
                                setbit(0ull, false); // bit0 = 0
935,334✔
1296
                        }
1297
                        else {
1298
                                // maximum positive value has this bit pattern: 0-11...10-111...111, that is, sign = 0, e = 11..10, f = 111...111
1299
                                clear();
419✔
1300
                                flip();
419✔
1301
                                setbit(fbits, false); // set least significant exponent bit to 0
419✔
1302
                                setbit(nbits - 1ull, false); // set sign to 0
419✔
1303
                        }
1304
                }
1305
                else {
1306
                        // the Inf encoding is the pattern 0b0'11...11'11...10 for a +inf, and 0b1'11...11'11...110 for a -inf
1307
                        // the maxpos is the encoding before that
1308
                        if constexpr (hasMaxExpValues) {
1309
                                // maximum positive value has this bit pattern: 0-1...1-111...101, that is, sign = 0, e = 11..11, f = 111...101
1310
                                clear();
113✔
1311
                                flip();
113✔
1312
                                setbit(nbits - 1ull, false); // sign = 0
113✔
1313
                                setbit(1ull, false); // bit1 = 0
113✔
1314
                        }
1315
                        else {
1316
                                // maximum positive value has this bit pattern: 0-1...0-111...111, that is, sign = 0, e = 11..10, f = 111...111
1317
                                clear();
169✔
1318
                                flip();
169✔
1319
                                setbit(fbits, false); // set least significant exponent bit to 0
169✔
1320
                                setbit(nbits - 1ull, false); // set sign to 0
169✔
1321
                        }
1322
                }
1323
                return *this;
936,035✔
1324
        }
1325
        constexpr cfloat& minpos() noexcept {
13,287✔
1326
                // minpos encoding is not impacted by saturating encodings, which only affects maxpos and inf
1327
                if constexpr (hasSubnormals) {
1328
                        // minimum positive value has this bit pattern: 0-000-00...01, that is, sign = 0, e = 000, f = 00001
1329
                        clear();
327✔
1330
                        setbit(0);
327✔
1331
                }
1332
                else {
1333
                        // minimum positive value has this bit pattern: 0-001-00...0, that is, sign = 0, e = 001, f = 0000
1334
                        clear();
12,960✔
1335
                        setbit(fbits);
12,960✔
1336
                }
1337
                return *this;
13,287✔
1338
        }
1339
        constexpr cfloat& zero() noexcept {
1✔
1340
                // the zero value
1341
                clear();
1✔
1342
                return *this;
1✔
1343
        }
1344
        constexpr cfloat& minneg() noexcept {
11✔
1345
                // minneg encoding is not impacted by saturating encodings, which only affects maxpos and inf
1346
                if constexpr (hasSubnormals) {
1347
                        // minimum negative value has this bit pattern: 1-000-00...01, that is, sign = 1, e = 00, f = 00001
1348
                        clear();
8✔
1349
                        setbit(nbits - 1ull);
8✔
1350
                        setbit(0);
8✔
1351
                }
1352
                else {
1353
                        // minimum negative value has this bit pattern: 1-001-00...0, that is, sign = 1, e = 001, f = 0000
1354
                        clear();
3✔
1355
                        setbit(fbits);
3✔
1356
                        setbit(nbits - 1ull);
3✔
1357
                }
1358
                return *this;
11✔
1359
        }
1360
        constexpr cfloat& maxneg() noexcept {
935,697✔
1361
                if constexpr (isSaturating) {
1362
                        // in a saturating encoding with max-exponent values we are removing the Inf encoding pattern 0b0'11...11'11...10 for a +inf, 
1363
                        // and 0b1'11...11'11...110 for a -inf and using it as a value
1364
                        if constexpr (hasMaxExpValues) {
1365
                                // maximum negative value has this bit pattern: 1-1...1-111...110, that is, sign = 1, e = 1..1, f = 111...110
1366
                                clear();
935,240✔
1367
                                flip();
935,240✔
1368
                                setbit(0ull, false);
935,240✔
1369
                        }
1370
                        else {
1371
                                // maximum negative value has this bit pattern: 1-1...0-111...111, that is, sign = 1, e = 11..10, f = 111...111
1372
                                clear();
399✔
1373
                                flip();
399✔
1374
                                setbit(fbits, false);
399✔
1375
                        }
1376
                }
1377
                else {
1378
                        if constexpr (hasMaxExpValues) {
1379
                                // maximum negative value has this bit pattern: 1-1...1-111...101, that is, sign = 1, e = 1..1, f = 111...101
1380
                                clear();
12✔
1381
                                flip();
12✔
1382
                                setbit(1ull, false);
12✔
1383
                        }
1384
                        else {
1385
                                // maximum negative value has this bit pattern: 1-1...0-111...111, that is, sign = 1, e = 11..10, f = 111...111
1386
                                clear();
46✔
1387
                                flip();
46✔
1388
                                setbit(fbits, false);
46✔
1389
                        }
1390
                }
1391
                return *this;
935,697✔
1392
        }
1393

1394

1395
        /// <summary>
1396
        /// assign the value of the string representation to the cfloat
1397
        /// </summary>
1398
        /// <param name="stringRep">decimal scientific notation of a real number to be assigned</param>
1399
        /// <returns>reference to this cfloat</returns>
1400
        /// Clang doesn't support constexpr yet on string manipulations, so we need to make it conditional
1401
        CONSTEXPRESSION cfloat& assign(const std::string& str) noexcept {
7✔
1402
                clear();
7✔
1403
                unsigned nrChars = static_cast<unsigned>(str.size());
7✔
1404
                unsigned nrBits = 0;
7✔
1405
                unsigned nrDots = 0;
7✔
1406
                std::string bits;
7✔
1407
                if (nrChars > 2) {
7✔
1408
                        if (str[0] == '0' && str[1] == 'b') {
7✔
1409
                                for (unsigned i = 2; i < nrChars; ++i) {
211✔
1410
                                        char c = str[i];
204✔
1411
                                        switch (c) {
204✔
1412
                                        case '0':
186✔
1413
                                        case '1':
1414
                                                ++nrBits;
186✔
1415
                                                bits += c;
186✔
1416
                                                break;
186✔
1417
                                        case '.':
14✔
1418
                                                ++nrDots;
14✔
1419
                                                bits += c;
14✔
1420
                                                break;
14✔
1421
                                        case '\'':
4✔
1422
                                                // consume this delimiting character
1423
                                                break;
4✔
1424
                                        default:
×
1425
                                                std::cerr << "string contained a non-standard character: " << c << '\n';
×
1426
                                                return *this;
×
1427
                                        }
1428
                                }
1429
                        }
1430
                        else {
1431
                                std::cerr << "string must start with 0b: instead input pattern was " << str << '\n';
×
1432
                                return *this;
×
1433
                        }
1434
                }
1435
                else {
1436
                        std::cerr << "string is too short\n";
×
1437
                        return *this;
×
1438
                }
1439

1440
                if (nrBits != nbits) {
7✔
1441
                        std::cerr << "number of bits in the string is " << nrBits << " and needs to be " << nbits << '\n';
×
1442
                        return *this;
×
1443
                }
1444
                if (nrDots != 2) {
7✔
1445
                        std::cerr << "number of segment delimiters in string is " << nrDots << " and needs to be 2 for a cfloat<>\n";
×
1446
                        return *this;
×
1447
                }
1448

1449
                // assign the bits
1450
                int field{ 0 };  // three fields: sign, exponent, mantissa: fields are separated by a '.'
7✔
1451
                int nrExponentBits{ -1 };
7✔
1452
                unsigned bit = nrBits;
7✔
1453
                for (unsigned i = 0; i < bits.size(); ++i) {
207✔
1454
                        char c = bits[i];
200✔
1455
                        if (c == '.') {
200✔
1456
                                ++field;
14✔
1457
                                if (field == 2) { // just finished parsing exponent field: we can now check the number of exponent bits
14✔
1458
                                        if (nrExponentBits != es) {
7✔
1459
                                                std::cerr << "provided binary string representation does not contain " << es << " exponent bits. Found " << nrExponentBits << ". Reset to 0\n";
×
1460
                                                clear();
×
1461
                                                return *this;
×
1462
                                        }
1463
                                }
1464
                        }
1465
                        else {
1466
                                setbit(--bit, c == '1');
186✔
1467
                        }
1468
                        if (field == 1) { // exponent field
200✔
1469
                                ++nrExponentBits;
51✔
1470
                        }
1471
                }
1472
                if (field != 2) {
7✔
1473
                        std::cerr << "provided binary string did not contain three fields separated by '.': Reset to 0\n";
×
1474
                        clear();
×
1475
                        return *this;
×
1476
                }
1477
                return *this;
7✔
1478
        }
7✔
1479

1480
        // selectors
1481
        constexpr bool sign() const noexcept { return (_block[MSU] & SIGN_BIT_MASK) == SIGN_BIT_MASK; }
257,854,297✔
1482
        constexpr int  scale() const noexcept {
60,904,333✔
1483
                int e{ 0 };
60,904,333✔
1484
                if constexpr (MSU_CAPTURES_EXP) {
1485
                        e = static_cast<int>((_block[MSU] & ~SIGN_BIT_MASK) >> EXP_SHIFT);
49,256,346✔
1486
                        if (e == 0) {
49,256,346✔
1487
                                // subnormal scale is determined by fraction
1488
                                // subnormals: (-1)^s * 2^(2-2^(es-1)) * (f/2^fbits))
1489
                                e = (2l - (1l << (es - 1ull))) - 1;
6,532,963✔
1490
                                if constexpr (nbits > 2 + es) {
1491
                                        for (unsigned i = nbits - 2ull - es; i > 0; --i) {
12,592,433✔
1492
                                                if (test(i)) break;
12,487,831✔
1493
                                                --e;
6,082,006✔
1494
                                        }
1495
                                }
1496
                        }
1497
                        else {
1498
                                e -= EXP_BIAS;
42,723,383✔
1499
                        }
1500
                }
1501
                else {
1502
                        blockbinary<es, bt> ebits{};
11,648,335✔
1503
                        exponent(ebits);
11,647,987✔
1504
                        if (ebits.iszero()) {
11,647,987✔
1505
                                // subnormal scale is determined by fraction
1506
                                // subnormals: (-1)^s * 2^(2-2^(es-1)) * (f/2^fbits))
1507
                                e = (2l - (1l << (es - 1ull))) - 1;
1,599,664✔
1508
                                if constexpr (nbits > 2 + es) {
1509
                                        for (unsigned i = nbits - 2ull - es; i > 0; --i) {
3,160,473✔
1510
                                                if (test(i)) break;
3,153,351✔
1511
                                                --e;
1,560,820✔
1512
                                        }
1513
                                }
1514
                        }
1515
                        else {
1516
                                e = static_cast<int>(unsigned(ebits) - EXP_BIAS);
10,048,323✔
1517
                        }
1518
                }
1519
                return e;
60,904,333✔
1520
        }
1521
        constexpr bool isneg() const noexcept {
558✔
1522
                if (isnan()) return false;
558✔
1523
                return sign(); 
541✔
1524
        }
1525
        constexpr bool ispos() const noexcept { 
34,472✔
1526
                if (isnan()) return false;
34,472✔
1527
                return !sign(); 
34,472✔
1528
        }
1529
        constexpr bool iszero() const noexcept {
428,012,200✔
1530
                // NOTE: this is a very specific design that makes the decsion that
1531
                // for subnormal encodings found in a configuration that doesn't
1532
                // support them, we assume that these values map to 0.
1533
                if constexpr (hasSubnormals) {
1534
                        return iszeroencoding();
248,936,982✔
1535
                }
1536
                else { // all subnormals round to 0
1537
                        blockbinary<es, bt> ebits{};
179,075,657✔
1538
                        exponent(ebits);
179,075,218✔
1539
                        if (ebits.iszero()) return true; else return false;
179,075,218✔
1540
                }
1541
        }
1542
        constexpr bool isone() const noexcept {
1543
                // unbiased exponent = scale = 0, fraction = 0
1544
                int s = scale();
1545
                if (s == 0) {
1546
                        blockbinary<fbits, bt> f{};
1547
                        fraction(f);
1548
                        return f.iszero();
1549
                }
1550
                return false;
1551
        }
1552
        constexpr bool isinf(int InfType = INF_TYPE_EITHER) const noexcept {
379,799,275✔
1553
                // the bit pattern encoding of Inf is independent of the max-exponent value configuration
1554
                bool isNegInf = false;
379,799,275✔
1555
                bool isPosInf = false;
379,799,275✔
1556
                if constexpr (0 == nrBlocks) {
1557
                        return false;
1558
                }
1559
                else if constexpr (1 == nrBlocks) {
1560
                        isNegInf = (_block[MSU] & MSU_MASK) == (MSU_MASK ^ LSB_BIT_MASK);
167,943,134✔
1561
                        isPosInf = (_block[MSU] & MSU_MASK) == ((MSU_MASK ^ SIGN_BIT_MASK) ^ LSB_BIT_MASK);
167,943,134✔
1562
                }
1563
                else if constexpr (2 == nrBlocks) {
1564
                        bool isInf = (_block[0] == (BLOCK_MASK ^ LSB_BIT_MASK));
145,263,770✔
1565
                        isNegInf = isInf && ((_block[MSU] & MSU_MASK) == MSU_MASK);
145,263,770✔
1566
                        isPosInf = isInf && (_block[MSU] & MSU_MASK) == (MSU_MASK ^ SIGN_BIT_MASK);
145,263,770✔
1567
                }
1568
                else if constexpr (3 == nrBlocks) {
1569
                        bool isInf = (_block[0] == (BLOCK_MASK ^ LSB_BIT_MASK)) && (_block[1] == BLOCK_MASK);
66,400,024✔
1570
                        isNegInf = isInf && ((_block[MSU] & MSU_MASK) == MSU_MASK);
66,400,024✔
1571
                        isPosInf = isInf && (_block[MSU] & MSU_MASK) == (MSU_MASK ^ SIGN_BIT_MASK);
66,400,024✔
1572
                }
1573
                else if constexpr (4 == nrBlocks) {
1574
                        bool isInf = (_block[0] == (BLOCK_MASK ^ LSB_BIT_MASK)) && (_block[1] == BLOCK_MASK) && (_block[2] == BLOCK_MASK);
100,247✔
1575
                        isNegInf = isInf && ((_block[MSU] & MSU_MASK) == MSU_MASK);
100,247✔
1576
                        isPosInf = isInf && (_block[MSU] & MSU_MASK) == (MSU_MASK ^ SIGN_BIT_MASK);
100,247✔
1577
                }
1578
                else {
1579
                        bool isInf = (_block[0] == (BLOCK_MASK ^ LSB_BIT_MASK));
92,100✔
1580
                        for (unsigned i = 1; i < nrBlocks - 1; ++i) {
93,635✔
1581
                                if (_block[i] != BLOCK_MASK) {
93,335✔
1582
                                        isInf = false;
91,800✔
1583
                                        break;
91,800✔
1584
                                }
1585
                        }
1586
                        isNegInf = isInf && ((_block[MSU] & MSU_MASK) == MSU_MASK);
92,100✔
1587
                        isPosInf = isInf && (_block[MSU] & MSU_MASK) == (MSU_MASK ^ SIGN_BIT_MASK);
92,100✔
1588
                }
1589

1590
                return (InfType == INF_TYPE_EITHER ? (isNegInf || isPosInf) :
436,638,535✔
1591
                        (InfType == INF_TYPE_NEGATIVE ? isNegInf :
85,259,341✔
1592
                                (InfType == INF_TYPE_POSITIVE ? isPosInf : false)));
436,639,437✔
1593
        }
56,839,260✔
1594
        constexpr bool isnan(int NaNType = NAN_TYPE_EITHER) const noexcept {
995,581,317✔
1595
                if constexpr (hasMaxExpValues) {
1596
                        return isnanencoding(NaNType);
596,655,126✔
1597
                }
1598
                else {
1599
                        if (ismaxexpvalue()) {
398,926,191✔
1600
                                // all these max-exponent encodings are NANs, except for the encoding representing INF
1601
                                bool isNaN = isinf() ? false : true;
24,515,344✔
1602
                                bool isNegNaN = isNaN && sign();
24,515,344✔
1603
                                bool isPosNaN = isNaN && !sign();
24,515,344✔
1604
                                return (NaNType == NAN_TYPE_EITHER ? (isNaN) :
27,937,075✔
1605
                                        (NaNType == NAN_TYPE_SIGNALLING ? isNegNaN :
4,556,804✔
1606
                                                (NaNType == NAN_TYPE_QUIET ? isPosNaN : false)));
26,785,490✔
1607
                        }
1608
                        else {
1609
                                return false;
374,410,847✔
1610
                        }
1611
                }
1612
        }
24,515,344✔
1613
        // iszeroencoding returns true if it finds a pure -0 or +0 pattern and returns false otherwise
1614
        constexpr bool iszeroencoding() const noexcept {
354,868,135✔
1615
                if constexpr (0 == nrBlocks) {
1616
                        return true;
1617
                }
1618
                else if constexpr (1 == nrBlocks) {
1619
                        return (_block[MSU] & ~SIGN_BIT_MASK) == 0;
134,114,865✔
1620
                }
1621
                else if constexpr (2 == nrBlocks) {
1622
                        return (_block[0] == 0) && (_block[MSU] & ~SIGN_BIT_MASK) == 0;
146,606,856✔
1623
                }
1624
                else if constexpr (3 == nrBlocks) {
1625
                        return (_block[0] == 0) && _block[1] == 0 && (_block[MSU] & ~SIGN_BIT_MASK) == 0;
73,918,371✔
1626
                }
1627
                else if constexpr (4 == nrBlocks) {
1628
                        return (_block[0] == 0) && _block[1] == 0 && _block[2] == 0 && (_block[MSU] & ~SIGN_BIT_MASK) == 0;
135,315✔
1629
                }
1630
                else {
1631
                        for (unsigned i = 0; i < nrBlocks - 1; ++i) if (_block[i] != 0) return false;
338,460✔
1632
                        return (_block[MSU] & ~SIGN_BIT_MASK) == 0;
555✔
1633
                }
1634
        }
1635
        // isminnegencoding returns true if it find the pattern 1.00.00001 and returns false otherwise
1636
        constexpr bool isminnegencoding() const noexcept {
66,947✔
1637
                if constexpr (0 == nrBlocks) {
1638
                        return false;
1639
                }
1640
                else if constexpr (1 == nrBlocks) {
1641
                        return (_block[MSU] & (SIGN_BIT_MASK | 1ul));
1642
                }
1643
                else if constexpr (2 == nrBlocks) {
1644
                        return ((_block[0] == 1ul) && (_block[1] == SIGN_BIT_MASK));
1,408✔
1645
                }
1646
                else if constexpr (3 == nrBlocks) {
1647
                        return ((_block[0] == 1ul) && (_block[1] == 0) && (_block[2] == SIGN_BIT_MASK));
65,536✔
1648
                }
1649
                else if constexpr (4 == nrBlocks) {
1650
                        return ((_block[0] == 1ul) && (_block[1] == 0) && (_block[2] == 0) && (_block[3] == SIGN_BIT_MASK));
3✔
1651
                }
1652
                else {
1653
                        if (_block[0] != 1ul) return false;
×
1654
                        for (unsigned i = 1; i < nrBlocks - 2; ++i) if (_block[i] != 0) return false;
×
1655
                        return (_block[MSU] == SIGN_BIT_MASK);
×
1656
                }
1657
        }
1658
        constexpr bool isnanencoding(int NaNType = NAN_TYPE_EITHER) const noexcept {
600,322,566✔
1659
                // the bit encoding of NaN is independent of the gradual overflow configuration
1660
                bool isNaN = true;
600,322,566✔
1661
                bool isNegNaN = false;
600,322,566✔
1662
                bool isPosNaN = false;
600,322,566✔
1663

1664
                if constexpr (0 == nrBlocks) {
1665
                        return false;
1666
                }
1667
                else if constexpr (1 == nrBlocks) {
1668
                }
1669
                else if constexpr (2 == nrBlocks) {
1670
                        isNaN = (_block[0] == BLOCK_MASK);
223,995,963✔
1671
                }
1672
                else if constexpr (3 == nrBlocks) {
1673
                        isNaN = (_block[0] == BLOCK_MASK) && (_block[1] == BLOCK_MASK);
201,348,349✔
1674
                }
1675
                else if constexpr (4 == nrBlocks) {
1676
                        isNaN = (_block[0] == BLOCK_MASK) && (_block[1] == BLOCK_MASK) && (_block[2] == BLOCK_MASK);
226,944✔
1677
                }
1678
                else {
1679
                        for (unsigned i = 0; i < nrBlocks - 1; ++i) {
270,059✔
1680
                                if (_block[i] != BLOCK_MASK) {
270,017✔
1681
                                        isNaN = false;
269,781✔
1682
                                        break;
269,781✔
1683
                                }
1684
                        }
1685
                }
1686
                isNegNaN = isNaN && ((_block[MSU] & MSU_MASK) == MSU_MASK);
600,322,566✔
1687
                isPosNaN = isNaN && (_block[MSU] & MSU_MASK) == (MSU_MASK ^ SIGN_BIT_MASK);
600,322,566✔
1688

1689
                return (NaNType == NAN_TYPE_EITHER ? (isNegNaN || isPosNaN) :
816,677,996✔
1690
                        (NaNType == NAN_TYPE_SIGNALLING ? isNegNaN :
324,385,524✔
1691
                                (NaNType == NAN_TYPE_QUIET ? isPosNaN : false)));
816,382,754✔
1692
        }
216,355,430✔
1693
        // isnormal returns true if 0 or exponent bits are not all zero or one, false otherwise
1694
        constexpr bool isnormal() const noexcept {
60,691,763✔
1695
                if (iszeroencoding()) return true; // filter out the one special case
60,691,763✔
1696
                blockbinary<es, bt> e{};
60,692,110✔
1697
                exponent(e);
60,691,762✔
1698
                return !e.iszero() && !e.all();
60,691,762✔
1699
        }
1700
        // isdenormal returns true if exponent bits are all zero, false otherwise
1701
        constexpr bool isdenormal() const noexcept {
45,172,432✔
1702
                if (iszeroencoding()) return false; // filter out the one special case
45,172,432✔
1703
                blockbinary<es, bt> e{};
45,163,653✔
1704
                exponent(e);
45,163,653✔
1705
                return e.iszero(); 
45,163,653✔
1706
        }
1707
        // ismaxexpvalue returns true if exponent bits are all one, false otherwise
1708
        constexpr bool ismaxexpvalue() const noexcept {
402,173,324✔
1709
                blockbinary<es, bt> e{};
402,174,371✔
1710
                exponent(e);
402,173,324✔
1711
                return e.all();
402,173,324✔
1712
        }
1713
        // isinteger is TBD
1714
        constexpr bool isinteger() const noexcept { return false; } // return (floor(*this) == *this) ? true : false; }
1715
        
1716
        template<typename NativeReal>
1717
        constexpr bool inrange(NativeReal v) const noexcept {
9,306,296✔
1718
                // the valid range for this cfloat includes the interval between 
1719
                // maxpos and the value that would round down to maxpos
1720
                bool bIsInRange = true;                
9,306,296✔
1721
                if (v > 0) {
9,306,296✔
1722
                        cfloat c(SpecificValue::maxpos);
4,427,047✔
1723
                        cfloat<nbits + 1, es, BlockType, hasSubnormals, hasMaxExpValues, isSaturating> d{};
8,027,370✔
1724
                        d = NativeReal(c);
4,427,047✔
1725
                        ++d;
4,427,047✔
1726
                        if (v >= NativeReal(d)) bIsInRange = false;
4,427,047✔
1727
                }
1728
                else {
1729
                        cfloat c(SpecificValue::maxneg);
4,879,249✔
1730
                        cfloat<nbits + 1, es, BlockType, hasSubnormals, hasMaxExpValues, isSaturating> d{};
8,816,662✔
1731
                        d = NativeReal(c);
4,879,249✔
1732
                        --d;
4,879,249✔
1733
                        if (v <= NativeReal(d)) bIsInRange = false;
4,879,249✔
1734
                }
1735

1736
                return bIsInRange;
9,306,296✔
1737
        }
1738
        constexpr bool test(unsigned bitIndex) const noexcept {
15,656,641✔
1739
                return at(bitIndex);
15,656,641✔
1740
        }
1741
        constexpr bool at(unsigned bitIndex) const noexcept {
1,878,289,065✔
1742
                if (bitIndex < nbits) {
1,878,289,065✔
1743
                        bt word = _block[bitIndex / bitsInBlock];
1,878,289,065✔
1744
                        bt mask = bt(1ull << (bitIndex % bitsInBlock));
1,878,289,065✔
1745
                        return (word & mask);
1,878,289,065✔
1746
                }
1747
                return false;
×
1748
        }
1749
        constexpr uint8_t nibble(unsigned n) const noexcept {
640✔
1750
                if (n < (1 + ((nbits - 1) >> 2))) {
640✔
1751
                        bt word = _block[(n * 4) / bitsInBlock];
640✔
1752
                        int nibbleIndexInWord = int(n % (bitsInBlock >> 2ull));
640✔
1753
                        bt mask = bt(0xF << (nibbleIndexInWord * 4));
640✔
1754
                        bt nibblebits = bt(mask & word);
640✔
1755
                        return uint8_t(nibblebits >> (nibbleIndexInWord * 4));
640✔
1756
                }
1757
                return 0;
×
1758
        }
1759
        constexpr bt block(unsigned b) const noexcept {
1760
                if (b < nrBlocks) {
1761
                        return _block[b];
1762
                }
1763
                return 0;
1764
        }
1765

1766
        constexpr void sign(bool& s) const {
800✔
1767
                s = sign();
800✔
1768
        }
800✔
1769
        constexpr void exponent(blockbinary<es, bt>& e) const {
805,024,271✔
1770
                e.clear();
805,024,271✔
1771
                if constexpr (0 == nrBlocks) return;
1772
                else if constexpr (1 == nrBlocks) {
1773
                        bt ebits = bt(_block[MSU] & ~SIGN_BIT_MASK);
368,556,432✔
1774
                        e.setbits(uint64_t(ebits >> EXP_SHIFT));
368,556,432✔
1775
                }
1776
                else if constexpr (nrBlocks > 1) {
1777
                        if (MSU_CAPTURES_EXP) {
1778
                                bt ebits = bt(_block[MSU] & ~SIGN_BIT_MASK);
250,085,833✔
1779
                                e.setbits(uint64_t(ebits >> ((nbits - 1ull - es) % bitsInBlock)));
250,085,833✔
1780
                        }
1781
                        else {
1782
                                for (unsigned i = 0; i < es; ++i) { e.setbit(i, at(nbits - 1ull - es + i)); }
841,048,970✔
1783
                        }
1784
                }
1785
        }
805,024,271✔
1786
        template<unsigned targetFractionBits>
1787
        constexpr blockbinary<targetFractionBits, bt>& fraction(blockbinary<targetFractionBits, bt>& f) const {
34,082✔
1788
                static_assert(targetFractionBits >= fbits, "target blockbinary is too small and can't receive all fraction bits");
1789
                f.clear();
34,082✔
1790
                if constexpr (0 == nrBlocks) return f;
1791
                else if constexpr (1 == nrBlocks) {
1792
                        bt fraction = bt(_block[MSU] & ~MSU_EXP_MASK);
999✔
1793
                        f.setbits(fraction);
999✔
1794
                }
1795
                else if constexpr (nrBlocks > 1) {
1796
                        for (unsigned i = 0; i < fbits; ++i) { f.setbit(i, at(i)); }
242,396✔
1797
                }
1798
                return f;
34,082✔
1799
        }
1800
        constexpr uint64_t fraction_ull() const {
59,636,111✔
1801
                uint64_t raw{ 0 };
59,636,111✔
1802
                if constexpr (nbits - es - 1ull < 65ull) { // no-op if precondition doesn't hold
1803
                        if constexpr (1 == nrBlocks) {
1804
                                uint64_t fbitMask = 0xFFFF'FFFF'FFFF'FFFF >> (64 - fbits);
26,585,520✔
1805
                                raw = fbitMask & uint64_t(_block[0]);
26,585,520✔
1806
                        }
1807
                        else if constexpr (2 == nrBlocks) {
1808
                                uint64_t fbitMask = 0xFFFF'FFFF'FFFF'FFFF >> (64 - fbits);
22,778,150✔
1809
                                raw = fbitMask & ((uint64_t(_block[1]) << bitsInBlock) | uint64_t(_block[0]));
22,778,150✔
1810
                        }
1811
                        else if constexpr (3 == nrBlocks) {
1812
                                uint64_t fbitMask = 0xFFFF'FFFF'FFFF'FFFF >> (64 - fbits);
10,249,633✔
1813
                                raw = fbitMask & ((uint64_t(_block[2]) << (2 * bitsInBlock)) | (uint64_t(_block[1]) << bitsInBlock) | uint64_t(_block[0]));
10,249,633✔
1814
                        }
1815
                        else if constexpr (4 == nrBlocks) {
1816
                                uint64_t fbitMask = 0xFFFF'FFFF'FFFF'FFFF >> (64 - fbits);
22,445✔
1817
                                raw = fbitMask & ((uint64_t(_block[3]) << (3 * bitsInBlock)) | (uint64_t(_block[2]) << (2 * bitsInBlock)) | (uint64_t(_block[1]) << bitsInBlock) | uint64_t(_block[0]));
22,445✔
1818
                        }
1819
                        else {
1820
                                uint64_t mask{ 1 };
363✔
1821
                                for (unsigned i = 0; i < fbits; ++i) { 
15,822✔
1822
                                        if (test(i)) {
15,459✔
1823
                                                raw |= mask;
2,190✔
1824
                                        }
1825
                                        mask <<= 1;
15,459✔
1826
                                }
1827
                        }
1828
                }
1829
                return raw;
59,636,111✔
1830
        }
1831
        // construct the significant from the encoding, returns normalization offset
1832
        constexpr unsigned significant(blockbinary<fhbits, bt>& s, bool isNormal = true) const {
23✔
1833
                unsigned shift = 0;
23✔
1834
                if (iszero()) return 0;
23✔
1835
                if constexpr (0 == nrBlocks) return 0;
1836
                else if constexpr (1 == nrBlocks) {
1837
                        bt significant = bt(_block[MSU] & ~MSU_EXP_MASK & ~SIGN_BIT_MASK);
23✔
1838
                        if (isNormal) {
23✔
1839
                                significant |= (bt(0x1ul) << fbits);
×
1840
                        }
1841
                        else {
1842
                                unsigned msb = find_msb(significant);
23✔
1843
//                                std::cout << "msb : " << msb << " : fhbits : " << fhbits << " : " << to_binary(significant, true) << std::endl;
1844
                                shift = fhbits - msb;
23✔
1845
                                significant <<= shift;
23✔
1846
                        }
1847
                        s.setbits(significant);
23✔
1848
                }
1849
                else if constexpr (nrBlocks > 1) {
1850
                        s.clear();
1851
                        // TODO: design and implement a block-oriented algorithm, this sequential algorithm is super slow
1852
                        if (isNormal) {
1853
                                s.setbit(fbits);
1854
                                for (unsigned i = 0; i < fbits; ++i) { s.setbit(i, at(i)); }
1855
                        }
1856
                        else {
1857
                                // Find the MSB of the subnormal: 
1858
                                unsigned msb = 0;
1859
                                for (unsigned i = 0; i < fbits; ++i) { // msb protected from not being assigned through iszero test at prelude of function
1860
                                        msb = fbits - 1ull - i;
1861
                                        if (test(msb)) break;
1862
                                }
1863
                                //      m-----lsb
1864
                                // h00001010101
1865
                                // 101010100000
1866
                                for (unsigned i = 0; i <= msb; ++i) {
1867
                                        s.setbit(fbits - msb + i, at(i));
1868
                                }
1869
                                shift = fhbits - msb;
1870
                        }
1871
                }
1872
                return shift;
23✔
1873
        }
1874
        template<unsigned targetbits>
1875
        constexpr void bits(blockbinary<targetbits, bt>& b) const {
640✔
1876
                unsigned upperbound = (nbits > targetbits ? targetbits : nbits);
640✔
1877
                b.clear();
640✔
1878
                for (unsigned i = 0; i < upperbound; ++i) { b.setbit(i, at(i)); }
3,840✔
1879
        }
640✔
1880

1881
        // casts to native types
1882
        int to_int() const {
59,976✔
1883
                if (isnan()) return 0;
59,976✔
1884
                if (isinf()) return sign() ? std::numeric_limits<int>::min() : std::numeric_limits<int>::max();
56,644✔
1885
                return int(to_native<float>());
50,308✔
1886
        }
1887
        long to_long() const {
1888
                if (isnan()) return 0;
1889
                if (isinf()) return sign() ? std::numeric_limits<long>::min() : std::numeric_limits<long>::max();
1890
                return long(to_native<double>());
1891
        }
1892
        long long to_long_long() const {
1✔
1893
                if (isnan()) return 0;
1✔
1894
                if (isinf()) return sign() ? std::numeric_limits<long long>::min() : std::numeric_limits<long long>::max();
1✔
1895
                return (long long)(to_native<double>());
1✔
1896
        }
1897

1898
        // transform an cfloat to a native C++ floating-point. We are using the native
1899
        // precision to compute, which means that all sub-values need to be representable 
1900
        // by the native precision.
1901
        // A more accurate approximation would require an adaptive precision algorithm
1902
        // with a final rounding step.
1903
        template<typename TargetFloat>
1904
        TargetFloat to_native() const { 
113,465,424✔
1905
                TargetFloat v{ 0.0 };
113,465,424✔
1906
                if (iszero()) {
113,465,424✔
1907
                        // the optimizer might destroy the sign
1908
                        return sign() ? -TargetFloat(0) : TargetFloat(0);
910,617✔
1909
                }
1910
                else if (isnan()) {
112,554,807✔
1911
                        v = sign() ? std::numeric_limits<TargetFloat>::signaling_NaN() : std::numeric_limits<TargetFloat>::quiet_NaN();
6,139,181✔
1912
                }
1913
                else if (isinf()) {
106,415,626✔
1914
                        v = sign() ? -std::numeric_limits<TargetFloat>::infinity() : std::numeric_limits<TargetFloat>::infinity();
144,099✔
1915
                }
1916
                else { // TODO: this approach has catastrophic cancellation when nbits is large and native target float is too small
1917
                        TargetFloat f{ 0 };
106,271,527✔
1918
                        TargetFloat fbit{ 0.5 };
106,271,527✔
1919
                        for (int i = static_cast<int>(nbits - 2ull - es); i >= 0; --i) {
1,313,984,337✔
1920
                                f += at(static_cast<unsigned>(i)) ? fbit : TargetFloat(0);
1,207,712,810✔
1921
                                fbit *= TargetFloat(0.5);
1,207,712,810✔
1922
                        }
1923
                        blockbinary<es, bt> ebits;
1924
                        exponent(ebits);
106,271,527✔
1925
                        if constexpr (hasSubnormals) {
1926
                                if (ebits.iszero()) {
66,510,132✔
1927
                                        // subnormals: (-1)^s * 2^(2-2^(es-1)) * (f/2^fbits))
1928
                                        TargetFloat exponentiation = TargetFloat(subnormal_exponent[es]); // precomputed values for 2^(2-2^(es-1))
14,672,836✔
1929
                                        v = exponentiation * f;  // f is already f/2^fbits
14,672,836✔
1930
                                        return sign() ? -v : v;
14,672,836✔
1931
                                }
1932
                        }
1933
                        else {
1934
                                if (ebits.iszero()) { // underflow to 0
39,761,395✔
1935
                                        // compiler fast float optimization might destroy the sign
1936
                                        return sign() ? -TargetFloat(0) : TargetFloat(0);
×
1937
                                }
1938
                        }
1939
                        if constexpr (hasMaxExpValues) {
1940
                                // regular: (-1)^s * 2^(e+1-2^(es-1)) * (1 + f/2^fbits))
1941
                                int exponent = static_cast<int>(unsigned(ebits) - EXP_BIAS);
52,318,478✔
1942
                                if (-64 < exponent && exponent < 64) {
52,318,478✔
1943
                                        TargetFloat exponentiation = (exponent >= 0 ? TargetFloat(1ull << exponent) : (1.0f / TargetFloat(1ull << -exponent)));
51,994,942✔
1944
                                        v = exponentiation * (TargetFloat(1.0) + f);
51,994,942✔
1945
                                }
51,994,942✔
1946
                                else {
1947
                                        double exponentiation = ipow(exponent);
323,536✔
1948
                                        v = TargetFloat(exponentiation * (1.0 + f));
323,536✔
1949
                                }
1950
                        }
1951
                        else {
1952
                                if (ebits.all()) {
39,280,213✔
1953
                                        // max-exponent values are mapped to quiet NaNs
1954
                                        v = std::numeric_limits<TargetFloat>::quiet_NaN();
×
1955
                                        return v;
×
1956
                                }
1957
                                else {
1958
                                        // regular: (-1)^s * 2^(e+1-2^(es-1)) * (1 + f/2^fbits))
1959
                                        int exponent = static_cast<int>(unsigned(ebits) - EXP_BIAS);
39,280,213✔
1960
                                        if (-64 < exponent && exponent < 64) {
39,280,213✔
1961
                                                TargetFloat exponentiation = (exponent >= 0 ? TargetFloat(1ull << exponent) : (1.0f / TargetFloat(1ull << -exponent)));
39,079,023✔
1962
                                                v = exponentiation * (TargetFloat(1.0) + f);
39,079,023✔
1963
                                        }
39,079,023✔
1964
                                        else {
1965
                                                double exponentiation = ipow(exponent);
201,190✔
1966
                                                v = TargetFloat(exponentiation * (1.0 + f));
201,190✔
1967
                                        }
1968
                                }
1969
                        }
1970
                        v = sign() ? -v : v;
91,598,691✔
1971
                }
1972
                return v;
97,881,971✔
1973
        }
1974

1975
        // convert a cfloat to a blocktriple with the fraction format 1.ffff
1976
        // we are using the same block type so that we can use block copies to move bits around.
1977
        // Since we tend to have at least two exponent bits, this will lead to
1978
        // most cfloat<->blocktriple cases being efficient as the block types are aligned.
1979
        // The relationship between the source cfloat and target blocktriple is not
1980
        // arbitrary, enforce it by setting the blocktriple fbits to the cfloat's (nbits - es - 1)
1981
        template<typename TargetBlockType = bt>
1982
        constexpr void normalize(blocktriple<fbits, BlockTripleOperator::REP, TargetBlockType>& tgt) const {
3,159✔
1983
                // test special cases
1984
                if (isnan()) {
3,159✔
1985
                        tgt.setnan();
253✔
1986
                }
1987
                else if (isinf()) {
2,906✔
1988
                        tgt.setinf();
245✔
1989
                }
1990
                else if (iszero()) {
2,661✔
1991
                        tgt.setzero();
317✔
1992
                }
1993
                else {
1994
                        tgt.setnormal(); // a blocktriple is always normalized
2,344✔
1995
                        int scale = this->scale();
2,344✔
1996
                        tgt.setsign(sign());
2,344✔
1997
                        tgt.setscale(scale);
2,344✔
1998
                        // set significant
1999
                        // we are going to unify to the format 01.ffffeeee
2000
                        // where 'f' is a fraction bit, and 'e' is an extension bit
2001
                        // so that normalize can be used to generate blocktriples for add/sub/mul/div/sqrt
2002
                        if (isnormal() || ismaxexpvalue()) {
2,344✔
2003
                                // normal encoding or max-exponent encoding (hasMaxExpValues=true):
2004
                                // significand has a hidden bit at position fbits
2005
                                if constexpr (fbits < 64) { // max 63 bits of fraction to yield 64bit of raw significant bits
2006
                                        uint64_t raw = fraction_ull();
1,675✔
2007
                                        raw |= (1ull << fbits);
1,675✔
2008
                                        tgt.setbits(raw);
1,675✔
2009
                                }
2010
                                else {
2011
                                        blockcopy(tgt);
111✔
2012
                                        tgt.setbit(fbits);
111✔
2013
                                }
2014
                        }
2015
                        else { // it is a subnormal encoding in this target cfloat
2016
                                int shift = MIN_EXP_NORMAL - scale;
558✔
2017
                                if (shift < 0) shift = 0; // guard against negative shifts
558✔
2018
                                // Stored subnormals have no hidden bit, but blocktriple REP expects a normalized 1.ffff payload.
2019
                                // Shift the encoded fraction up until the leading 1 reaches the hidden-bit position, while leaving
2020
                                // scale at the value returned by cfloat::scale(); that preserves the original real value in canonical form.
2021
                                if constexpr (fbits < 64) {
2022
                                        uint64_t raw = fraction_ull();
541✔
2023
                                        raw <<= shift;
541✔
2024
                                        raw |= (1ull << fbits);
541✔
2025
                                        tgt.setbits(raw);
541✔
2026
                                }
2027
                                else {
2028
                                        blockcopy(tgt);
17✔
2029
                                        tgt <<= shift;
17✔
2030
                                        tgt.setbit(fbits);
17✔
2031
                                }
2032
                        }
2033
                }
2034
        }
3,159✔
2035

2036
        // normalize a cfloat to a blocktriple used in add/sub, which has the form 00h.fffff
2037
        // that is 3 + fbits, the 3 extra bits are required to be able to use 2's complement 
2038
        // and capture the largest value of an addition/subtraction.
2039
        // TODO: currently abits = 2*fhbits as the worst case input argument size to
2040
        // capture the smallest normal value in aligned form. There is a faster/smaller
2041
        // implementation where the input is constrainted to just the round, guard, and sticky bits.
2042
        constexpr void normalizeAddition(blocktriple<fbits, BlockTripleOperator::ADD, bt>& tgt) const {
39,489,209✔
2043
                using BlockTripleConfiguration = blocktriple<fbits, BlockTripleOperator::ADD, bt>;
2044
                // test special cases
2045
                if (isnan()) {
39,489,209✔
2046
                        tgt.setnan();
396,494✔
2047
                }
2048
                else if (isinf()) {
39,092,715✔
2049
                        tgt.setinf();
326✔
2050
                }
2051
                else if (iszero()) {
39,092,389✔
2052
                        tgt.setzero();
325,672✔
2053
                }
2054
                else {
2055
                        tgt.setnormal(); // a blocktriple is always normalized
38,766,717✔
2056
                        int scale = this->scale();
38,766,717✔
2057
                        tgt.setsign(sign());
38,766,717✔
2058
                        tgt.setscale(scale);
38,766,717✔
2059
                        // set significant
2060
                        // we are going to unify to the format 001.ffffeeee
2061
                        // where 'f' is a fraction bit, and 'e' is an extension bit
2062
                        // so that normalize can be used to generate blocktriples for add/sub/mul/div/sqrt
2063
                        if (isnormal()) {
38,766,717✔
2064
                                if constexpr (fbits < 64 && BlockTripleConfiguration::rbits < (64 - fbits)) {
2065
                                        uint64_t raw = fraction_ull();
26,010,321✔
2066
                                        raw |= (1ull << fbits); // add the hidden bit
26,010,321✔
2067
                                        //std::cout << "normalize      : " << *this << '\n';
2068
                                        //std::cout << "significant    : " << to_binary(raw, fbits + 2) << '\n';
2069
                                        raw <<= BlockTripleConfiguration::rbits;  // rounding bits required for correct rounding
26,010,321✔
2070
                                        //std::cout << "rounding shift : " << to_binary(raw, fbits + 2 + BlockTripleConfiguration::rbits) << '\n';
2071
                                        tgt.setbits(raw);
26,010,321✔
2072
                                }
2073
                                else {
2074
                                        blockcopy(tgt);
962✔
2075
                                        tgt.setradix();
962✔
2076
                                        tgt.setbit(fbits); // add the hidden bit
962✔
2077
                                        tgt.bitShift(BlockTripleConfiguration::rbits);  // rounding bits required for correct rounding
962✔
2078
                                }
2079
                        }
2080
                        else {
2081
                                if (isdenormal()) { // it is a subnormal encoding in this target cfloat
12,755,434✔
2082
                                        if constexpr (hasSubnormals) {
2083
                                                if constexpr (BlockTripleConfiguration::rbits < (64 - fbits)) {
2084
                                                        uint64_t raw = fraction_ull();
6,463,789✔
2085
                                                        int shift = MIN_EXP_NORMAL - scale;
6,463,789✔
2086
                                                        raw <<= shift; // shift but do NOT add a hidden bit as the MSB of the subnormal is shifted in the hidden bit position
6,463,789✔
2087
                                                        raw <<= BlockTripleConfiguration::rbits;  // rounding bits required for correct rounding
6,463,789✔
2088
                                                        tgt.setbits(raw);
6,463,789✔
2089
                                                }
2090
                                                else {
2091
                                                        blockcopy(tgt);
2092
                                                        tgt.setradix();
2093
                                                        int shift = MIN_EXP_NORMAL - scale;
2094
                                                        tgt.bitShift(shift + BlockTripleConfiguration::rbits);  // rounding bits required for correct rounding
2095
                                                }
2096
                                        }
2097
                                        else {  // this cfloat has no subnormals
2098
                                                tgt.setzero(tgt.sign()); // preserve the sign
×
2099
                                        }
2100
                                }
2101
                                else {
2102
                                        // by design, a cfloat is either normal, subnormal, or max-exponent value, so this else clause is by deduction covering a max-exponent value
2103
                                        if constexpr (hasMaxExpValues) {
2104
                                                if constexpr (fbits < 64 && BlockTripleConfiguration::rbits < (64 - fbits)) {
2105
                                                        uint64_t raw = fraction_ull();
6,291,645✔
2106
                                                        raw |= (1ull << fbits); // add the hidden bit
6,291,645✔
2107
                                                        raw <<= BlockTripleConfiguration::rbits;  // rounding bits required for correct rounding
6,291,645✔
2108
                                                        tgt.setbits(raw);
6,291,645✔
2109
                                                }
2110
                                                else {
2111
                                                        blockcopy(tgt);
×
2112
                                                        tgt.setradix();
×
2113
                                                        tgt.setbit(fbits); // add the hidden bit
×
2114
                                                        tgt.bitShift(BlockTripleConfiguration::rbits);  // rounding bits required for correct rounding
×
2115
                                                }
2116
                                        }
2117
                                        else {  // this cfloat has no max-exponent values and thus this represents a nan, signalling or quiet determined by the sign
2118
                                                tgt.setnan(tgt.sign());
×
2119
                                        }                        
2120
                                }
2121
                        }
2122
                }
2123
                // tgt.setradix(radix);
2124
        }
39,489,209✔
2125

2126
        // Normalize a cfloat to a blocktriple used in mul, which has the form 0'00001.fffff
2127
        // that is 2*fbits, plus 1 overflow bit, and the radix set at <fbits>.
2128
        // The result radix will go to 2*fbits after multiplication.
2129
        constexpr void normalizeMultiplication(blocktriple<fbits, BlockTripleOperator::MUL, bt>& tgt) const {
13,924,728✔
2130
                // test special cases
2131
                if (isnan()) {
13,924,728✔
2132
                        tgt.setnan();
450,696✔
2133
                }
2134
                else if (isinf()) {
13,474,032✔
2135
                        tgt.setinf();
652✔
2136
                }
2137
                else if (iszero()) {
13,473,380✔
2138
                        tgt.setzero();
450,960✔
2139
                }
2140
                else {
2141
                        tgt.setnormal(); // a blocktriple is always normalized
13,022,420✔
2142
                        int scale = this->scale();
13,022,420✔
2143
                        tgt.setsign(sign());
13,022,420✔
2144
                        tgt.setscale(scale);
13,022,420✔
2145

2146
                        // set significant
2147
                        // we are going to unify to the format 01.ffffeeee
2148
                        // where 'f' is a fraction bit, and 'e' is an extension bit
2149
                        // so that normalize can be used to generate blocktriples for add/sub/mul/div/sqrt
2150
                        if (isnormal() || ismaxexpvalue()) {
13,022,420✔
2151
                                if constexpr (fbits < 64) { // max 63 bits of fraction to yield 64bit of raw significant bits
2152
                                        uint64_t raw = fraction_ull();
11,803,470✔
2153
                                        raw |= (1ull << fbits);
11,803,470✔
2154
                                        tgt.setbits(raw);
11,803,470✔
2155
                                }
2156
                                else {
2157
                                        blockcopy(tgt);
832✔
2158
                                        tgt.setradix();
832✔
2159
                                        tgt.setbit(fbits); // add the hidden bit
832✔
2160
                                }
2161
                        }
2162
                        else { 
2163
                                // it is a subnormal encoding in this target cfloat
2164
                                if constexpr (hasSubnormals) {
2165
                                        if constexpr (fbits < 64) {
2166
                                                uint64_t raw = fraction_ull();
1,218,118✔
2167
                                                int shift = MIN_EXP_NORMAL - scale;
1,218,118✔
2168
                                                raw <<= shift;
1,218,118✔
2169
                                                raw |= (1ull << fbits);
1,218,118✔
2170
                                                tgt.setbits(raw);
1,218,118✔
2171
                                        }
2172
                                        else {
2173
                                                blockcopy(tgt);
×
2174
                                                int shift = MIN_EXP_NORMAL - scale;
×
2175
                                                tgt.bitShift(shift);
×
2176
                                                tgt.setbit(fbits);
×
2177
                                        }
2178
                                }
2179
                                else { // this cfloat has no subnormals
2180
                                        tgt.setzero(tgt.sign()); // preserve the sign
×
2181
                                }
2182
                        }
2183
                }
2184
                tgt.setradix(fbits); // override the radix with the input scale for accurate value printing
13,924,728✔
2185
        }
13,924,728✔
2186

2187
        // normalize a cfloat to a blocktriple used in div, which has the form 0'00000'00001.fffff
2188
        // that is 3*fbits, plus 1 overflow bit, and the radix set at <fbits>.
2189
        // the result radix will go to 2*fbits after multiplication.
2190
        // TODO: needs implementation
2191
        constexpr void normalizeDivision(blocktriple<fbits, BlockTripleOperator::DIV, bt>& tgt) const {
8,900,364✔
2192
                constexpr unsigned divshift = blocktriple<fbits, BlockTripleOperator::DIV, bt>::divshift;
8,900,364✔
2193
                // test special cases
2194
                if (isnan()) {
8,900,364✔
2195
                        tgt.setnan();
38✔
2196
                }
2197
                else if (isinf()) {
8,900,326✔
2198
                        tgt.setinf();
6✔
2199
                }
2200
                else if (iszero()) {
8,900,320✔
2201
                        tgt.setzero();
44✔
2202
                }
2203
                else {
2204
                        tgt.setnormal(); // a blocktriple is always normalized
8,900,276✔
2205
                        int scale = this->scale();
8,900,276✔
2206
                        tgt.setsign(sign());
8,900,276✔
2207
                        tgt.setscale(scale);
8,900,276✔
2208
                        // set significant
2209
                        // we are going to unify to the format 01.ffffeeee
2210
                        // where 'f' is a fraction bit, and 'e' is an extension bit
2211
                        // so that normalize can be used to generate blocktriples for add/sub/mul/div/sqrt
2212
                        if (isnormal() || ismaxexpvalue()) {
8,900,276✔
2213
                                if constexpr (fbits < 64 && divshift < (64 - fbits)) {
2214
                                        uint64_t raw = fraction_ull();
7,400,315✔
2215
                                        raw |= (1ull << fbits);
7,400,315✔
2216
                                        raw <<= divshift; // shift the input value to the output radix
7,400,315✔
2217
                                        tgt.setbits(raw);
7,400,315✔
2218
                                }
2219
                                else {
2220
                                        // brute force copy of blocks
2221
                                        blockcopy(tgt);
1,054,322✔
2222
                                        tgt.setbit(fbits);
1,054,322✔
2223
                                        tgt.bitShift(divshift); // shift the input value to the output radix
1,054,322✔
2224
                                }
2225
                        }
2226
                        else { // it is a subnormal encoding in this target cfloat
2227
                                if constexpr (fbits < 64 && divshift < (64 - fbits)) {
2228
                                        uint64_t raw = fraction_ull();
445,637✔
2229
                                        int shift = MIN_EXP_NORMAL - scale;
445,637✔
2230
                                        raw <<= shift;
445,637✔
2231
                                        raw |= (1ull << fbits);
445,637✔
2232
                                        raw <<= divshift; // shift the input value to the output radix
445,637✔
2233
                                        tgt.setbits(raw);
445,637✔
2234
                                }
2235
                                else {
2236
                                        blockcopy(tgt);
2✔
2237
                                        int shift = MIN_EXP_NORMAL - scale;
2✔
2238
                                        tgt.bitShift(shift);
2✔
2239
                                        tgt.setbit(fbits);
2✔
2240
                                        tgt.bitShift(divshift); // shift the input value to the output radix
2✔
2241
                                }
2242
                        }
2243
                }
2244
                tgt.setradix(blocktriple<fbits, BlockTripleOperator::DIV, bt>::radix);
8,900,364✔
2245
        }
8,900,364✔
2246

2247
        // helper debug function
2248
        void constexprClassParameters() const noexcept {
8✔
2249
                std::cout << "-------------------------------------------------------------\n";
8✔
2250
                std::cout << "type              : " << typeid(*this).name() << '\n';
8✔
2251
                std::cout << "nbits             : " << nbits << '\n';
8✔
2252
                std::cout << "es                : " << es << std::endl;
8✔
2253
                std::cout << "hasSubnormals     : " << (hasSubnormals ? "true" : "false") << '\n';
8✔
2254
                std::cout << "hasMaxExpValues   : " << (hasMaxExpValues ? "true" : "false") << '\n';
8✔
2255
                std::cout << "isSaturating      : " << (isSaturating ? "true" : "false") << '\n';
8✔
2256
                std::cout << "ALL_ONES          : " << to_binary(ALL_ONES, 0, true) << '\n';
8✔
2257
                std::cout << "BLOCK_MASK        : " << to_binary(BLOCK_MASK, 0, true) << '\n';
8✔
2258
                std::cout << "nrBlocks          : " << nrBlocks << '\n';
8✔
2259
                std::cout << "bits in MSU       : " << bitsInMSU << '\n';
8✔
2260
                std::cout << "MSU               : " << MSU << '\n';
8✔
2261
                std::cout << "MSU MASK          : " << to_binary(MSU_MASK, 0, true) << '\n';
8✔
2262
                std::cout << "SIGN_BIT_MASK     : " << to_binary(SIGN_BIT_MASK, 0, true) << '\n';
8✔
2263
                std::cout << "LSB_BIT_MASK      : " << to_binary(LSB_BIT_MASK, 0, true) << '\n';
8✔
2264
                std::cout << "MSU CAPTURES_EXP  : " << (MSU_CAPTURES_EXP ? "yes\n" : "no\n");
8✔
2265
                std::cout << "EXP_SHIFT         : " << EXP_SHIFT << '\n';
8✔
2266
                std::cout << "MSU EXP MASK      : " << to_binary(MSU_EXP_MASK, 0, true) << '\n';
8✔
2267
                std::cout << "ALL_ONE_MASK_ES   : " << to_binary(ALL_ONES_ES) << '\n';
8✔
2268
                std::cout << "EXP_BIAS          : " << EXP_BIAS << '\n';
8✔
2269
                std::cout << "MAX_EXP           : " << MAX_EXP << '\n';
8✔
2270
                std::cout << "MIN_EXP_NORMAL    : " << MIN_EXP_NORMAL << '\n';
8✔
2271
                std::cout << "MIN_EXP_SUBNORMAL : " << MIN_EXP_SUBNORMAL << '\n';
8✔
2272
                std::cout << "fraction Blocks   : " << fBlocks << '\n';
8✔
2273
                std::cout << "bits in FSU       : " << bitsInFSU << '\n';
8✔
2274
                std::cout << "FSU               : " << FSU << '\n';
8✔
2275
                std::cout << "FSU MASK          : " << to_binary(FSU_MASK, 0, true) << '\n';
8✔
2276
                std::cout << "topfbits          : " << topfbits << '\n';
8✔
2277
                std::cout << "ALL_ONE_MASK_FR   : " << to_binary(ALL_ONES_FR) << '\n';
8✔
2278
        }
8✔
2279
        void showLimbs() const {
2280
                for (unsigned b = 0; b < nrBlocks; ++b) {
2281
                        std::cout << to_binary(_block[nrBlocks - b - 1], sizeof(bt) * 8) << ' ';
2282
                }
2283
                std::cout << '\n';
2284
        }
2285

2286
protected:
2287
        // HELPER methods
2288

2289
        /// <summary>
2290
        /// 1's complement of the encoding used to set up specific encoding patterns.
2291
        /// This is not an arithmetic operator that makes sense for floating-point numbers.
2292
        /// </summary>
2293
        /// <returns>reference to this cfloat object</returns>
2294
        constexpr cfloat& flip() noexcept { // in-place one's complement
1,871,732✔
2295
                for (unsigned i = 0; i < nrBlocks; ++i) {
7,118,187✔
2296
                        _block[i] = bt(~_block[i]);
5,246,455✔
2297
                }
2298
                _block[MSU] &= MSU_MASK; // assert precondition of properly nulled leading non-bits
1,871,732✔
2299
                return *this;
1,871,732✔
2300
        }
2301

2302
        /// <summary>
2303
        /// shift left is a bit level encoding helper for fast limb-based conversions between different cfloats
2304
        /// </summary>
2305
        /// <param name="bitsToShift"></param>
2306
        void shiftLeft(unsigned bitsToShift) {
60,275✔
2307
                if (bitsToShift == 0) return;
60,275✔
2308
                if (bitsToShift > nbits) {
60,275✔
2309
                        setzero();
×
2310
                }
2311
                if (bitsToShift >= bitsInBlock) {
60,275✔
2312
                        int blockShift = static_cast<int>(bitsToShift / bitsInBlock);
60,275✔
2313
                        for (int i = static_cast<int>(MSU); i >= blockShift; --i) {
140,585✔
2314
                                _block[i] = _block[i - blockShift];
80,310✔
2315
                        }
2316
                        for (int i = blockShift - 1; i >= 0; --i) {
341,594✔
2317
                                _block[i] = bt(0);
281,319✔
2318
                        }
2319
                        // adjust the shift
2320
                        bitsToShift -= blockShift * bitsInBlock;
60,275✔
2321
                        if (bitsToShift == 0) return;
60,275✔
2322
                }
2323
                if constexpr (MSU > 0) {
2324
                        // construct the mask for the upper bits in the block that need to move to the higher word
2325
                        bt mask = 0xFFFFFFFFFFFFFFFF << (bitsInBlock - bitsToShift);
60,229✔
2326
                        for (unsigned i = MSU; i > 0; --i) {
361,001✔
2327
                                _block[i] <<= bitsToShift;
300,772✔
2328
                                // mix in the bits from the right
2329
                                bt bits = bt(mask & _block[i - 1]);
300,772✔
2330
                                _block[i] |= (bits >> (bitsInBlock - bitsToShift));
300,772✔
2331
                        }
2332
                }
2333
                _block[0] <<= bitsToShift;
60,229✔
2334
        }
2335

2336
        // convert an unsigned integer into a cfloat
2337
        // TODO: this method does not protect against being called with a signed integer
2338
        template<typename Ty>
2339
        constexpr cfloat& convert_unsigned_integer(const Ty& rhs) noexcept {
140✔
2340
                clear();
140✔
2341
                if (0 == rhs) return *this;
140✔
2342

2343
                uint64_t raw = static_cast<uint64_t>(rhs);
137✔
2344
                int msb = static_cast<int>(find_msb(raw)) - 1; // msb > 0 due to zero test above 
137✔
2345
                int exponent = msb;
137✔
2346
                // remove the MSB as it represents the hidden bit in the cfloat representation
2347
                uint64_t hmask = ~(1ull << msb);
137✔
2348
                raw &= hmask;
137✔
2349

2350
                constexpr uint32_t sizeInBits = 8 * sizeof(Ty);
137✔
2351
                uint32_t shift = sizeInBits - exponent - 1;
137✔
2352
                raw <<= shift;
137✔
2353
                raw = round<sizeInBits, uint64_t>(raw, exponent);
137✔
2354

2355
                // construct the target cfloat
2356
                if constexpr (fbits < (64 - es)) {
2357
                        uint64_t biasedExponent = static_cast<uint64_t>(static_cast<long long>(exponent) + static_cast<long long>(EXP_BIAS));
107✔
2358
                        uint64_t bits = 0;
107✔
2359
                        bits <<= es;
107✔
2360
                        bits |= biasedExponent;
107✔
2361
                        bits <<= fbits;
107✔
2362
                        bits |= raw;
107✔
2363
                        setbits(bits);
107✔
2364
                }
2365
                else {
2366
                        setsign(false);
30✔
2367
                        setexponent(exponent);
30✔
2368
                        // For large types, place fraction bits at the TOP of the fraction field
2369
                        // After shift, raw has fraction bits at positions (sizeInBits-2) down to (sizeInBits-1-exponent)
2370
                        // We need to place them at positions (fbits-1) down to (fbits-exponent)
2371
                        for (int i = 0; i < exponent; ++i) {
291✔
2372
                                bool bit = (raw >> (sizeInBits - 2 - i)) & 1;
261✔
2373
                                setbit(static_cast<unsigned>(fbits - 1 - i), bit);
261✔
2374
                        }
2375
                }
2376
                return *this;
137✔
2377
        }
2378
        // convert a signed integer into a cfloat
2379
        // TODO: this method does not protect against being called with a signed integer
2380
        template<typename Ty>
2381
        constexpr cfloat& convert_signed_integer(const Ty& rhs) noexcept {
174,972✔
2382
                clear();
174,972✔
2383
                if (0 == rhs) return *this;
174,972✔
2384
                bool s = (rhs < 0);
33,377✔
2385
                using UnsignedTy = std::make_unsigned_t<Ty>;
2386
                UnsignedTy urhs = static_cast<UnsignedTy>(rhs);
33,377✔
2387
                uint64_t raw = static_cast<uint64_t>(s ? (UnsignedTy(0) - urhs) : urhs);
33,377✔
2388

2389
                int msb = static_cast<int>(find_msb(raw)) - 1; // msb > 0 due to zero test above 
33,377✔
2390
                int exponent = msb;
33,377✔
2391
                // remove the MSB as it represents the hidden bit in the cfloat representation
2392
                uint64_t hmask = ~(1ull << msb);
33,377✔
2393
                raw &= hmask;
33,377✔
2394

2395
                // shift the msb to the msb of the fraction
2396
                constexpr uint32_t sizeInBits = 8 * sizeof(Ty);
33,377✔
2397
                uint32_t shift = sizeInBits - exponent - 1;
33,377✔
2398
                raw <<= shift;
33,377✔
2399
                raw = round<sizeInBits, uint64_t>(raw, exponent);
33,377✔
2400

2401
                // construct the target cfloat
2402
                if constexpr (fbits < (64 - es)) {
2403
                        uint64_t biasedExponent = static_cast<uint64_t>(static_cast<long long>(exponent) + static_cast<long long>(EXP_BIAS));
33,006✔
2404
                        uint64_t bits = (s ? 1ull : 0ull);
33,006✔
2405
                        bits <<= es;
33,006✔
2406
                        bits |= biasedExponent;
33,006✔
2407
                        bits <<= fbits;
33,006✔
2408
                        bits |= raw;
33,006✔
2409
                        setbits(bits);
33,006✔
2410
                }
2411
                else {
2412
                        setsign(s);
371✔
2413
                        setexponent(exponent);
371✔
2414
                        // For large types, place fraction bits at the TOP of the fraction field
2415
                        // After shift, raw has fraction bits at positions (sizeInBits-2) down to (sizeInBits-1-exponent)
2416
                        // We need to place them at positions (fbits-1) down to (fbits-exponent)
2417
                        for (int i = 0; i < exponent; ++i) {
3,120✔
2418
                                bool bit = (raw >> (sizeInBits - 2 - i)) & 1;
2,749✔
2419
                                setbit(static_cast<unsigned>(fbits - 1 - i), bit);
2,749✔
2420
                        }
2421
                }
2422
                return *this;
33,377✔
2423
        }
2424

2425
public:
2426
        template<typename Real>
2427
        CONSTEXPRESSION cfloat& convert_ieee754(Real rhs) noexcept {
138,249,809✔
2428
                if constexpr (nbits == 32 && es == 8 && sizeof(Real) == 4) {
2429
                        // we CANNOT use the native conversion to float as cfloats have max-exponent values
2430
                        // which IEEE-754 does not have and thus a native conversion would destroy
2431
                        // only if the Real type is a float can we use the direct conversion
2432

2433
                        // when our cfloat is a perfect match to single precision IEEE-754
2434
                        bool s{ false };
65,749✔
2435
                        uint64_t rawExponent{ 0 };
65,749✔
2436
                        uint64_t rawFraction{ 0 };
65,749✔
2437
                        uint64_t bits{ 0 };
65,749✔
2438
                        extractFields(rhs, s, rawExponent, rawFraction, bits);
65,749✔
2439
                        if (rawExponent == ieee754_parameter<Real>::eallset) { // nan and inf need to be remapped
65,749✔
2440
                                if (rawFraction == (ieee754_parameter<Real>::fmask & ieee754_parameter<Real>::snanmask) ||
24✔
2441
                                        rawFraction == (ieee754_parameter<Real>::fmask & (ieee754_parameter<Real>::qnanmask | ieee754_parameter<Real>::snanmask))) {
18✔
2442
                                        // 1.11111111.00000000.......00000001 signalling nan
2443
                                        // 0.11111111.00000000000000000000001 signalling nan
2444
                                        // MSVC
2445
                                        // 1.11111111.10000000.......00000001 signalling nan
2446
                                        // 0.11111111.10000000.......00000001 signalling nan
2447
                                        setnan(NAN_TYPE_SIGNALLING);
6✔
2448
                                        //setsign(s);  a cfloat encodes a signalling nan with sign = 1, and a quiet nan with sign = 0
2449
                                        return *this;
6✔
2450
                                }
2451
                                if (rawFraction == (ieee754_parameter<Real>::fmask & ieee754_parameter<Real>::qnanmask)) {
18✔
2452
                                        // 1.11111111.10000000.......00000000 quiet nan
2453
                                        // 0.11111111.10000000.......00000000 quiet nan
2454
                                        setnan(NAN_TYPE_QUIET);
5✔
2455
                                        //setsign(s);  a cfloat encodes a signalling nan with sign = 1, and a quiet nan with sign = 0
2456
                                        return *this;
5✔
2457
                                }
2458
                                if (rawFraction == 0ull) {
13✔
2459
                                        // 1.11111111.0000000.......000000000 -inf
2460
                                        // 0.11111111.0000000.......000000000 +inf
2461
                                        setinf(s);
13✔
2462
                                        return *this;
13✔
2463
                                }
2464
                        }
2465
                        uint64_t raw{ s ? 1ull : 0ull };
65,725✔
2466
                        raw <<= 31;
65,725✔
2467
                        raw |= (rawExponent << fbits);
65,725✔
2468
                        raw |= rawFraction;
65,725✔
2469
                        setbits(raw);
65,725✔
2470
                        return *this;
65,725✔
2471
                }
2472
                else if constexpr (nbits == 64 && es == 11 && sizeof(Real) == 8) {
2473
                        // when our cfloat is a perfect match to double precision IEEE-754
2474
                        bool s{ false };
12,582✔
2475
                        uint64_t rawExponent{ 0 };
12,582✔
2476
                        uint64_t rawFraction{ 0 };
12,582✔
2477
                        uint64_t bits{ 0 };
12,582✔
2478
                        extractFields(rhs, s, rawExponent, rawFraction, bits);
12,582✔
2479
                        if (rawExponent == ieee754_parameter<Real>::eallset) { // nan and inf need to be remapped
12,582✔
2480
                                if (rawFraction == (ieee754_parameter<Real>::fmask & ieee754_parameter<Real>::snanmask) ||
23✔
2481
                                        rawFraction == (ieee754_parameter<Real>::fmask & (ieee754_parameter<Real>::qnanmask | ieee754_parameter<Real>::snanmask))) {
16✔
2482
                                        // 1.11111111.00000000.......00000001 signalling nan
2483
                                        // 0.11111111.00000000000000000000001 signalling nan
2484
                                        // MSVC
2485
                                        // 1.11111111.10000000.......00000001 signalling nan
2486
                                        // 0.11111111.10000000.......00000001 signalling nan
2487
                                        setnan(NAN_TYPE_SIGNALLING);
7✔
2488
                                        //setsign(s);  a cfloat encodes a signalling nan with sign = 1, and a quiet nan with sign = 0
2489
                                        return *this;
7✔
2490
                                }
2491
                                if (rawFraction == (ieee754_parameter<Real>::fmask & ieee754_parameter<Real>::qnanmask)) {
16✔
2492
                                        // 1.11111111.10000000.......00000000 quiet nan
2493
                                        // 0.11111111.10000000.......00000000 quiet nan
2494
                                        setnan(NAN_TYPE_QUIET);
7✔
2495
                                        //setsign(s);  a cfloat encodes a signalling nan with sign = 1, and a quiet nan with sign = 0
2496
                                        return *this;
7✔
2497
                                }
2498
                                if (rawFraction == 0ull) {
9✔
2499
                                        // 1.11111111.0000000.......000000000 -inf
2500
                                        // 0.11111111.0000000.......000000000 +inf
2501
                                        setinf(s);
9✔
2502
                                        return *this;
9✔
2503
                                }
2504
                        }
2505
                        // normal and subnormal handling
2506
                        uint64_t raw{ s ? 1ull : 0ull };
12,559✔
2507
                        raw <<= 63;
12,559✔
2508
                        raw |= (rawExponent << fbits);
12,559✔
2509
                        raw |= rawFraction;
12,559✔
2510
                        setbits(raw);
12,559✔
2511
                        return *this;
12,559✔
2512
                }
2513
                else {
2514
                        clear();
138,171,478✔
2515
                        // extract raw IEEE-754 bits
2516
                        bool s{ false };
138,171,478✔
2517
                        uint64_t rawExponent{ 0 };
138,171,478✔
2518
                        uint64_t rawFraction{ 0 };
138,171,478✔
2519
                        uint64_t bits{ 0 };
138,171,478✔
2520
                        extractFields(rhs, s, rawExponent, rawFraction, bits);
138,171,478✔
2521
                        // special case handling
2522
                        if (rawExponent == ieee754_parameter<Real>::eallset) { // nan and inf
138,171,478✔
2523
                                if (rawFraction == (ieee754_parameter<Real>::fmask & ieee754_parameter<Real>::snanmask) ||
5,101,576✔
2524
                                        rawFraction == (ieee754_parameter<Real>::fmask & (ieee754_parameter<Real>::qnanmask | ieee754_parameter<Real>::snanmask))) {
2,585,891✔
2525
                                        // 1.11111111.00000000.......00000001 signalling nan
2526
                                        // 0.11111111.00000000000000000000001 signalling nan
2527
                                        // MSVC
2528
                                        // 1.11111111.10000000.......00000001 signalling nan
2529
                                        // 0.11111111.10000000.......00000001 signalling nan
2530
                                        setnan(NAN_TYPE_SIGNALLING);
2,521,229✔
2531
                                        //setsign(s);  a cfloat encodes a signalling nan with sign = 1, and a quiet nan with sign = 0
2532
                                        return *this;
2,521,229✔
2533
                                }
2534
                                if (rawFraction == (ieee754_parameter<Real>::fmask & ieee754_parameter<Real>::qnanmask)) {
2,580,347✔
2535
                                        // 1.11111111.10000000.......00000000 quiet nan
2536
                                        // 0.11111111.10000000.......00000000 quiet nan
2537
                                        setnan(NAN_TYPE_QUIET);
2,551,507✔
2538
                                        //setsign(s);  a cfloat encodes a signalling nan with sign = 1, and a quiet nan with sign = 0
2539
                                        return *this;
2,551,507✔
2540
                                }
2541
                                if (rawFraction == 0ull) {
28,840✔
2542
                                        // 1.11111111.0000000.......000000000 -inf
2543
                                        // 0.11111111.0000000.......000000000 +inf
2544
                                        setinf(s);
28,816✔
2545
                                        return *this;
28,816✔
2546
                                }
2547
                        }
2548
                        if (rhs == 0.0) { // IEEE rule: this is valid for + and - 0.0
133,069,926✔
2549
                                setbit(nbits - 1ull, s);
597,512✔
2550
                                return *this;
597,512✔
2551
                        }
2552
        
2553
                        // normal number consists of fbits fraction bits and one hidden bit
2554
                        // subnormal number has no hidden bit
2555
                        int exponent = static_cast<int>(rawExponent) - ieee754_parameter<Real>::bias;  // unbias the exponent
132,472,414✔
2556

2557
                        // normalize subnormal source values so downstream code can treat them as normal
2558
                        // A subnormal has rawExponent == 0 and value = 0.fraction * 2^(1-bias).
2559
                        // We find the MSB of the fraction, shift it into the hidden bit position,
2560
                        // mask it off (like a normal's implicit 1), and adjust the exponent.
2561
                        // Setting rawExponent = 1 causes all downstream if(rawExponent != 0) branches
2562
                        // to take the normal-number path, which already handles normal-to-normal and
2563
                        // normal-to-subnormal target conversions correctly.
2564
                        if (rawExponent == 0 && rawFraction != 0) {
132,472,414✔
2565
                                exponent = 1 - ieee754_parameter<Real>::bias; // effective exponent for subnormals
1,210✔
2566
                                unsigned msb_pos = find_msb(rawFraction); // 1-indexed position of MSB
1,210✔
2567
                                unsigned normalizeShift = static_cast<unsigned>(ieee754_parameter<Real>::fbits) + 1u - msb_pos;
1,210✔
2568
                                rawFraction = (rawFraction << normalizeShift) & static_cast<uint64_t>(ieee754_parameter<Real>::fmask);
1,210✔
2569
                                exponent -= static_cast<int>(normalizeShift);
1,210✔
2570
                                rawExponent = 1; // mark as normalized so downstream paths treat this as a normal number
1,210✔
2571
                        }
2572

2573
                        // check special case of
2574
                        //  1- saturating to maxpos/maxneg, or 
2575
                        //  2- projecting to +-inf 
2576
                        // if the value is out of range.
2577
                        // 
2578
                        // One problem here is that at the rounding cusps of maxpos <-> inf <-> nan
2579
                        // you need to go through the rounding logic to know which encoding you end up
2580
                        // with. 
2581
                        // For each specific cfloat configuration, you can work out these rounding cusps
2582
                        // but they need to go through the value transformation to map them back to native
2583
                        // IEEE-754. That is a complex computation to do as a static constexpr as you need
2584
                        // to construct the value, then evaluate it, and store it.
2585
                        // 
2586
                        // The algorithm used here is to check for the obvious out of range values by
2587
                        // comparing their scale to the max scale this cfloat encoding can represent.
2588
                        // For the rounding cusps, we go through the rounding logic, and then clean up
2589
                        // after rounding using the observation that no conversion from a value can ever
2590
                        // yield the NaN encoding.
2591
                        //
2592
                        // The rounding logic will correctly sort between maxpos and inf, and we clean
2593
                        // up any NaN encodings by projecting back to the configuration's saturation rule.
2594
                        //
2595
                        // We could improve on this by creating the database of rounding cusps and
2596
                        // referencing them with a direct value comparison with the input. That would be
2597
                        // the most performant implementation.
2598
                        if (exponent > MAX_EXP) {
132,472,414✔
2599
                                if constexpr (isSaturating) {
2600
                                        if (s) this->maxneg(); else this->maxpos(); // saturate to maxpos or maxneg
934,376✔
2601
                                }
2602
                                else {
2603
                                        setinf(s);
969,448✔
2604
                                }
2605
                                return *this;
1,903,824✔
2606
                        }
2607
                        if constexpr (hasSubnormals) {
2608
                                if (exponent < MIN_EXP_SUBNORMAL - 1) { 
88,959,921✔
2609
                                        // map to +-0 any values that have a scale less than (MIN_EXP_SUBMORNAL - 1)
2610
                                        this->setbit(nbits - 1, s);
145,391✔
2611
                                        return *this;
145,391✔
2612
                                }
2613
                        }
2614
                        else {
2615
                                if (exponent < MIN_EXP_NORMAL - 1) {
41,608,669✔
2616
                                        // map to +-0 any values that have a scale less than (MIN_EXP_MORNAL - 1)
2617
                                        this->setbit(nbits - 1, s);
270,877✔
2618
                                        return *this;
270,877✔
2619
                                }
2620
                        }
2621

2622
                        /////////////////  
2623
                        /// end of special case processing, move on to value sampling and rounding
2624

2625
#if TRACE_CONVERSION
2626
                        std::cout << '\n';
2627
                        std::cout << "value             : " << rhs << '\n';
2628
                        std::cout << "segments          : " << to_binary(rhs) << '\n';
2629
                        std::cout << "sign     bit      : " << (s ? '1' : '0') << '\n';
2630
                        std::cout << "exponent bits     : " << to_binary(rawExponent, ieee754_parameter<Real>::ebits, true) << '\n';
2631
                        std::cout << "fraction bits     : " << to_binary(rawFraction, ieee754_parameter<Real>::fbits, true) << '\n';
2632
                        std::cout << "exponent value    : " << exponent << '\n';
2633
#endif
2634

2635
                        // do the following scenarios have different rounding bits?
2636
                        // input is normal, cfloat is normal           <-- rounding can happen with native ieee-754 bits
2637
                        // input is normal, cfloat is subnormal
2638
                        // input is subnormal, cfloat is normal
2639
                        // input is subnormal, cfloat is subnormal
2640

2641
                        // The first condition is the relationship between the number 
2642
                        // of fraction bits from the source and the number of fraction bits 
2643
                        // in the target cfloat: these are constexpressions and guard the shifts
2644
                        // input fbits >= cfloat fbits                 <-- need to round
2645
                        // input fbits < cfloat fbits                  <-- no need to round
2646

2647
                        // quick check if we are truncating to 0 for all subnormal values
2648
                        if constexpr (!hasSubnormals) {
2649
                                if (exponent < MIN_EXP_NORMAL) {
41,337,792✔
2650
                                        setsign(s); // rest of the bits, exponent and fraction, are already set correctly
123,584✔
2651
                                        return *this;
123,584✔
2652
                                }
2653
                        }
2654
                        if constexpr (fbits < ieee754_parameter<Real>::fbits) {
2655
                                // this is the common case for cfloats that are smaller in size compared to single and double precision IEEE-754
2656
                                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,769,241✔
2657
                                uint32_t biasedExponent{ 0 };
129,769,241✔
2658
                                int adjustment{ 0 }; // right shift adjustment for subnormal representation
129,769,241✔
2659
                                uint64_t mask;
2660
                                if (rawExponent != 0) {
129,769,241✔
2661
                                        // the source real is a normal number, 
2662
//                                        if (exponent >= (MIN_EXP_SUBNORMAL - 1) && exponent < MIN_EXP_NORMAL) {
2663
                                        if (exponent < MIN_EXP_NORMAL) {
129,769,241✔
2664
//                                                exponent = (exponent < MIN_EXP_SUBNORMAL ? MIN_EXP_SUBNORMAL : exponent); // clip to the smallest subnormal exponent, otherwise the adjustment is off
2665
                                                // the value is a subnormal number in this representation: biasedExponent = 0
2666
                                                // add the hidden bit to the fraction bits so the denormalization has the correct MSB
2667
                                                rawFraction |= ieee754_parameter<Real>::hmask;
41,271,953✔
2668

2669
                                                // fraction processing: we have 1 hidden + 23 explicit fraction bits 
2670
                                                // f = 1.ffff 2^exponent * 2^fbits * 2^-(2-2^(es-1)) = 1.ff...ff >> (23 - (-exponent + fbits - (2 -2^(es-1))))
2671
                                                // -exponent because we are right shifting and exponent in this range is negative
2672
                                                adjustment = -(exponent + subnormal_reciprocal_shift[es]);
41,271,953✔
2673
                                                // this is the right shift adjustment required for subnormal representation due 
2674
                                                // to the scale of the input number, i.e. the exponent of 2^-adjustment
2675
                                        }
2676
                                        else {
2677
                                                // the value is a normal number in this representation: common case
2678
                                                biasedExponent = static_cast<uint32_t>(exponent + EXP_BIAS); // project the exponent into the target 
88,497,288✔
2679
                                                // fraction processing
2680
                                                // float structure is: seee'eeee'efff'ffff'ffff'ffff'ffff'ffff, s = sign, e - exponent bit, f = fraction bit
2681
                                                // target structure is for example cfloat<8,2>: seef'ffff
2682
                                                // since both are normals, we can shift the incoming fraction to the target structure bits, and round
2683
                                                // MSB of source = 23 - 1, MSB of target = fbits - 1: shift = MSB of src - MSB of tgt => 23 - fbits
2684
                                                adjustment = 0;
88,497,288✔
2685
                                        }
2686
                                        if constexpr (rightShift > 0) {
2687
                                                // if true we need to round
2688
                                                // round-to-even logic
2689
                                                //  ... lsb | guard  round sticky   round
2690
                                                //       x     0       x     x       down
2691
                                                //       0     1       0     0       down  round to even
2692
                                                //       1     1       0     0        up   round to even
2693
                                                //       x     1       0     1        up
2694
                                                //       x     1       1     0        up
2695
                                                //       x     1       1     1        up
2696
                                                // collect lsb, guard, round, and sticky bits
2697

2698

2699
#if TRACE_CONVERSION
2700
                                                std::cout << "fraction bits     : " << to_binary(rawFraction, ieee754_parameter<Real>::nbits, true) << '\n';
2701
                                                std::cout << "lsb mask bits     : " << to_binary(mask, ieee754_parameter<Real>::nbits, true) << '\n';
2702
#endif
2703
                                                mask = (1ull << (rightShift + adjustment)); // bit mask for the lsb bit
129,769,241✔
2704
                                                bool lsb = (mask & rawFraction);
129,769,241✔
2705
                                                mask >>= 1;
129,769,241✔
2706
                                                bool guard = (mask & rawFraction);
129,769,241✔
2707
                                                mask >>= 1;
129,769,241✔
2708
                                                bool round = (mask & rawFraction);
129,769,241✔
2709
                                                if ((rightShift + adjustment) > 1) {
129,769,241✔
2710
                                                        mask = (0xFFFF'FFFF'FFFF'FFFFull << (rightShift + adjustment - 2));
129,769,241✔
2711
                                                        mask = ~mask;
129,769,241✔
2712
                                                }
2713
                                                else {
2714
                                                        mask = 0;
×
2715
                                                }
2716
#if TRACE_CONVERSION
2717
                                                std::cout << "right shift       : " << rightShift << '\n';
2718
                                                std::cout << "adjustment        : " << adjustment << '\n';
2719
                                                std::cout << "shift to LSB      : " << (rightShift + adjustment) << '\n';
2720
                                                std::cout << "fraction bits     : " << to_binary(rawFraction, ieee754_parameter<Real>::nbits, true) << '\n';
2721
                                                std::cout << "sticky mask bits  : " << to_binary(mask, ieee754_parameter<Real>::nbits, true) << '\n';
2722
#endif
2723
                                                bool sticky = (mask & rawFraction);
129,769,241✔
2724
                                                rawFraction >>= (static_cast<int64_t>(rightShift) + static_cast<int64_t>(adjustment));
129,769,241✔
2725

2726
                                                // execute rounding operation
2727
                                                if (guard) {
129,769,241✔
2728
                                                        if (lsb && (!round && !sticky)) ++rawFraction; // round to even
26,454,958✔
2729
                                                        if (round || sticky) ++rawFraction;
26,454,958✔
2730
                                                        if (rawFraction == (1ull << fbits)) { // overflow
26,454,958✔
2731
                                                                if (biasedExponent == ALL_ONES_ES) { // overflow to INF == .111..01
451,134✔
2732
                                                                        rawFraction = INF_ENCODING;
570✔
2733
                                                                }
2734
                                                                else {
2735
                                                                        ++biasedExponent;
450,564✔
2736
                                                                        rawFraction = 0;
450,564✔
2737
                                                                }
2738
                                                        }
2739
                                                }
2740
#if TRACE_CONVERSION
2741
                                                std::cout << "lsb               : " << (lsb ? "1\n" : "0\n");
2742
                                                std::cout << "guard             : " << (guard ? "1\n" : "0\n");
2743
                                                std::cout << "round             : " << (round ? "1\n" : "0\n");
2744
                                                std::cout << "sticky            : " << (sticky ? "1\n" : "0\n");
2745
                                                std::cout << "rounding decision : " << (lsb && (!round && !sticky) ? "round to even\n" : "-\n");
2746
                                                std::cout << "rounding direction: " << (round || sticky ? "round up\n" : "round down\n");
2747
#endif
2748
                                        }
2749
                                        else { // all bits of the float go into this representation and need to be shifted up, no rounding necessary
2750
                                                int shiftLeft = fbits - ieee754_parameter<Real>::fbits;
2751
                                                rawFraction <<= shiftLeft;
2752
                                        }
2753
#if TRACE_CONVERSION
2754
                                        std::cout << "biased exponent   : " << biasedExponent << " : 0x" << std::hex << biasedExponent << std::dec << '\n';
2755
                                        std::cout << "right shift       : " << rightShift << '\n';
2756
                                        std::cout << "adjustment shift  : " << adjustment << '\n';
2757
                                        std::cout << "sticky bit mask   : " << to_binary(mask, 32, true) << '\n';
2758
                                        std::cout << "fraction bits     : " << to_binary(rawFraction, 32, true) << '\n';
2759
#endif
2760
                                        // construct the target cfloat
2761
                                        bits = (s ? 1ull : 0ull);
129,769,241✔
2762
                                        bits <<= es;
129,769,241✔
2763
                                        bits |= biasedExponent;
129,769,241✔
2764
                                        bits <<= fbits;
129,769,241✔
2765
                                        bits |= rawFraction;
129,769,241✔
2766
#if TRACE_CONVERSION
2767
                                        std::cout << "sign bit          : " << (s ? '1' : '0') << '\n';
2768
                                        std::cout << "biased exponent   : " << biasedExponent << " : 0x" << std::hex << biasedExponent << std::dec << '\n';
2769
                                        std::cout << "fraction bits     : " << to_binary(rawFraction, 32, true) << '\n';
2770
                                        std::cout << "cfloat bits       : " << to_binary(bits, nbits, true) << '\n';
2771
#endif
2772
                                        setbits(bits);
129,769,241✔
2773
                                }
2774
                                else {
2775
                                        // the source real is a subnormal number                                
2776
                                        mask = 0x00FF'FFFFu >> (fbits + exponent + subnormal_reciprocal_shift[es] + 1); // mask for sticky bit 
×
2777

2778
                                        // fraction processing: we have fbits+1 bits = 1 hidden + fbits explicit fraction bits 
2779
                                        // f = 1.ffff  2^exponent * 2^fbits * 2^-(2-2^(es-1)) = 1.ff...ff >> (23 - (-exponent + fbits - (2 -2^(es-1))))
2780
                                        // -exponent because we are right shifting and exponent in this range is negative
2781
                                        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
×
2782
#if TRACE_CONVERSION                                        
2783
                                        std::cout << "source is subnormal: TBD\n";
2784
                                        std::cout << "shift to LSB    " << (rightShift + adjustment) << '\n';
2785
                                        std::cout << "adjustment      " << adjustment << '\n';
2786
                                        std::cout << "exponent        " << exponent << '\n';
2787
                                        std::cout << "subnormal shift " << subnormal_reciprocal_shift[es] << '\n';
2788
#endif
2789
                                        if (exponent >= (MIN_EXP_SUBNORMAL - 1) && exponent < MIN_EXP_NORMAL) {
2790
                                                // the value is a subnormal number in this representation
2791
                                        }
2792
                                        else {
2793
                                                // the value is a normal number in this representation
2794
                                        }
2795
                                }
2796
                        }
2797
                        else {
2798
                                // no need to round, but we need to shift left to deliver the bits
2799
                                // cfloat<40,  8> = float
2800
                                // cfloat<48,  9> = float
2801
                                // cfloat<56, 10> = float
2802
                                // cfloat<64, 11> = float
2803
                                // cfloat<64, 10> = double 
2804
                                // can we go from an input subnormal to a cfloat normal? 
2805
                                // yes, for example a cfloat<64,11> assigned to a subnormal float
2806

2807
                                // map exponent into target cfloat encoding
2808
                                constexpr int upshift = fbits - ieee754_parameter<Real>::fbits;
259,497✔
2809
                                uint64_t biasedExponent{ 0 };
259,497✔
2810
                                bool subnormalInTarget = (exponent < MIN_EXP_NORMAL);
259,497✔
2811
                                int denormShift = 0;
259,497✔
2812
                                if (subnormalInTarget) {
259,497✔
2813
                                        biasedExponent = 0;
666✔
2814
                                        denormShift = MIN_EXP_NORMAL - exponent;
666✔
2815
                                }
2816
                                else {
2817
                                        biasedExponent = static_cast<uint64_t>(static_cast<int64_t>(exponent) + EXP_BIAS);
258,831✔
2818
                                }
2819
                                // output processing
2820
                                if constexpr (nbits < 65) {
2821
                                        // we can compose the bits in a native 64-bit unsigned integer
2822
                                        // common case: normal to normal
2823
                                        // reference example: nbits = 40, es = 8, fbits = 31: rhs = float fbits = 23; shift left by (31 - 23) = 8
2824

2825
                                        if (rawExponent != 0) {
199,222✔
2826
                                                // rhs is a normal encoding (or a normalized former-subnormal)
2827
                                                uint64_t raw{ s ? 1ull : 0ull };
199,222✔
2828
                                                raw <<= es;
199,222✔
2829
                                                raw |= biasedExponent;
199,222✔
2830
                                                raw <<= fbits;
199,222✔
2831
                                                if (subnormalInTarget) {
199,222✔
2832
                                                        // add the hidden bit and denormalize the fraction
2833
                                                        uint64_t significand = rawFraction | ieee754_parameter<Real>::hmask;
653✔
2834
                                                        int netShift = upshift - denormShift;
653✔
2835
                                                        if (netShift >= 0) {
653✔
2836
                                                                rawFraction = significand << netShift;
653✔
2837
                                                        }
2838
                                                        else {
2839
                                                                // right shift with round-to-nearest-even
2840
                                                                int rshift = -netShift;
×
2841
                                                                uint64_t lsbMask = (1ull << rshift);
×
2842
                                                                bool lsb    = (significand & lsbMask) != 0;
×
2843
                                                                bool guard  = (rshift >= 1) && ((significand & (lsbMask >> 1)) != 0);
×
2844
                                                                bool round  = (rshift >= 2) && ((significand & (lsbMask >> 2)) != 0);
×
2845
                                                                bool sticky = (rshift >= 3) && ((significand & ((lsbMask >> 2) - 1)) != 0);
×
2846
                                                                rawFraction = significand >> rshift;
×
2847
                                                                if (guard) {
×
2848
                                                                        if (lsb && (!round && !sticky)) ++rawFraction; // round to even
×
2849
                                                                        if (round || sticky) ++rawFraction;
×
2850
                                                                }
2851
                                                        }
2852
                                                }
2853
                                                else {
2854
                                                        rawFraction <<= upshift;
198,569✔
2855
                                                }
2856
                                                raw |= rawFraction;
199,222✔
2857
                                                setbits(raw);
199,222✔
2858
                                        }
2859
                                        else {
2860
                                                // rhs is a subnormal (unreachable after normalization above, kept for safety)
2861
                                        }
2862
                                }
2863
                                else {
2864
                                        // we need to write and shift bits into place
2865
                                        // use cases are cfloats like cfloat<80, 11, bt>
2866
                                        // even though the bits that come in are single or double precision
2867
                                        // we need to write the fields and then shifting them in place
2868
                                        // 
2869
                                        // common case: normal to normal
2870
                                        if constexpr (bitsInBlock < 64) {
2871
                                                if (rawExponent != 0) {
60,275✔
2872
                                                        // reference example: nbits = 128, es = 15, fbits = 112: rhs = float: shift left by (112 - 23) = 89
2873
                                                        setbits(biasedExponent);
60,275✔
2874
                                                        shiftLeft(fbits);
60,275✔
2875
                                                        // if subnormal in target, add hidden bit before copying
2876
                                                        uint64_t fractionToCopy = subnormalInTarget
60,275✔
2877
                                                                ? (rawFraction | ieee754_parameter<Real>::hmask)
60,275✔
2878
                                                                : rawFraction;
2879
                                                        // shift fraction bits
2880
                                                        int bitsToShift = subnormalInTarget ? (upshift - denormShift) : upshift;
60,275✔
2881
                                                        // for subnormal targets near minpos, bitsToShift can be negative (right shift)
2882
                                                        // apply rounding on the 64-bit significand before splitting into blocks
2883
                                                        if (bitsToShift < 0) {
60,275✔
2884
                                                                int rshift = -bitsToShift;
×
2885
                                                                uint64_t lsbMask = (1ull << rshift);
×
2886
                                                                bool lsb    = (fractionToCopy & lsbMask) != 0;
×
2887
                                                                bool guard  = (rshift >= 1) && ((fractionToCopy & (lsbMask >> 1)) != 0);
×
2888
                                                                bool round  = (rshift >= 2) && ((fractionToCopy & (lsbMask >> 2)) != 0);
×
2889
                                                                bool sticky = (rshift >= 3) && ((fractionToCopy & ((lsbMask >> 2) - 1)) != 0);
×
2890
                                                                fractionToCopy >>= rshift;
×
2891
                                                                if (guard) {
×
2892
                                                                        if (lsb && (!round && !sticky)) ++fractionToCopy; // round to even
×
2893
                                                                        if (round || sticky) ++fractionToCopy;
×
2894
                                                                }
2895
                                                                bitsToShift = 0;
×
2896
                                                        }
2897
                                                        bt fractionBlock[nrBlocks]{ 0 };
60,275✔
2898
                                                        // copy fraction bits
2899
                                                        unsigned blocksRequired = (8 * sizeof(fractionToCopy) + 1) / bitsInBlock;
60,275✔
2900
                                                        unsigned maxBlockNr = (blocksRequired < nrBlocks ? blocksRequired : nrBlocks);
60,275✔
2901
                                                        uint64_t mask = static_cast<uint64_t>(ALL_ONES); // set up the block mask
60,275✔
2902
                                                        unsigned shift = 0;
60,275✔
2903
                                                        for (unsigned i = 0; i < maxBlockNr; ++i) {
340,969✔
2904
                                                                fractionBlock[i] = bt((mask & fractionToCopy) >> shift);
280,694✔
2905
                                                                mask <<= bitsInBlock;
280,694✔
2906
                                                                shift += bitsInBlock;
280,694✔
2907
                                                        }
2908
                                                        if (bitsToShift >= static_cast<int>(bitsInBlock)) {
60,275✔
2909
                                                                int blockShift = static_cast<int>(bitsToShift / bitsInBlock);
50,244✔
2910
                                                                for (int i = MSU; i >= blockShift; --i) {
271,149✔
2911
                                                                        fractionBlock[i] = fractionBlock[i - blockShift];
220,905✔
2912
                                                                }
2913
                                                                for (int i = blockShift - 1; i >= 0; --i) {
160,863✔
2914
                                                                        fractionBlock[i] = bt(0);
110,619✔
2915
                                                                }
2916
                                                                // adjust the shift
2917
                                                                bitsToShift -= blockShift * bitsInBlock;
50,244✔
2918
                                                        }
2919
                                                        if (bitsToShift > 0) {
60,275✔
2920
                                                                // construct the mask for the upper bits in the block that need to move to the higher word
2921
                                                                bt bitsToMoveMask = bt(ALL_ONES << (bitsInBlock - bitsToShift));
40,251✔
2922
                                                                for (unsigned i = MSU; i > 0; --i) {
211,354✔
2923
                                                                        fractionBlock[i] <<= bitsToShift;
171,103✔
2924
                                                                        // mix in the bits from the right
2925
                                                                        bt fracbits = static_cast<bt>(bitsToMoveMask & fractionBlock[i - 1]); // operator & yields an int
171,103✔
2926
                                                                        fractionBlock[i] |= (fracbits >> (bitsInBlock - bitsToShift));
171,103✔
2927
                                                                }
2928
                                                                fractionBlock[0] <<= bitsToShift;
40,251✔
2929
                                                        }
2930
                                                        // OR the bits in
2931
                                                        for (unsigned i = 0; i <= MSU; ++i) {
421,904✔
2932
                                                                _block[i] |= fractionBlock[i];
361,629✔
2933
                                                        }
2934
                                                        // enforce precondition for fast comparison by properly nulling bits that are outside of nbits
2935
                                                        _block[MSU] &= MSU_MASK;
60,275✔
2936
                                                        // finally, set the sign bit
2937
                                                        setsign(s);
60,275✔
2938
                                                }
2939
                                                else {
2940
                                                        // rhs is a subnormal
2941
                //                                        std::cerr << "rhs is a subnormal : " << to_binary(rhs) << " : " << rhs << '\n';
2942
                                                }
2943
                                        }
2944
                                        else {
2945
                                                // BlockType is incorrect
2946
                                        }
2947
                                }
2948
                        }
2949
                        // post-processing results to implement saturation and projection after rounding logic
2950
                        if constexpr (isSaturating) {
2951
                                if (isinf(INF_TYPE_POSITIVE) || isnan(NAN_TYPE_QUIET)) {
20,168,368✔
2952
                                        maxpos();
888✔
2953
                                }
2954
                                else if (isinf(INF_TYPE_NEGATIVE) || isnan(NAN_TYPE_SIGNALLING)) {
20,167,480✔
2955
                                        maxneg();
888✔
2956
                                }
2957
                        }
2958
                        else {
2959
                                if (isnan(NAN_TYPE_QUIET)) {
109,860,370✔
2960
                                        setinf(false);
301,301✔
2961
                                }
2962
                                else if (isnan(NAN_TYPE_SIGNALLING)) {
109,559,069✔
2963
                                        setinf(true);
300,253✔
2964
                                }
2965
                        }
2966
                        return *this;  // TODO: unreachable in some configurations        
130,028,738✔
2967
                }
2968
        }
2969

2970
        // post-processing results to implement saturation and projection after rounding logic
2971
        // arithmetic bit operations can't produce NaN encodings, so we need to re-interpret
2972
        // these encodings and 'project' them to the proper values.
2973
        void constexpr post_process() noexcept {
2974
                if constexpr (isSaturating) {
2975
                        if (isinf(INF_TYPE_POSITIVE) || isnan(NAN_TYPE_QUIET)) {
2976
                                maxpos();
2977
                        }
2978
                        else if (isinf(INF_TYPE_NEGATIVE) || isnan(NAN_TYPE_SIGNALLING)) {
2979
                                maxneg();
2980
                        }
2981
                }
2982
                else {
2983
                        if (isnan(NAN_TYPE_QUIET)) {
2984
                                setinf(false);
2985
                        }
2986
                        else if (isnan(NAN_TYPE_SIGNALLING)) {
2987
                                setinf(true);
2988
                        }
2989
                }
2990
        }
2991

2992
protected:
2993

2994
        /// <summary>
2995
        /// round a set of source bits to the present representation.
2996
        /// srcbits is the number of bits of significant in the source representation
2997
        /// </summary>
2998
        /// <typeparam name="StorageType"></typeparam>
2999
        /// <param name="raw"></param>
3000
        /// <returns></returns>
3001
        template<unsigned srcbits, typename StorageType>
3002
        constexpr uint64_t round(StorageType raw, int& exponent) noexcept {
33,514✔
3003
                if constexpr (fhbits < srcbits) {
3004
                        // round to even: lsb guard round sticky
3005
                    // collect guard, round, and sticky bits
3006
                    // this same logic will work for the case where
3007
                    // we only have a guard bit and no round and sticky bits
3008
                    // because the mask logic will make round and sticky both 0
3009
                        constexpr uint32_t shift = srcbits - fhbits - 1ull;
30,530✔
3010
                        StorageType mask = (StorageType(1ull) << shift);
30,530✔
3011
                        bool guard = (mask & raw);
30,530✔
3012
                        mask >>= 1;
30,530✔
3013
                        bool round = (mask & raw);
30,530✔
3014
                        if constexpr (shift > 1u) { // protect against a negative shift
3015
                                StorageType allones(StorageType(~0));
30,530✔
3016
                                mask = StorageType(allones << (shift - 2));
30,530✔
3017
                                mask = ~mask;
30,530✔
3018
                        }
3019
                        else {
3020
                                mask = 0;
3021
                        }
3022
                        bool sticky = (mask & raw);
30,530✔
3023

3024
                        raw >>= (shift + 1);  // shift out the bits we are rounding away
30,530✔
3025
                        bool lsb = (raw & 0x1u);
30,530✔
3026
                        //  ... lsb | guard  round sticky   round
3027
                        //       x     0       x     x       down
3028
                        //       0     1       0     0       down  round to even
3029
                        //       1     1       0     0        up   round to even
3030
                        //       x     1       0     1        up
3031
                        //       x     1       1     0        up
3032
                        //       x     1       1     1        up
3033
                        if (guard) {
30,530✔
3034
                                if (lsb && (!round && !sticky)) ++raw; // round to even
3,168✔
3035
                                if (round || sticky) ++raw;
3,168✔
3036
                                if (raw == (1ull << fbits)) { // overflow
3,168✔
3037
                                        ++exponent;
3,168✔
3038
                                        raw >>= 1u;
3,168✔
3039
                                }
3040
                        }
3041
                }
3042
                else {
3043
                        // Target has more precision than source - need to left-shift to align
3044
                        // For large types where fhbits > 64, the fraction bits cannot fit in
3045
                        // a 64-bit raw after shifting. In this case, skip the shift and let
3046
                        // convert_signed/unsigned_integer place bits using setbit().
3047
                        // The caller positions fraction bits at the top of srcbits, and we
3048
                        // need to ensure they don't overflow 64 bits after our shift.
3049
                        constexpr unsigned shift = fhbits - srcbits;
2,984✔
3050
                        if constexpr (fhbits <= 64 && shift < 64) {
3051
                                raw <<= shift;
2,583✔
3052
                        }
3053
                        // else: raw stays as-is; caller will extract bits and place them
3054
                }
3055
                uint64_t significant = raw;
33,514✔
3056
                return significant;
33,514✔
3057
        }
3058

3059
        template<typename ArgumentBlockType>
3060
        constexpr void copyBits(ArgumentBlockType v) {
3061
                unsigned blocksRequired = (8 * sizeof(v) + 1 ) / bitsInBlock;
3062
                unsigned maxBlockNr = (blocksRequired < nrBlocks ? blocksRequired : nrBlocks);
3063
                bt b{ 0ul }; b = bt(~b);
3064
                ArgumentBlockType mask = ArgumentBlockType(b);
3065
                unsigned shift = 0;
3066
                for (unsigned i = 0; i < maxBlockNr; ++i) {
3067
                        _block[i] = bt((mask & v) >> shift);
3068
                        mask <<= bitsInBlock;
3069
                        shift += bitsInBlock;
3070
                }
3071
        }
3072
        void shiftLeft(int leftShift) {
3073
                if (leftShift == 0) return;
3074
                if (leftShift < 0) return shiftRight(-leftShift);
3075
                if (leftShift > long(nbits)) leftShift = nbits; // clip to max
3076
                if (leftShift >= long(bitsInBlock)) {
3077
                        int blockShift = leftShift / static_cast<int>(bitsInBlock);
3078
                        for (signed i = signed(MSU); i >= blockShift; --i) {
3079
                                _block[i] = _block[i - blockShift];
3080
                        }
3081
                        for (signed i = blockShift - 1; i >= 0; --i) {
3082
                                _block[i] = bt(0);
3083
                        }
3084
                        // adjust the shift
3085
                        leftShift -= (long)(blockShift * bitsInBlock);
3086
                        if (leftShift == 0) return;
3087
                }
3088
                // construct the mask for the upper bits in the block that need to move to the higher word
3089
//                bt mask = static_cast<bt>(0xFFFFFFFFFFFFFFFFull << (bitsInBlock - leftShift));
3090
                bt mask = ALL_ONES;
3091
                mask <<= (bitsInBlock - leftShift);
3092
                for (unsigned i = MSU; i > 0; --i) {
3093
                        _block[i] <<= leftShift;
3094
                        // mix in the bits from the right
3095
                        bt bits = static_cast<bt>(mask & _block[i - 1]);
3096
                        _block[i] |= (bits >> (bitsInBlock - leftShift));
3097
                }
3098
                _block[0] <<= leftShift;
3099
        }
3100
        void shiftRight(int rightShift) {
3101
                if (rightShift == 0) return;
3102
                if (rightShift < 0) return shiftLeft(-rightShift);
3103
                if (rightShift >= long(nbits)) {
3104
                        setzero();
3105
                        return;
3106
                }
3107
                bool signext = sign();
3108
                unsigned blockShift = 0;
3109
                if (rightShift >= long(bitsInBlock)) {
3110
                        blockShift = rightShift / bitsInBlock;
3111
                        if (MSU >= blockShift) {
3112
                                // shift by blocks
3113
                                for (unsigned i = 0; i <= MSU - blockShift; ++i) {
3114
                                        _block[i] = _block[i + blockShift];
3115
                                }
3116
                        }
3117
                        // adjust the shift
3118
                        rightShift -= (long)(blockShift * bitsInBlock);
3119
                        if (rightShift == 0) {
3120
                                // fix up the leading zeros if we have a negative number
3121
                                if (signext) {
3122
                                        // rightShift is guaranteed to be less than nbits
3123
                                        rightShift += (long)(blockShift * bitsInBlock);
3124
                                        for (unsigned i = nbits - rightShift; i < nbits; ++i) {
3125
                                                this->setbit(i);
3126
                                        }
3127
                                }
3128
                                else {
3129
                                        // clean up the blocks we have shifted clean
3130
                                        rightShift += (long)(blockShift * bitsInBlock);
3131
                                        for (unsigned i = nbits - rightShift; i < nbits; ++i) {
3132
                                                this->setbit(i, false);
3133
                                        }
3134
                                }
3135
                                return;  // shift was aligned to block boundary, no per-bit shift needed
3136
                        }
3137
                }
3138

3139
                bt mask = ALL_ONES;
3140
                mask >>= (bitsInBlock - rightShift); // this is a mask for the lower bits in the block that need to move to the lower word
3141
                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
3142
                        _block[i] >>= rightShift;
3143
                        // mix in the bits from the left
3144
                        bt bits = static_cast<bt>(mask & _block[i + 1]); // & operator returns an int
3145
                        _block[i] |= (bits << (bitsInBlock - rightShift));
3146
                }
3147
                _block[MSU] >>= rightShift;
3148

3149
                // fix up the leading zeros if we have a negative number
3150
                if (signext) {
3151
                        // bitsToShift is guaranteed to be less than nbits
3152
                        rightShift += (long)(blockShift * bitsInBlock);
3153
                        for (unsigned i = nbits - rightShift; i < nbits; ++i) {
3154
                                this->setbit(i);
3155
                        }
3156
                }
3157
                else {
3158
                        // clean up the blocks we have shifted clean
3159
                        rightShift += (long)(blockShift * bitsInBlock);
3160
                        for (unsigned i = nbits - rightShift; i < nbits; ++i) {
3161
                                this->setbit(i, false);
3162
                        }
3163
                }
3164

3165
                // enforce precondition for fast comparison by properly nulling bits that are outside of nbits
3166
                _block[MSU] &= MSU_MASK;
3167
        }
3168

3169
        // calculate the integer power 2 ^ b using exponentiation by squaring
3170
        double ipow(int exponent) const {
524,726✔
3171
                bool negative = (exponent < 0);
524,726✔
3172
                exponent = negative ? -exponent : exponent;
524,726✔
3173
                double result(1.0);
524,726✔
3174
                double base = 2.0;
524,726✔
3175
                for (;;) {
3176
                        if (exponent % 2) result *= base;
3,844,408✔
3177
                        exponent >>= 1;
3,844,408✔
3178
                        if (exponent == 0) break;
3,844,408✔
3179
                        base *= base;
3,319,682✔
3180
                }
3181
                return (negative ? (1.0 / result) : result);
524,726✔
3182
        }
3183

3184
        template<BlockTripleOperator btop, typename TargetBlockType = bt>
3185
        constexpr void blockcopy(blocktriple<fbits, btop, TargetBlockType>& tgt) const {
1,056,246✔
3186
                // brute force copy of blocks
3187
                if constexpr (1 == fBlocks) {
3188
                        tgt.setblock(0, static_cast<TargetBlockType>(_block[0] & FSU_MASK));
1,050,734✔
3189
                }
3190
                else if constexpr (2 == fBlocks) {
3191
                        tgt.setblock(0, static_cast<TargetBlockType>(_block[0]));
3,028✔
3192
                        tgt.setblock(1, static_cast<TargetBlockType>(_block[1] & FSU_MASK));
3,028✔
3193
                }
3194
                else if constexpr (3 == fBlocks) {
3195
                        tgt.setblock(0, static_cast<TargetBlockType>(_block[0]));
204✔
3196
                        tgt.setblock(1, static_cast<TargetBlockType>(_block[1]));
204✔
3197
                        tgt.setblock(2, static_cast<TargetBlockType>(_block[2] & FSU_MASK));
204✔
3198
                }
3199
                else if constexpr (4 == fBlocks) {
3200
                        tgt.setblock(0, static_cast<TargetBlockType>(_block[0]));
1,500✔
3201
                        tgt.setblock(1, static_cast<TargetBlockType>(_block[1]));
1,500✔
3202
                        tgt.setblock(2, static_cast<TargetBlockType>(_block[2]));
1,500✔
3203
                        tgt.setblock(3, static_cast<TargetBlockType>(_block[3] & FSU_MASK));
1,500✔
3204
                }
3205
                else if constexpr (5 == fBlocks) {
3206
                        tgt.setblock(0, static_cast<TargetBlockType>(_block[0]));
×
3207
                        tgt.setblock(1, static_cast<TargetBlockType>(_block[1]));
×
3208
                        tgt.setblock(2, static_cast<TargetBlockType>(_block[2]));
×
3209
                        tgt.setblock(3, static_cast<TargetBlockType>(_block[3]));
×
3210
                        tgt.setblock(4, static_cast<TargetBlockType>(_block[4] & FSU_MASK));
×
3211
                }
3212
                else if constexpr (6 == fBlocks) {
3213
                        tgt.setblock(0, static_cast<TargetBlockType>(_block[0]));
3214
                        tgt.setblock(1, static_cast<TargetBlockType>(_block[1]));
3215
                        tgt.setblock(2, static_cast<TargetBlockType>(_block[2]));
3216
                        tgt.setblock(3, static_cast<TargetBlockType>(_block[3]));
3217
                        tgt.setblock(4, static_cast<TargetBlockType>(_block[4]));
3218
                        tgt.setblock(5, static_cast<TargetBlockType>(_block[5] & FSU_MASK));
3219
                }
3220
                else if constexpr (7 == fBlocks) {
3221
                        tgt.setblock(0, static_cast<TargetBlockType>(_block[0]));
8✔
3222
                        tgt.setblock(1, static_cast<TargetBlockType>(_block[1]));
8✔
3223
                        tgt.setblock(2, static_cast<TargetBlockType>(_block[2]));
8✔
3224
                        tgt.setblock(3, static_cast<TargetBlockType>(_block[3]));
8✔
3225
                        tgt.setblock(4, static_cast<TargetBlockType>(_block[4]));
8✔
3226
                        tgt.setblock(5, static_cast<TargetBlockType>(_block[5]));
8✔
3227
                        tgt.setblock(6, static_cast<TargetBlockType>(_block[6] & FSU_MASK));
8✔
3228
                }
3229
                else if constexpr (8 == fBlocks) {
3230
                        tgt.setblock(0, static_cast<TargetBlockType>(_block[0]));
370✔
3231
                        tgt.setblock(1, static_cast<TargetBlockType>(_block[1]));
370✔
3232
                        tgt.setblock(2, static_cast<TargetBlockType>(_block[2]));
370✔
3233
                        tgt.setblock(3, static_cast<TargetBlockType>(_block[3]));
370✔
3234
                        tgt.setblock(4, static_cast<TargetBlockType>(_block[4]));
370✔
3235
                        tgt.setblock(5, static_cast<TargetBlockType>(_block[5]));
370✔
3236
                        tgt.setblock(6, static_cast<TargetBlockType>(_block[6]));
370✔
3237
                        tgt.setblock(7, static_cast<TargetBlockType>(_block[7] & FSU_MASK));
370✔
3238
                }
3239
                else {
3240
                        for (unsigned i = 0; i < FSU; ++i) {
4,127✔
3241
                                tgt.setblock(i, static_cast<TargetBlockType>(_block[i]));
3,725✔
3242
                        }
3243
                        tgt.setblock(FSU, static_cast<TargetBlockType>(_block[FSU] & FSU_MASK));
402✔
3244
                }
3245
        }
1,056,246✔
3246

3247
private:
3248
        bt _block[nrBlocks];
3249

3250
        //////////////////////////////////////////////////////////////////////////////
3251
        // friend functions
3252

3253
        // template parameters need names different from class template parameters (for gcc and clang)
3254
        template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3255
        friend std::ostream& operator<< (std::ostream& ostr, const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& r);
3256
        template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3257
        friend std::istream& operator>> (std::istream& istr, cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& r);
3258

3259
        template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3260
        friend bool operator==(const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& lhs, const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& rhs);
3261
        template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3262
        friend bool operator!=(const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& lhs, const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& rhs);
3263
        template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3264
        friend bool operator< (const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& lhs, const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& rhs);
3265
        template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3266
        friend bool operator> (const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& lhs, const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& rhs);
3267
        template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3268
        friend bool operator<=(const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& lhs, const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& rhs);
3269
        template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3270
        friend bool operator>=(const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& lhs, const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& rhs);
3271
};
3272

3273
///////////////////////////// IOSTREAM operators ///////////////////////////////////////////////
3274

3275
// convert cfloat to decimal fixpnt string, i.e. "-1234.5678"
3276
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3277
std::string to_decimal_fixpnt_string(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& value, long long precision) {
3278
        constexpr unsigned fbits = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>::fbits;
3279
        constexpr unsigned bias = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>::EXP_BIAS;
3280
        std::stringstream str;
3281
        if (value.iszero()) {
3282
                str << '0';
3283
                return str.str();
3284
        }
3285
        if (value.sign()) str << '-';
3286

3287
        // construct the discretization levels of the fraction part
3288
        support::decimal range, discretizationLevels, step;
3289
        // create the decimal range we are discretizing
3290
        range.setdigit(1);
3291
        range.shiftLeft(fbits); // the decimal range of the fraction
3292
        discretizationLevels.powerOf2(fbits); // calculate the discretization levels of this range
3293
        step = div(range, discretizationLevels);
3294
        // now construct the value of this range by adding the fraction samples
3295
        support::decimal partial, multiplier;
3296
        partial.setzero();  // if you just want the fraction
3297
        multiplier.setdigit(1);
3298
        // convert the fraction part
3299
        for (unsigned i = 0; i < fbits; ++i) {
3300
                if (value.at(i)) {
3301
                        support::add(partial, multiplier);
3302
                }
3303
                support::add(multiplier, multiplier);
3304
        }
3305
        if (value.isdenormal()) {
3306
                support::mul(partial, step);
3307
                support::decimal scale;
3308
                scale.powerOf2(bias - 1ull);
3309
                partial = support::div(partial, scale);
3310
        } 
3311
        else {
3312
                support::add(partial, multiplier); // add the hidden bit
3313
                support::mul(partial, step);
3314
                support::decimal scale;
3315
                int exponent = value.scale();
3316
                if (exponent < 0) {
3317
                        scale.powerOf2(static_cast<unsigned>(-exponent));
3318
                        partial = support::div(partial, scale);
3319
                }
3320
                else {
3321
                        scale.powerOf2(static_cast<unsigned>(exponent));
3322
                        support::mul(partial, scale);
3323
                }
3324
        }
3325

3326
        // the radix is at fbits
3327
        // The partial represents the parts in the range, so we can deduce
3328
        // the number of leading zeros by comparing to the length of range
3329
        int nrLeadingZeros = static_cast<int>(range.size()) - static_cast<int>(partial.size()) - 1;
3330
        if (nrLeadingZeros >= 0) str << "0.";
3331
        for (int i = 0; i < nrLeadingZeros; ++i) str << '0';
3332
        int digitsWritten = (nrLeadingZeros > 0) ? nrLeadingZeros : 0;
3333
        int position = static_cast<int>(partial.size()) - 1;
3334
        for (support::decimal::const_reverse_iterator rit = partial.rbegin(); rit != partial.rend(); ++rit) {
3335
                str << (int)*rit;
3336
                ++digitsWritten;
3337
                if (position == fbits) str << '.';
3338
                --position;
3339
        }
3340
        if (digitsWritten < precision) { // deal with trailing 0s
3341
                for (unsigned i = static_cast<unsigned>(digitsWritten); i < fbits; ++i) {
3342
                        str << '0';
3343
                }
3344
        }
3345

3346
        return str.str();
3347
}
3348

3349
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3350
std::string to_string(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& value, long long precision) {
3351
        constexpr unsigned fbits = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>::fbits;
3352
        std::stringstream str;
3353
        if (value.iszero()) {
3354
                str << '0';
3355
                return str.str();
3356
        }
3357
        if (value.sign()) str << '-';
3358

3359
        // denormalize the number to gain access to the most sigificant digits
3360
        // 1.ffff^e
3361
        // scale is e
3362
        // lsbScale is e - fbits
3363
        // shift to get lsb to position 2^0 = (e - fbits)
3364
        std::int64_t scale = value.scale();
3365
//        std::int64_t shift = scale + fbits; // we want the lsb at 2^0
3366
        std::int64_t lsbScale = scale - fbits;  // scale of the lsb
3367
        support::decimal partial, multiplier;
3368
        partial.setzero();
3369

3370
        multiplier.powerOf2(lsbScale);
3371

3372
        // convert the fraction bits 
3373
        for (unsigned i = 0; i < fbits; ++i) {
3374
                if (value.at(i)) {
3375
                        support::add(partial, multiplier);
3376
                }
3377
                support::add(multiplier, multiplier);
3378
        }
3379
        if (!value.isdenormal()) {
3380
                support::add(partial, multiplier); // add the hidden bit
3381
        }
3382
        str << partial;
3383
        return str.str();
3384
}
3385

3386
//////////////////////////////////////////////////////////////////////////////////////////////
3387
/// stream operators
3388

3389
// ostream output generates an ASCII format for the floating-point argument
3390
// Uses native binary-to-decimal conversion via blocktriple::to_string()
3391
// to produce exact output for all cfloat sizes without double conversion.
3392
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3393
inline std::ostream& operator<<(std::ostream& ostr, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& v) {
3,122✔
3394
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
3395
        constexpr unsigned cfbits = Cfloat::fbits;
3,122✔
3396

3397
        std::streamsize prec  = ostr.precision();
3,122✔
3398
        std::streamsize width = ostr.width();
3,122✔
3399
        std::ios_base::fmtflags ff = ostr.flags();
3,122✔
3400
        bool bFixed      = (ff & std::ios_base::fixed) == std::ios_base::fixed;
3,122✔
3401
        bool bScientific = (ff & std::ios_base::scientific) == std::ios_base::scientific;
3,122✔
3402
        bool bShowpos    = (ff & std::ios_base::showpos) != 0;
3,122✔
3403
        bool bUppercase  = (ff & std::ios_base::uppercase) != 0;
3,122✔
3404
        bool bInternal   = (ff & std::ios_base::internal) != 0;
3,122✔
3405
        bool bLeft       = (ff & std::ios_base::left) != 0;
3,122✔
3406
        char fillChar    = ostr.fill();
3,122✔
3407

3408
        if constexpr (cfbits == 0) {
3409
                // degenerate cfloat with no fraction bits: fall back to double
3410
                std::ostringstream oss;
3411
                oss.precision(prec);
3412
                if (bFixed) oss << std::fixed;
3413
                if (bScientific) oss << std::scientific;
3414
                if (bUppercase) oss << std::uppercase;
3415
                if (bShowpos) oss << std::showpos;
3416
                oss << static_cast<double>(v);
3417
                std::string s = oss.str();
3418
                if (width > 0 && s.length() < static_cast<size_t>(width)) {
3419
                        size_t pad = static_cast<size_t>(width) - s.length();
3420
                        if (bLeft) { s.append(pad, fillChar); }
3421
                        else { s.insert(0u, pad, fillChar); }
3422
                }
3423
                return ostr << s;
3424
        } else {
3425
                blocktriple<cfbits, BlockTripleOperator::REP, bt> a;
3,122✔
3426
                v.normalize(a);
3,122✔
3427
                return ostr << a.to_string(prec, width, bFixed, bScientific,
6,244✔
3428
                                           bInternal, bLeft, bShowpos, bUppercase, fillChar);
6,244✔
3429
        }
3430
}
3431

3432
// parse a cfloat from a string in either cfloat hex format (nbits.esxHEXVALUEc)
3433
// or a decimal floating-point representation
3434
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3435
bool parse(const std::string& txt, cfloat<nbits,es,bt,hasSubnormals,hasMaxExpValues,isSaturating>& v) {
24✔
3436
        // check if the txt is of the native cfloat form: nbits.esX[0x]hexvaluec
3437
        std::regex cfloat_regex(R"(^[0-9]+\.[0-9]+[xX](0[xX])?[0-9A-Fa-f]+c?$)");
24✔
3438
        if (std::regex_match(txt, cfloat_regex)) {
24✔
3439
                // found a cfloat representation: parse nbits.esxHEXVALUEc
3440
                std::string nbitsStr, esStr, bitStr;
3✔
3441
                auto it = txt.begin();
3✔
3442
                for (; it != txt.end(); ++it) {
9✔
3443
                        if (*it == '.') break;
9✔
3444
                        nbitsStr.append(1, *it);
6✔
3445
                }
3446
                for (++it; it != txt.end(); ++it) {
6✔
3447
                        if (*it == 'x' || *it == 'X') break;
6✔
3448
                        esStr.append(1, *it);
3✔
3449
                }
3450
                for (++it; it != txt.end(); ++it) {
21✔
3451
                        if (*it == 'c') break;
21✔
3452
                        bitStr.append(1, *it);
18✔
3453
                }
3454
                unsigned nbits_in = 0;
3✔
3455
                unsigned es_in = 0;
3✔
3456
                {
3457
                        std::istringstream ss(nbitsStr);
3✔
3458
                        ss >> nbits_in;
3✔
3459
                        if (ss.fail()) return false;
3✔
3460
                }
3✔
3461
                {
3462
                        std::istringstream ss(esStr);
3✔
3463
                        ss >> es_in;
3✔
3464
                        if (ss.fail()) return false;
3✔
3465
                }
3✔
3466
                // native cfloat form must match target configuration
3467
                if (nbits_in != nbits || es_in != es) return false;
3✔
3468
                uint64_t raw = 0;
2✔
3469
                std::istringstream ss(bitStr);
2✔
3470
                ss >> std::hex >> raw;
2✔
3471
                if (ss.fail()) return false;
2✔
3472
                ss >> std::ws;
2✔
3473
                if (!ss.eof()) return false;
2✔
3474
                v.setbits(raw);
2✔
3475
                return true;
2✔
3476
        }
3✔
3477
        else {
3478
                // assume it is a float/double/long double representation
3479
                std::istringstream ss(txt);
21✔
3480
                double d;
3481
                ss >> d;
21✔
3482
                if (ss.fail()) return false;
21✔
3483
                ss >> std::ws;
21✔
3484
                if (!ss.eof()) return false;
21✔
3485
                v = d;
20✔
3486
                return true;
20✔
3487
        }
21✔
3488
}
24✔
3489

3490
// read an ASCII float or cfloat format: nbits.esxNN...NNc, for example: 16.5x7C00c
3491
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3492
inline std::istream& operator>>(std::istream& istr, cfloat<nbits,es,bt,hasSubnormals,hasMaxExpValues,isSaturating>& v) {
17✔
3493
        std::string txt;
17✔
3494
        istr >> txt;
17✔
3495
        if (!parse(txt, v)) {
17✔
3496
                std::cerr << "unable to parse -" << txt << "- into a cfloat value\n";
×
3497
        }
3498
        return istr;
17✔
3499
}
17✔
3500

3501
// encoding helpers
3502

3503
// return the Unit in the Last Position
3504
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3505
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> ulp(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& a) {
49✔
3506
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> b(a);
49✔
3507
        return ++b - a;
86✔
3508
}
3509

3510
// transform cfloat to a binary representation
3511
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3512
inline std::string to_binary(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& number, bool nibbleMarker = false) {
1,486✔
3513
        std::stringstream s;
1,486✔
3514
        s << "0b";
1,486✔
3515
        unsigned index = nbits;
1,486✔
3516
        s << (number.at(--index) ? '1' : '0') << '.';
1,486✔
3517

3518
        for (int i = int(es) - 1; i >= 0; --i) {
10,255✔
3519
                s << (number.at(--index) ? '1' : '0');
8,769✔
3520
                if (i > 0 && (i % 4) == 0 && nibbleMarker) s << '\'';
8,769✔
3521
        }
3522

3523
        s << '.';
1,486✔
3524

3525
        constexpr int fbits = nbits - 1ull - es;
1,486✔
3526
        for (int i = fbits - 1; i >= 0; --i) {
31,368✔
3527
                s << (number.at(--index) ? '1' : '0');
29,882✔
3528
                if (i > 0 && (i % 4) == 0 && nibbleMarker) s << '\'';
29,882✔
3529
        }
3530

3531
        return s.str();
2,972✔
3532
}
1,486✔
3533

3534
// transform a cfloat into a triple representation
3535
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3536
inline std::string to_triple(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& number, bool nibbleMarker = true) {
3✔
3537
        std::stringstream s;
3✔
3538
        blocktriple<cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>::fbits, BlockTripleOperator::REP, bt> triple;
3✔
3539
        number.normalize(triple);
3✔
3540
        s << to_triple(triple, nibbleMarker);
3✔
3541
        return s.str();
6✔
3542
}
3✔
3543

3544
// Magnitude of a cfloat (equivalent to turning the sign bit off).
3545
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3546
cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>
3547
abs(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& v) {
16,939✔
3548
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> a(v);
16,939✔
3549
        a.setsign(false);
16,939✔
3550
        return a;
16,939✔
3551
}
3552

3553
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3554
cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>
3555
fabs(cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> v) {
40✔
3556
        return abs(v);
40✔
3557
}
3558

3559
////////////////////// debug helpers
3560

3561
// convenience method to gain access to the values of the constexpr variables that govern the cfloat behavior
3562
template<unsigned nbits, unsigned es, typename bt = uint8_t, bool hasSubnormals = false, bool hasMaxExpValues = false, bool isSaturating = false>
3563
void ReportCfloatClassParameters() {
3564
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> a;
3565
        a.constexprClassParameters();
3566
}
3567

3568
//////////////////////////////////////////////////////
3569
/// cfloat - cfloat binary logic operators
3570

3571
template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3572
inline bool operator==(const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& lhs, const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& rhs) {
127,130,075✔
3573
        if (lhs.isnan() || rhs.isnan()) return false;
127,130,075✔
3574
        if (lhs.iszero() && rhs.iszero()) return true; // +0 == -0 per IEEE-754 §5.11
125,474,990✔
3575
        for (unsigned i = 0; i < lhs.nrBlocks; ++i) {
385,784,462✔
3576
                if (lhs._block[i] != rhs._block[i]) {
263,565,530✔
3577
                        return false;
2,104,956✔
3578
                }
3579
        }
3580
        return true;
122,218,932✔
3581
}
3582
template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3583
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,731,928✔
3584
template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3585
inline bool operator< (const cfloat<nnbits, nes, nbt, nsub, nsup, nsat>& lhs, const cfloat<nnbits, nes, nbt, nsub, nsup, nsat>& rhs) {
5,568,897✔
3586
        if (lhs.isnan() || rhs.isnan()) return false;
5,568,897✔
3587
        // need this as arithmetic difference is defined as snan(indeterminate)
3588
        if (lhs.isinf(INF_TYPE_NEGATIVE) && rhs.isinf(INF_TYPE_NEGATIVE)) return false;
5,453,389✔
3589
        if (lhs.isinf(INF_TYPE_POSITIVE) && rhs.isinf(INF_TYPE_POSITIVE)) return false;
5,453,375✔
3590
        if constexpr (nsub) {
3591
                cfloat<nnbits, nes, nbt, nsub, nsup, nsat> diff = (lhs - rhs);
5,384,416✔
3592
                return (!diff.iszero() && diff.sign()) ? true : false;  // got to guard against -0
5,384,416✔
3593
        }
3594
        else {
3595
                if (lhs.iszero() && rhs.iszero()) return false;  // we need to 'collapse' all zero encodings
68,926✔
3596
                if (lhs.sign() && !rhs.sign()) return true;
68,918✔
3597
                if (!lhs.sign() && rhs.sign()) return false;
51,695✔
3598
                bool positive = lhs.ispos();
34,472✔
3599
                if (positive) {
34,472✔
3600
                        if (lhs.scale() < rhs.scale()) return true;
17,252✔
3601
                        if (lhs.scale() > rhs.scale()) return false;
12,786✔
3602
                }
3603
                else {
3604
                        if (lhs.scale() > rhs.scale()) return true;
17,220✔
3605
                        if (lhs.scale() < rhs.scale()) return false;
12,770✔
3606
                }
3607
                // sign and scale are the same
3608
                if (lhs.scale() == rhs.scale()) {
16,641✔
3609
                        // compare fractions: we do not have subnormals, so we can ignore the hidden bit
3610
                        blockbinary<nnbits - 1ull - nes, nbt> l, r;
3611
                        lhs.fraction(l);
16,641✔
3612
                        rhs.fraction(r);
16,641✔
3613
                        blockbinary<nnbits - nes, nbt> ll, rr; // fbits + 1 so we can 0 extend to honor 2's complement encoding of blockbinary
3614
                        ll.assignWithoutSignExtend(l);
16,641✔
3615
                        rr.assignWithoutSignExtend(r);
16,641✔
3616
                        return (positive ? (ll < rr) : (ll > rr));
16,641✔
3617
                }
3618
                return false;
×
3619
        }
3620
}
3621
template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3622
inline bool operator> (const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& lhs, const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& rhs) { 
2,794,589✔
3623
        if (lhs.isnan() || rhs.isnan()) return false;
2,794,589✔
3624
        // need this as arithmetic difference is defined as snan(indeterminate)
3625
        if (lhs.isinf(INF_TYPE_NEGATIVE) && rhs.isinf(INF_TYPE_NEGATIVE)) return false;
2,786,475✔
3626
        if (lhs.isinf(INF_TYPE_POSITIVE) && rhs.isinf(INF_TYPE_POSITIVE)) return false;
2,786,461✔
3627
        return  operator< (rhs, lhs); 
2,786,445✔
3628
}
3629
template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3630
inline bool operator<=(const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& lhs, const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& rhs) { 
1,398,017✔
3631
        if (lhs.isnan() || rhs.isnan()) return false;
1,398,017✔
3632
        return !operator> (lhs, rhs); 
1,389,917✔
3633
}
3634
template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3635
inline bool operator>=(const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& lhs, const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& rhs) {
1,399,400✔
3636
        if (lhs.isnan() || rhs.isnan()) return false;
1,399,400✔
3637
        return !operator< (lhs, rhs); 
1,391,294✔
3638
}
3639

3640
//////////////////////////////////////////////////////
3641
/// cfloat - cfloat binary arithmetic operators
3642

3643
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3644
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) {
9,974,529✔
3645
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> sum(lhs);
9,974,529✔
3646
        sum += rhs;
9,974,529✔
3647
        return sum;
9,974,529✔
3648
}
3649
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3650
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,460,600✔
3651
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> diff(lhs);
8,460,600✔
3652
        diff -= rhs;
8,460,600✔
3653
        return diff;
8,460,600✔
3654
}
3655
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3656
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,280,467✔
3657
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> mul(lhs);
4,280,467✔
3658
        mul *= rhs;
4,280,467✔
3659
        return mul;
4,280,467✔
3660
}
3661
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3662
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,138,816✔
3663
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> ratio(lhs);
5,138,816✔
3664
        ratio /= rhs;
5,138,816✔
3665
        return ratio;
5,138,815✔
3666
}
3667

3668
/// binary cfloat - literal arithmetic operators
3669

3670
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3671
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator+(float lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3672
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> sum(lhs);
3673
        sum += rhs;
3674
        return sum;
3675
}
3676
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3677
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator-(float lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3678
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> diff(lhs);
3679
        diff -= rhs;
3680
        return diff;
3681
}
3682
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3683
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator*(float lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3684
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> mul(lhs);
3685
        mul *= rhs;
3686
        return mul;
3687
}
3688
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3689
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator/(float lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
2✔
3690
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> ratio(lhs);
2✔
3691
        ratio /= rhs;
2✔
3692
        return ratio;
2✔
3693
}
3694

3695
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3696
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator+(double lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
4✔
3697
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> sum(lhs);
4✔
3698
        sum += rhs;
4✔
3699
        return sum;
4✔
3700
}
3701
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3702
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator-(double lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3703
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> diff(lhs);
3704
        diff -= rhs;
3705
        return diff;
3706
}
3707
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3708
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator*(double lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
16✔
3709
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> mul(lhs);
16✔
3710
        mul *= rhs;
16✔
3711
        return mul;
16✔
3712
}
3713
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3714
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator/(double lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3715
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> ratio(lhs);
3716
        ratio /= rhs;
3717
        return ratio;
3718
}
3719

3720
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3721
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator+(int lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
×
3722
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> sum(lhs);
×
3723
        sum += rhs;
×
3724
        return sum;
×
3725
}
3726
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3727
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator-(int lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3728
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> diff(lhs);
3729
        diff -= rhs;
3730
        return diff;
3731
}
3732
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3733
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator*(int lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
38✔
3734
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> mul(lhs);
38✔
3735
        mul *= rhs;
38✔
3736
        return mul;
38✔
3737
}
3738
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3739
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator/(int lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
374✔
3740
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> ratio(lhs);
374✔
3741
        ratio /= rhs;
374✔
3742
        return ratio;
374✔
3743
}
3744

3745
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3746
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator+(unsigned int lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3747
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> sum(lhs);
3748
        sum += rhs;
3749
        return sum;
3750
}
3751
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3752
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator-(unsigned int lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3753
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> diff(lhs);
3754
        diff -= rhs;
3755
        return diff;
3756
}
3757
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3758
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator*(unsigned int lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3759
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> mul(lhs);
3760
        mul *= rhs;
3761
        return mul;
3762
}
3763
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3764
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator/(unsigned int lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3765
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> ratio(lhs);
3766
        ratio /= rhs;
3767
        return ratio;
3768
}
3769

3770
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3771
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator+(long long lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3772
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> sum(lhs);
3773
        sum += rhs;
3774
        return sum;
3775
}
3776
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3777
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator-(long long lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3778
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> diff(lhs);
3779
        diff -= rhs;
3780
        return diff;
3781
}
3782
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3783
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator*(long long lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3784
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> mul(lhs);
3785
        mul *= rhs;
3786
        return mul;
3787
}
3788
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3789
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator/(long long lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3790
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> ratio(lhs);
3791
        ratio /= rhs;
3792
        return ratio;
3793
}
3794

3795
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3796
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator+(unsigned long long lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3797
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> sum(lhs);
3798
        sum += rhs;
3799
        return sum;
3800
}
3801
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3802
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator-(unsigned long long lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3803
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> diff(lhs);
3804
        diff -= rhs;
3805
        return diff;
3806
}
3807
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3808
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator*(unsigned long long lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3809
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> mul(lhs);
3810
        mul *= rhs;
3811
        return mul;
3812
}
3813
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3814
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator/(unsigned long long lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3815
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> ratio(lhs);
3816
        ratio /= rhs;
3817
        return ratio;
3818
}
3819

3820
///  binary cfloat - literal arithmetic operators
3821

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

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

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

3909
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3910
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator+(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, unsigned int rhs) {
3911
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
3912
        Cfloat sum(lhs);
3913
        sum += Cfloat(rhs);
3914
        return sum;
3915
}
3916
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3917
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator-(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, unsigned int rhs) {
3918
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
3919
        Cfloat diff(lhs);
3920
        diff -= rhs;
3921
        return diff;
3922
}
3923
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3924
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator*(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, unsigned int rhs) {
3925
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
3926
        Cfloat mul(lhs);
3927
        mul *= Cfloat(rhs);
3928
        return mul;
3929
}
3930
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3931
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator/(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, unsigned int rhs) {
3932
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
3933
        Cfloat ratio(lhs);
3934
        ratio /= Cfloat(rhs);
3935
        return ratio;
3936
}
3937

3938
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3939
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator+(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long long rhs) {
3940
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
3941
        Cfloat sum(lhs);
3942
        sum += Cfloat(rhs);
3943
        return sum;
3944
}
3945
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3946
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator-(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long long rhs) {
3947
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
3948
        Cfloat diff(lhs);
3949
        diff -= rhs;
3950
        return diff;
3951
}
3952
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3953
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator*(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long long rhs) {
3954
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
3955
        Cfloat mul(lhs);
3956
        mul *= Cfloat(rhs);
3957
        return mul;
3958
}
3959
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3960
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator/(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long long rhs) {
3961
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
3962
        Cfloat ratio(lhs);
3963
        ratio /= Cfloat(rhs);
3964
        return ratio;
3965
}
3966

3967
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3968
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator+(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, unsigned long long rhs) {
3969
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
3970
        Cfloat sum(lhs);
3971
        sum += Cfloat(rhs);
3972
        return sum;
3973
}
3974
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3975
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator-(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, unsigned long long rhs) {
3976
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
3977
        Cfloat diff(lhs);
3978
        diff -= rhs;
3979
        return diff;
3980
}
3981
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3982
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator*(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, unsigned long long rhs) {
3983
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
3984
        Cfloat mul(lhs);
3985
        mul *= Cfloat(rhs);
3986
        return mul;
3987
}
3988
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3989
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator/(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, unsigned long long rhs) {
3990
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
3991
        Cfloat ratio(lhs);
3992
        ratio /= Cfloat(rhs);
3993
        return ratio;
3994
}
3995

3996
///////////////////////////////////////////////////////////////////////
3997
///   binary logic literal comparisons
3998

3999
// cfloat - literal float logic operators
4000
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4001
inline bool operator==(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, float rhs) {
1✔
4002
        return float(lhs) == rhs;
1✔
4003
}
4004
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4005
inline bool operator!=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, float rhs) {
1✔
4006
        return float(lhs) != rhs;
1✔
4007
}
4008
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4009
inline bool operator< (const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, float rhs) {
2✔
4010
        return float(lhs) < rhs;
2✔
4011
}
4012
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4013
inline bool operator> (const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, float rhs) {
1✔
4014
        return float(lhs) > rhs;
1✔
4015
}
4016
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4017
inline bool operator<=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, float rhs) {
1✔
4018
        return float(lhs) <= rhs;
1✔
4019
}
4020
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4021
inline bool operator>=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, float rhs) {
1✔
4022
        return float(lhs) >= rhs;
1✔
4023
}
4024
// cfloat - literal double logic operators
4025
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4026
inline bool operator==(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, double rhs) {
1✔
4027
        return double(lhs) == rhs;
1✔
4028
}
4029
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4030
inline bool operator!=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, double rhs) {
1✔
4031
        return double(lhs) != rhs;
1✔
4032
}
4033
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4034
inline bool operator< (const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, double rhs) {
336✔
4035
        return double(lhs) < rhs;
336✔
4036
}
4037
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4038
inline bool operator> (const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, double rhs) {
892✔
4039
        return double(lhs) > rhs;
892✔
4040
}
4041
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4042
inline bool operator<=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, double rhs) {
1✔
4043
        return double(lhs) <= rhs;
1✔
4044
}
4045
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4046
inline bool operator>=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, double rhs) {
1✔
4047
        return double(lhs) >= rhs;
1✔
4048
}
4049

4050
#if LONG_DOUBLE_SUPPORT
4051
// cfloat - literal long double logic operators
4052
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4053
inline bool operator==(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long double rhs) {
1✔
4054
        return (long double)(lhs) == rhs;
1✔
4055
}
4056
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4057
inline bool operator!=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long double rhs) {
1✔
4058
        return (long double)(lhs) != rhs;
1✔
4059
}
4060
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4061
inline bool operator< (const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long double rhs) {
1✔
4062
        return (long double)(lhs) < rhs;
1✔
4063
}
4064
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4065
inline bool operator> (const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long double rhs) {
1✔
4066
        return (long double)(lhs) > rhs;
1✔
4067
}
4068
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4069
inline bool operator<=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long double rhs) {
1✔
4070
        return (long double)(lhs) <= rhs;
1✔
4071
}
4072
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4073
inline bool operator>=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long double rhs) {
1✔
4074
        return (long double)(lhs) >= rhs;
1✔
4075
}
4076
#endif
4077

4078
// cfloat - literal int logic operators
4079
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4080
inline bool operator==(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, int rhs) {
11✔
4081
        return operator==(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
11✔
4082
}
4083
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4084
inline bool operator!=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, int rhs) {
1✔
4085
        return operator!=(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
1✔
4086
}
4087
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4088
inline bool operator< (const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, int rhs) {
107,196✔
4089
        return operator<(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
107,196✔
4090
}
4091
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4092
inline bool operator> (const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, int rhs) {
666✔
4093
        return operator<(cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs), lhs);
666✔
4094
}
4095
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4096
inline bool operator<=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, int rhs) {
1✔
4097
        return operator<(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs)) || operator==(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
1✔
4098
}
4099
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4100
inline bool operator>=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, int rhs) {
1✔
4101
        return !operator<(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
1✔
4102
}
4103

4104
// cfloat - long long logic operators
4105
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4106
inline bool operator==(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long long rhs) {
4107
        return operator==(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
4108
}
4109
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4110
inline bool operator!=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long long rhs) {
4111
        return operator!=(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
4112
}
4113
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4114
inline bool operator< (const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long long rhs) {
4115
        return operator<(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
4116
}
4117
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4118
inline bool operator> (const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long long rhs) {
4119
        return operator<(cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs), lhs);
4120
}
4121
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4122
inline bool operator<=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long long rhs) {
4123
        return operator<(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs)) || operator==(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
4124
}
4125
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4126
inline bool operator>=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long long rhs) {
4127
        return !operator<(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
4128
}
4129

4130
// standard library functions for floating point
4131

4132
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4133
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> frexp(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& x, int* exp) {
57,412✔
4134
        *exp = x.scale();
57,412✔
4135
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> fraction(x);
57,412✔
4136
        fraction.setexponent(0);
57,412✔
4137
        return fraction;
57,412✔
4138
}
4139

4140
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4141
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> ldexp(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& x, int exp) {
765✔
4142
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> result(x);
765✔
4143
        int xexp = x.scale();
765✔
4144
        result.setexponent(xexp + exp);  // TODO: this does not work for subnormals
765✔
4145
        return result;
765✔
4146
}
4147

4148
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4149
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> 
4150
fma(cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> x,
3✔
4151
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> y,
4152
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> z) {
4153
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> fused{ 0 };
3✔
4154
        constexpr unsigned FBITS = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>::fbits;
3✔
4155
        constexpr unsigned EXTRA_FBITS = FBITS+2;
3✔
4156
        constexpr unsigned EXTENDED_PRECISION = nbits + EXTRA_FBITS;
3✔
4157
        // the C++ fma spec indicates that the x*y+z is evaluated in 'infinite' precision
4158
        // with only a single rounding event. The minimum finite precision that would behave like this
4159
        // is the precision where the product x*y does not need to be rounded, which will
4160
        // need at least 2*(fbits+1) mantissa bits to capture all bits that can be
4161
        // generated by the product.
4162
        cfloat<EXTENDED_PRECISION, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> preciseX(x), preciseY(y), preciseZ(z);
3✔
4163
//        ReportValue(preciseX, "extended precision x");
4164
//        ReportValue(preciseY, "extended precision y");
4165
//        ReportValue(preciseZ, "extended precision z");
4166
        cfloat<EXTENDED_PRECISION, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> product = preciseX * preciseY;
3✔
4167
//        ReportValue(product, "extended precision p");
4168
        fused = product + preciseZ;
3✔
4169
        return fused;
3✔
4170
}
4171

4172
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4173
cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>&
4174
minpos(cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& c) {
4175
        return c.minpos();
4176
}
4177
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4178
cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>&
4179
maxpos(cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& c) {
4180
        return c.maxpos();
4181
}
4182
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4183
cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>&
4184
minneg(cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& c) {
4185
        return c.minneg();
4186
}
4187
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4188
cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>&
4189
maxneg(cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& c) {
4190
        return c.maxneg();
4191
}
4192

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