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

stillwater-sc / universal / 23116316620

15 Mar 2026 04:59PM UTC coverage: 84.015% (+0.01%) from 84.005%
23116316620

Pull #563

github

web-flow
Merge d0d3e5f22 into 339cc1e16
Pull Request #563: feat(cfloat): implement parse() for string-to-cfloat conversion

46 of 47 new or added lines in 1 file covered. (97.87%)

4 existing lines in 1 file now uncovered.

43036 of 51224 relevant lines covered (84.02%)

6155795.13 hits per line

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

95.44
/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) {
71,332,933✔
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()) {
71,332,933✔
126
                tgt.setnan(src.sign() ? NAN_TYPE_SIGNALLING : NAN_TYPE_QUIET);
388✔
127
        }
128
        else        if (src.isinf()) {
71,332,545✔
129
                tgt.setinf(src.sign());
388✔
130
        }
131
        else         if (src.iszero()) {
71,332,157✔
132
                tgt.setzero();
57,149✔
133
                tgt.setsign(src.sign()); // preserve sign
57,149✔
134
        }
135
        else {
136
                int significandScale = src.significandscale();
71,275,008✔
137
                int exponent = src.scale() + significandScale;
71,275,008✔
138
                // special case of underflow
139
                if constexpr (hasSubnormals) {
140
//                        std::cout << "exponent = " << exponent << " bias = " << cfloatType::EXP_BIAS << " exp subnormal = " << cfloatType::MIN_EXP_SUBNORMAL << '\n';
141
                        // why must exponent be less than (minExpSubnormal - 1) to be rounded to zero? 
142
                        // because the half-way value that would round up to minpos is at exp = (minExpSubnormal - 1)
143
                        if (exponent < cfloatType::MIN_EXP_SUBNORMAL) {
54,638,536✔
144
                                tgt.setzero();
192,401✔
145
                                if (exponent == (cfloatType::MIN_EXP_SUBNORMAL - 1)) {
192,401✔
146
                                        // -exponent because we are right shifting and exponent in this range is negative
147
                                        int adjustment = -(exponent + subnormal_reciprocal_shift[es]);
42,803✔
148
                                        std::pair<bool, unsigned> alignment = src.roundingDecision(adjustment);
42,803✔
149
                                        if (alignment.first) ++tgt; // we are minpos
42,803✔
150
                                }
151
                                tgt.setsign(src.sign());
192,401✔
152
                                return;
3,127,105✔
153
                        }
154
                }
155
                else {
156
                        if (exponent + cfloatType::EXP_BIAS <= 0) {  // value is in the subnormal range, which maps to 0
16,636,472✔
157
                                tgt.setzero();
362,507✔
158
                                tgt.setsign(src.sign());
362,507✔
159
                                return;
638,776✔
160
                        }
161
                }
162
                // special case of overflow
163
                if constexpr (hasMaxExpValues) {
164
                        if constexpr (isSaturating) {
165
                                if (exponent > cfloatType::MAX_EXP) {
17,743,113✔
166
                                        if (src.sign()) tgt.maxneg(); else tgt.maxpos();
634,020✔
167
                                        return;
634,020✔
168
                                }
169
                        }
170
                        else {
171
                                if (exponent > cfloatType::MAX_EXP) {
27,200,432✔
172
                                        tgt.setinf(src.sign());
2,373,099✔
173
                                        return;
2,373,099✔
174
                                }
175
                        }
176
                }
177
                else {  // no max-exponent values: will saturate at a different encoding: TODO can we hide it all in maxpos?
178
                        if constexpr (isSaturating) {
179
                                if (exponent > cfloatType::MAX_EXP) {
3,707✔
180
                                        if (src.sign()) tgt.maxneg(); else tgt.maxpos();
9✔
181
                                        return;
9✔
182
                                }
183
                        }
184
                        else {
185
                                if (exponent > cfloatType::MAX_EXP) {
25,772,848✔
186
                                        tgt.setinf(src.sign());
203,845✔
187
                                        return;
203,845✔
188
                                }
189
                        }
190
                }
191

192
                // our value needs to go through rounding to be correctly interpreted
193
                // 
194
                // tgt.clear();  // no need as all bits are going to be set by the code below
195

196
                // exponent construction
197
                int adjustment{ 0 };
67,509,127✔
198
                // construct exponent
199
                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
67,509,127✔
200
//                        std::cout << "exponent         " << to_binary(biasedExponent) << '\n';        
201
                if constexpr (hasSubnormals) {
202
                        //if (exponent >= cfloatType::MIN_EXP_SUBNORMAL && exponent < cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>::MIN_EXP_NORMAL) {
203
                        if (exponent < cfloatType::MIN_EXP_NORMAL) {
51,511,431✔
204
                                // the value is in the subnormal range of the cfloat
205
                                biasedExponent = 0;
35,471,636✔
206
                                // -exponent because we are right shifting and exponent in this range is negative
207
                                adjustment = -(exponent + subnormal_reciprocal_shift[es]);
35,471,636✔
208
                                // this is the right shift adjustment required for subnormal representation due 
209
                                // to the scale of the input number, i.e. the exponent of 2^-adjustment
210
                        }
211
                        else {
212
                                // the value is in the normal range of the cfloat
213
                                biasedExponent = static_cast<uint64_t>(static_cast<long long>(exponent) + static_cast<long long>(cfloatType::EXP_BIAS)); // this is guaranteed to be positive
16,039,795✔
214
                        }
215
                }
216
                else {
217
                        if (exponent < cfloatType::MIN_EXP_NORMAL) biasedExponent = 1ull; // fixup biasedExponent if we are in the subnormal region
15,997,696✔
218
                }
219

220

221
                // get the rounding direction and the LSB right shift: 
222
                std::pair<bool, unsigned> alignment = src.roundingDecision(adjustment);
67,509,127✔
223
                unsigned rightShift = alignment.second;  // this is the shift to get the LSB of the src to the LSB of the tgt
67,509,127✔
224
                //std::cout << "rightShift       " << rightShift << '\n';
225

226
                if constexpr (btType::bfbits < 65) {
227
                        bool roundup = alignment.first;
66,977,227✔
228
                        //std::cout << "round-up?        " << (roundup ? "yes" : "no") << '\n';
229
                        // we can use a uint64_t to construct the cfloat
230
                        uint64_t raw = (src.sign() ? 1ull : 0ull); // process sign
66,977,227✔
231
                        //std::cout << "raw bits (sign)  " << to_binary(raw) << '\n';
232
                        // construct the fraction bits
233
                        uint64_t fracbits = src.significand_ull(); // get all the bits, including the integer bits
66,977,227✔
234
                        //std::cout << "fracbits         " << to_binary(fracbits) << '\n';
235
                        fracbits >>= rightShift;
66,977,227✔
236
                        //std::cout << "fracbits shifted " << to_binary(fracbits) << '\n';
237
                        fracbits &= cfloatType::ALL_ONES_FR; // remove the hidden bit
66,977,227✔
238
                        //std::cout << "fracbits masked  " << to_binary(fracbits) << '\n';
239
                        if (roundup) ++fracbits;
66,977,227✔
240
                        if (fracbits == (1ull << cfloatType::fbits)) { // check for overflow
66,977,227✔
241
                                if (biasedExponent == cfloatType::ALL_ONES_ES) {
425,456✔
242
                                        fracbits = cfloatType::INF_ENCODING; // project to INF
6,244✔
243
                                }
244
                                else {
245
                                        ++biasedExponent;
419,212✔
246
                                        fracbits = 0;
419,212✔
247
                                }
248
                        }
249

250
                        raw <<= es; // shift sign to make room for the exponent bits
66,977,227✔
251
                        raw |= biasedExponent;
66,977,227✔
252
                        //std::cout << "raw bits (exp)   " << to_binary(raw) << '\n';
253
                        raw <<= cfloatType::fbits; // make room for the fraction bits
66,977,227✔
254
                        //std::cout << "raw bits (s+exp) " << to_binary(raw) << '\n';
255
                        raw |= fracbits;
66,977,227✔
256
                        //std::cout << "raw bits (final) " << to_binary(raw) << '\n';
257
                        tgt.setbits(raw);
66,977,227✔
258
//                        std::cout << "raw bits (all)   " << to_binary(raw) << '\n';
259
                        if constexpr (isSaturating) {
260
                                if (tgt.isnan()) {
17,112,785✔
261
                                        if (src.sign()) {
550✔
262
                                                tgt.maxneg();        // map back to maxneg
275✔
263
                                        }
264
                                        else {
265
                                                tgt.maxpos();        // map back to maxpos
275✔
266
                                        }
267
                                }
268
                        }
269
                        else {
270
                                // when you get too far, map it back to +-inf: 
271
                                // TBD: this doesn't appear to be the right algorithm to catch all overflow patterns
272
                                if (tgt.isnan()) tgt.setinf(src.sign());        // map back to +-inf
49,864,442✔
273
                        }
274
                }
275
                else {
276
                        // compose the segments
277
                        auto fracbits = src.significand();  // why significand? cheesy optimization: we are going to overwrite the hidden bit position anyway when we write the exponent below, so no need to pay the overhead of generating the fraction here.
531,900✔
278
                        //std::cout << "fraction      : " << to_binary(fracbits, true) << '\n';
279
                        fracbits >>= static_cast<int>(rightShift);
531,900✔
280
                        //std::cout << "aligned fbits : " << to_binary(fracbits, true) << '\n';
281

282
                        // copy the blocks that contain fraction bits
283
                        // significand blocks are organized like this:
284
                        //   ADD        iii.ffffrrrrrrrrr          3 integer bits, f fraction bits, and 2*fhbits rounding bits
285
                        //   MUL         ii.ffff'ffff              2 integer bits, 2*f fraction bits
286
                        //   DIV         ii.ffff'ffff'ffff'rrrr    2 integer bits, 3*f fraction bits, and r rounding bits
287
                        //std::cout << "fraction bits : " << to_binary(fracbits, true) << '\n';
288
                        tgt.clear();
531,900✔
289
                        //std::cout << "initial state : " << to_binary(tgt) << " : " << tgt << '\n';
290
                        for (unsigned b = 0; b < btType::nrBlocks; ++b) {
1,074,316✔
291
                                tgt.setblock(b, fracbits.block(b));
542,416✔
292
                        }
293
                        //std::cout << "fraction bits : " << to_binary(tgt, true) << '\n';
294
                        tgt.setsign(src.sign());
531,900✔
295
                        //std::cout << "adding sign   : " << to_binary(tgt) << '\n';
296
                        if (!tgt.setexponent(exponent)) {
531,900✔
297
                                std::cerr << "exponent value is out of range: " << exponent << '\n';
×
298
                        }
299
                        //std::cout << "add exponent  : " << to_binary(tgt) << '\n';
300
                }
301
        }
302
}
303

304

305
/// <summary>
306
/// An arbitrary, fixed-size floating-point number with configurable gradual under/overflow and saturation/non-saturation arithmetic.
307
/// Default configuration offers normal encoding and non-saturating arithmetic.
308
/// /// </summary>
309
/// <typeparam name="nbits">number of bits in the encoding</typeparam>
310
/// <typeparam name="es">number of exponent bits in the encoding</typeparam>
311
/// <typeparam name="bt">the type to use as storage class: one of [uint8_t|uint16_t|uint32_t]</typeparam>
312
/// <typeparam name="hasSubnormals">configure gradual underflow (==subnormals)</typeparam>
313
/// <typeparam name="hasMaxExpValues">reclaim max-exponent encodings as numeric values</typeparam>
314
/// <typeparam name="isSaturating">configure saturation arithmetic</typeparam>
315
template<unsigned _nbits, unsigned _es, typename bt = uint8_t,
316
        bool _hasSubnormals = false, bool _hasMaxExpValues = false, bool _isSaturating = false>
317
class cfloat {
318
public:
319
        static_assert(_nbits > _es + 1ull, "nbits is too small to accomodate the requested number of exponent bits");
320
        static_assert(_es < 21ull, "my God that is a big number, are you trying to break the Interweb?");
321
        static_assert(_es > 0, "number of exponent bits must be bigger than 0 to be a classic floating point number");
322
        // how do you assert on the condition that if es == 1 then subnormals and max-exponent values must be true?
323
        static constexpr bool     subsuper = (_hasSubnormals && _hasMaxExpValues);
324
        static constexpr bool     special = (subsuper ? true : (_es > 1));
325
        static_assert(special, "when es == 1, cfloat must have both subnormals and max-exponent values");
326
        static constexpr unsigned bitsInByte = 8u;
327
        static constexpr unsigned bitsInBlock = sizeof(bt) * bitsInByte;
328
        static_assert(bitsInBlock <= 64, "storage unit for block arithmetic needs to be <= uint64_t"); // TODO: carry propagation on uint64_t requires assembly code
329

330
        static constexpr unsigned nbits = _nbits;
331
        static constexpr unsigned es = _es;
332
        static constexpr unsigned fbits  = nbits - 1u - es;    // number of fraction bits excluding the hidden bit
333
        static constexpr unsigned fhbits = nbits - es;           // number of fraction bits including the hidden bit
334

335
        static constexpr uint64_t  storageMask = (0xFFFFFFFFFFFFFFFFull >> (64ull - bitsInBlock));
336
        static constexpr bt       BLOCK_MASK = bt(~0);
337
        static constexpr bt       ALL_ONES = bt(~0); // block type specific all 1's value
338
        static constexpr uint32_t ALL_ONES_ES = (0xFFFF'FFFFul >> (32 - es));
339
        static constexpr uint64_t topfbits = fbits % 64;
340
        static constexpr uint64_t FR_SHIFT = (topfbits > 0 ? (64 - topfbits) : 0);
341
        static constexpr uint64_t ALL_ONES_FR = (topfbits > 0 ? (0xFFFF'FFFF'FFFF'FFFFull >> FR_SHIFT) : 0ull); // special case for nbits <= 64
342
        static constexpr uint64_t INF_ENCODING = (ALL_ONES_FR & ~1ull);
343

344
        static constexpr unsigned nrBlocks = 1u + ((nbits - 1ull) / bitsInBlock);
345
        static constexpr unsigned MSU = nrBlocks - 1u; // MSU == Most Significant Unit, as MSB is already taken
346
        static constexpr bt       MSU_MASK = (ALL_ONES >> (nrBlocks * bitsInBlock - nbits));
347
        static constexpr unsigned bitsInMSU = bitsInBlock - (nrBlocks * bitsInBlock - nbits);
348
        static constexpr unsigned fBlocks = 1ull + ((fbits - 1ull) / bitsInBlock); // nr of blocks with fraction bits
349
        static constexpr unsigned FSU = fBlocks - 1u;  // FSU = Fraction Significant Unit: the index of the block that contains the most significant fraction bits
350
        static constexpr bt       FSU_MASK = (ALL_ONES >> (fBlocks * bitsInBlock - fbits));
351
        static constexpr unsigned bitsInFSU = bitsInBlock - (fBlocks * bitsInBlock - fbits);
352

353
        static constexpr bt       SIGN_BIT_MASK = bt(bt(1ull) << ((nbits - 1ull) % bitsInBlock));
354
        static constexpr bt       LSB_BIT_MASK = bt(1ull);
355
        static constexpr bool     MSU_CAPTURES_EXP = (1ull + es) <= bitsInMSU;
356
        static constexpr unsigned EXP_SHIFT = (MSU_CAPTURES_EXP ? (1 == nrBlocks ? (nbits - 1ull - es) : (bitsInMSU - 1ull - es)) : 0);
357
        static constexpr bt       MSU_EXP_MASK = ((ALL_ONES << EXP_SHIFT) & ~SIGN_BIT_MASK) & MSU_MASK;
358
        static constexpr int      EXP_BIAS = ((1 << (es - 1u)) - 1l);
359
        static constexpr int      MAX_EXP = (es == 1) ? 1 : ((1 << es) - EXP_BIAS - 1);
360
        static constexpr int      MIN_EXP_NORMAL = 1 - EXP_BIAS;
361
        static constexpr int      MIN_EXP_SUBNORMAL = 1 - EXP_BIAS - int(fbits); // the scale of smallest ULP
362

363
        static constexpr bool     hasSubnormals   = _hasSubnormals;
364
        static constexpr bool     hasMaxExpValues = _hasMaxExpValues;
365
        static constexpr bool     isSaturating    = _isSaturating;
366
        typedef bt BlockType;
367

368
        // constructors
369
        cfloat() = default;
370
        cfloat(const cfloat&) = default;
371
        cfloat& operator=(const cfloat&) = default;
372

373
        // construct a cfloat from another
374
        template<unsigned nnbits, unsigned ees, typename bbt, bool ssub, bool ssup, bool ssat>
375
        cfloat(const cfloat<nnbits, ees, bbt, ssub, ssup, ssat>& rhs) noexcept : _block{} {
6,104✔
376
                if (rhs.isnan()) {
6,104✔
377
                        setnan(rhs.sign() ? NAN_TYPE_SIGNALLING : NAN_TYPE_QUIET);
79✔
378
                }
379
                else if (rhs.isinf()) {
6,025✔
380
                        setinf(rhs.sign());
9✔
381
                }
382
                else if (rhs.iszero()) {
6,016✔
383
                        setzero();
738✔
384
                }
385
                else {
386
                        // TODO: cfloat from another cfloat: marshall through a proper blocktriple
387
                        /*
388
                        if constexpr (std::is_same_v<bt, bbt>) {
389
                                blocktriple<fbits, BlockTripleOperator::REP, bt> value;
390
                                value.setnormal();
391
                                value.setsign(rhs.sign());
392
                                value.setscale(rhs.scale());
393
                                //constexpr unsigned rhsFbits = nnbits - 1ul - ees;
394
                                //blockbinary<rhsFbits, bbt, BinaryNumberType::Signed> fraction;
395
                                //rhs.fraction<rhsFbits>(fraction);
396
                                //std::cout << "fraction : " << to_binary(fraction) << '\n';
397
                                //value.setfraction(fraction);
398
                                convert(value, *this);
399
                        }
400
                        else {
401
                                static_assert(nnbits < 64, "converting constructor marshalls values through native double precision, and rhs has more bits");
402
                                *this = double(rhs); 
403
                        }
404
                        */
405
                        *this = double(rhs);
5,278✔
406
                }
407
        }
6,104✔
408

409
        // converting constructors
410
        constexpr cfloat(const std::string& stringRep) : _block{} { assign(stringRep); }
1✔
411
        // specific value constructor
412
        constexpr cfloat(const SpecificValue code) noexcept : _block{} {
309✔
413
                switch (code) {
309✔
414
                case SpecificValue::maxpos:
95✔
415
                        maxpos();
95✔
416
                        break;
95✔
417
                case SpecificValue::minpos:
111✔
418
                        minpos();
111✔
419
                        break;
111✔
420
                case SpecificValue::zero:
×
421
                default:
422
                        zero();
×
423
                        break;
×
424
                case SpecificValue::minneg:
×
425
                        minneg();
×
426
                        break;
×
427
                case SpecificValue::maxneg:
48✔
428
                        maxneg();
48✔
429
                        break;
48✔
430
                case SpecificValue::infpos:
21✔
431
                        setinf(false);
21✔
432
                        break;
21✔
433
                case SpecificValue::infneg:
×
434
                        setinf(true);
×
435
                        break;
×
436
                case SpecificValue::nar: // approximation as cfloats don't have a NaR
17✔
437
                case SpecificValue::qnan:
438
                        setnan(NAN_TYPE_QUIET);
17✔
439
                        break;
17✔
440
                case SpecificValue::snan:
17✔
441
                        setnan(NAN_TYPE_SIGNALLING);
17✔
442
                        break;
17✔
443
                }
444
        }
309✔
445

446
        constexpr cfloat(signed char iv)                    noexcept : _block{} { *this = iv; }
447
        constexpr cfloat(short iv)                          noexcept : _block{} { *this = iv; }
448
        constexpr cfloat(int iv)                            noexcept : _block{} { *this = iv; }
114,803✔
449
        constexpr cfloat(long iv)                           noexcept : _block{} { *this = iv; }
450
        constexpr cfloat(long long iv)                      noexcept : _block{} { *this = iv; }
1✔
451
        constexpr cfloat(char iv)                           noexcept : _block{} { *this = iv; }
452
        constexpr cfloat(unsigned short iv)                 noexcept : _block{} { *this = iv; }
453
        constexpr cfloat(unsigned int iv)                   noexcept : _block{} { *this = iv; }
454
        constexpr cfloat(unsigned long iv)                  noexcept : _block{} { *this = iv; }
100✔
455
        constexpr cfloat(unsigned long long iv)             noexcept : _block{} { *this = iv; }
456
        CONSTEXPRESSION cfloat(float iv)                    noexcept : _block{} { *this = iv; }
340,794✔
457
        CONSTEXPRESSION cfloat(double iv)                   noexcept : _block{} { *this = iv; }
506,979✔
458

459
        // assignment operators
460
        constexpr cfloat& operator=(signed char rhs)        noexcept { return convert_signed_integer(rhs); }
461
        constexpr cfloat& operator=(short rhs)              noexcept { return convert_signed_integer(rhs); }
462
        constexpr cfloat& operator=(int rhs)                noexcept { return convert_signed_integer(rhs); }
174,971✔
463
        constexpr cfloat& operator=(long rhs)               noexcept { return convert_signed_integer(rhs); }
464
        constexpr cfloat& operator=(long long rhs)          noexcept { return convert_signed_integer(rhs); }
1✔
465

466
        constexpr cfloat& operator=(char rhs)               noexcept { return convert_unsigned_integer(rhs); }
467
        constexpr cfloat& operator=(unsigned short rhs)     noexcept { return convert_unsigned_integer(rhs); }
468
        constexpr cfloat& operator=(unsigned int rhs)       noexcept { return convert_unsigned_integer(rhs); }
30✔
469
        constexpr cfloat& operator=(unsigned long rhs)      noexcept { return convert_unsigned_integer(rhs); }
110✔
470
        constexpr cfloat& operator=(unsigned long long rhs) noexcept { return convert_unsigned_integer(rhs); }
471

472
        BIT_CAST_CONSTEXPR cfloat& operator=(float rhs)     noexcept { return convert_ieee754(rhs); }
32,488,669✔
473
        BIT_CAST_CONSTEXPR cfloat& operator=(double rhs)    noexcept { return convert_ieee754(rhs); }
99,040,160✔
474

475
        // make conversions to native types explicit
476
        explicit operator int()                       const noexcept { return to_int(); }
59,976✔
477
        explicit operator long()                      const noexcept { return to_long(); }
478
        explicit operator long long()                 const noexcept { return to_long_long(); }
1✔
479
        explicit operator float()                     const noexcept { return to_native<float>(); }
35,177,206✔
480
        explicit operator double()                    const noexcept { return to_native<double>(); }
78,233,390✔
481

482
        // guard long double support to enable ARM and RISC-V embedded environments
483
#if LONG_DOUBLE_SUPPORT
484
        explicit operator long double()               const noexcept { return to_native<long double>(); }
54✔
485
        BIT_CAST_CONSTEXPR cfloat(long double iv)           noexcept : _block{} { *this = iv; }
2✔
486
        BIT_CAST_CONSTEXPR cfloat& operator=(long double rhs)  noexcept { return convert_ieee754(rhs); }
74✔
487
#endif
488

489
        // arithmetic operators
490
        // prefix operator
491
        inline cfloat operator-() const {
8,346,919✔
492
                cfloat tmp(*this);
8,346,919✔
493
                tmp._block[MSU] ^= SIGN_BIT_MASK;
8,346,919✔
494
                return tmp;
8,346,919✔
495
        }
496

497
        cfloat& operator+=(const cfloat& rhs) CFLOAT_EXCEPT {
18,470,967✔
498
                if constexpr (_trace_add) std::cout << "---------------------- ADD -------------------" << std::endl;
499
                // special case handling of the inputs
500
#if CFLOAT_THROW_ARITHMETIC_EXCEPTION
501
                if (isnan(NAN_TYPE_SIGNALLING) || rhs.isnan(NAN_TYPE_SIGNALLING)) {
744✔
502
                        throw cfloat_operand_is_nan{};
×
503
                }
504
#else
505
                if (isnan(NAN_TYPE_SIGNALLING) || rhs.isnan(NAN_TYPE_SIGNALLING)) {
18,470,223✔
506
                        setnan(NAN_TYPE_SIGNALLING);
233,112✔
507
                        return *this;
233,112✔
508
                }
509
                if (isnan(NAN_TYPE_QUIET) || rhs.isnan(NAN_TYPE_QUIET)) {
18,237,111✔
510
                        setnan(NAN_TYPE_QUIET);
213,837✔
511
                        return *this;
213,837✔
512
                }
513
#endif
514
                // normal + inf  = inf
515
                // normal + -inf = -inf
516
                // inf + normal = inf
517
                // inf + inf    = inf
518
                // inf + -inf    = ?
519
                // -inf + normal = -inf
520
                // -inf + -inf   = -inf
521
                // -inf + inf    = ?
522
                if (isinf()) {
18,024,018✔
523
                        if (rhs.isinf()) {
58,855✔
524
                                if (sign() != rhs.sign()) {
783✔
525
                                        setnan(NAN_TYPE_SIGNALLING);
358✔
526
                                }
527
                                return *this;
783✔
528
                        }
529
                        else {
530
                                return *this;
58,072✔
531
                        }
532
                }
533
                else {
534
                        if (rhs.isinf()) {
17,965,163✔
535
                                *this = rhs;
58,076✔
536
                                return *this;
58,076✔
537
                        }
538
                }
539

540
                if (iszero()) {
17,907,087✔
541
                        *this = rhs;
205,858✔
542
                        return *this;
205,858✔
543
                }
544
                if (rhs.iszero()) return *this;
17,701,229✔
545

546
                // arithmetic operation
547
                blocktriple<fbits, BlockTripleOperator::ADD, bt> a, b, sum;
17,370,069✔
548

549
                // transform the inputs into (sign,scale,significant) 
550
                normalizeAddition(a); 
17,370,069✔
551
                rhs.normalizeAddition(b);
17,370,069✔
552
                sum.add(a, b);
17,370,069✔
553

554
                convert(sum, *this);
17,370,069✔
555

556
                return *this;
17,370,069✔
557
        }
558
        cfloat& operator+=(double rhs) CFLOAT_EXCEPT {
559
                return *this += cfloat(rhs);
560
        }
561
        cfloat& operator-=(const cfloat& rhs) CFLOAT_EXCEPT {
8,454,809✔
562
                if constexpr (_trace_sub) std::cout << "---------------------- SUB -------------------" << std::endl;
563
                if (rhs.isnan()) 
8,454,809✔
564
                        return *this += rhs;
163,649✔
565
                else 
566
                        return *this += -rhs;
8,291,160✔
567
        }
568
        cfloat& operator-=(double rhs) CFLOAT_EXCEPT {
8✔
569
                return *this -= cfloat(rhs);
8✔
570
        }
571
        cfloat& operator*=(const cfloat& rhs) CFLOAT_EXCEPT {
4,298,336✔
572
                if constexpr (_trace_mul) std::cout << "---------------------- MUL -------------------\n";
322✔
573
                // special case handling of the inputs
574
#if CFLOAT_THROW_ARITHMETIC_EXCEPTION
575
                if (isnan(NAN_TYPE_SIGNALLING) || rhs.isnan(NAN_TYPE_SIGNALLING)) {
612✔
576
                        throw cfloat_operand_is_nan{};
×
577
                }
578
#else
579
                if (isnan(NAN_TYPE_SIGNALLING) || rhs.isnan(NAN_TYPE_SIGNALLING)) {
4,297,724✔
580
                        setnan(NAN_TYPE_SIGNALLING);
121,229✔
581
                        return *this;
121,229✔
582
                }
583
                if (isnan(NAN_TYPE_QUIET) || rhs.isnan(NAN_TYPE_QUIET)) {
4,176,495✔
584
                        setnan(NAN_TYPE_QUIET);
111,602✔
585
                        return *this;
111,602✔
586
                }
587
#endif
588
                //  inf * inf = inf
589
                //  inf * -inf = -inf
590
                // -inf * inf = -inf
591
                // -inf * -inf = inf
592
                //        0 * inf = -nan(ind)
593
                //        inf * 0 = -nan(ind)
594
                bool resultSign = sign() != rhs.sign();
4,065,505✔
595
                if (isinf()) {
4,065,505✔
596
                        if (rhs.iszero()) {
24,457✔
597
                                setnan(NAN_TYPE_QUIET);
1,591✔
598
                        }
599
                        else {
600
                                setsign(resultSign);
22,866✔
601
                        }
602
                        return *this;
24,457✔
603
                }
604
                if (rhs.isinf()) {
4,041,048✔
605
                        if (iszero()) {
24,067✔
606
                                setnan(NAN_TYPE_QUIET);
1,595✔
607
                        }
608
                        else {
609
                                setinf(resultSign);
22,472✔
610
                        }
611
                        return *this;
24,067✔
612
                }
613

614
                if (iszero() || rhs.iszero()) {                        
4,016,981✔
615
                        setzero();
1,873,984✔
616
                        setsign(resultSign); // deal with negative 0
1,873,984✔
617
                        return *this;
1,873,984✔
618
                }
619

620
                // arithmetic operation
621
                blocktriple<fbits, BlockTripleOperator::MUL, bt> a, b, product;
2,142,997✔
622

623
                // transform the inputs into (sign,scale,significant) 
624
                // triples of the correct width
625
                normalizeMultiplication(a);
2,142,997✔
626
                rhs.normalizeMultiplication(b);
2,142,997✔
627
                product.mul(a, b);
2,142,997✔
628
                convert(product, *this);
2,142,997✔
629

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

632
                return *this;
2,142,997✔
633
        }
634
        cfloat& operator*=(double rhs) CFLOAT_EXCEPT {
40✔
635
                return *this *= cfloat(rhs);
40✔
636
        }
637
        cfloat& operator/=(const cfloat& rhs) CFLOAT_EXCEPT {
5,135,689✔
638
                if constexpr (_trace_div) std::cout << "---------------------- DIV -------------------" << std::endl;
639

640
                // special case handling of the inputs
641
                // qnan / qnan = qnan
642
                // qnan / snan = qnan
643
                // snan / qnan = snan
644
                // snan / snan = snan
645
#if CFLOAT_THROW_ARITHMETIC_EXCEPTION
646
                if (rhs.iszero()) throw cfloat_divide_by_zero();
49✔
647
                if (rhs.isnan()) throw cfloat_divide_by_nan();
48✔
648
                if (isnan()) throw cfloat_operand_is_nan();
48✔
649
#else
650
                if (isnan(NAN_TYPE_SIGNALLING) || rhs.isnan(NAN_TYPE_SIGNALLING)) {
5,135,640✔
651
                        setnan(NAN_TYPE_SIGNALLING);
163,870✔
652
                        return *this;
163,870✔
653
                }
654
                if (isnan(NAN_TYPE_QUIET) || rhs.isnan(NAN_TYPE_QUIET)) {
4,971,770✔
655
                        setnan(NAN_TYPE_QUIET);
150,950✔
656
                        return *this;
150,950✔
657
                }
658
                if (rhs.iszero()) {
4,820,820✔
659
                        if (iszero()) {
169,760✔
660
                                // zero divide by zero yields quiet NaN (in MSVC it is labeled -nan(ind) for indeterminate)
661
                                setnan(NAN_TYPE_QUIET);
29,299✔
662
                        }
663
                        else {
664
                                // non-zero divide by zero yields INF
665
                                bool resultSign = sign() != rhs.sign();
140,461✔
666
                                setinf(resultSign);
140,461✔
667
                        }
668
                        return *this;
169,760✔
669
                }
670
#endif
671
                //  inf /  inf = -nan(ind)
672
                //  inf / -inf = -nan(ind)
673
                // -inf /  inf = -nan(ind)
674
                // -inf / -inf = -nan(ind)
675
                //        1.0 /  inf = 0
676
                bool resultSign = sign() != rhs.sign();
4,651,108✔
677
                if (isinf()) {
4,651,108✔
678
                        if (rhs.isinf()) {
31,056✔
679
                                // inf divide by inf yields quiet NaN (in MSVC it is labeled -nan(ind) for indeterminate)
680
                                setnan(NAN_TYPE_QUIET);
528✔
681
                                return *this;
528✔
682
                        }
683
                        else {
684
                                // we stay an infinite but may change sign
685
                                setsign(resultSign);
30,528✔
686
                                return *this;
30,528✔
687
                        }
688
                }
689
                else {
690
                        if (rhs.isinf()) {
4,620,052✔
691
                                setzero();
32,648✔
692
                                setsign(resultSign);
32,648✔
693
                                return *this;
32,648✔
694
                        }
695
                }
696

697
                if (iszero()) {
4,587,404✔
698
                        setzero();
139,004✔
699
                        setsign(resultSign); // deal with negative 0
139,004✔
700
                        return *this;
139,004✔
701
                }
702

703
                // arithmetic operation
704
                using BlockTriple = blocktriple<fbits, BlockTripleOperator::DIV, bt>;
705
                BlockTriple a, b, quotient;
4,448,400✔
706

707
                // transform the inputs into (sign,scale,significant) 
708
                // triples of the correct width
709
                normalizeDivision(a);
4,448,400✔
710
                rhs.normalizeDivision(b);
4,448,400✔
711
                quotient.div(a, b);
4,448,400✔
712
                quotient.setradix(BlockTriple::radix);
4,448,400✔
713
                convert(quotient, *this);
4,448,400✔
714

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

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

933
        // modifiers        
934
        constexpr void clear() noexcept {
136,133,164✔
935
                if constexpr (0 == nrBlocks) {
936
                        return;
937
                }
938
                else if constexpr (1 == nrBlocks) {
939
                        _block[0] = bt(0);
41,270,145✔
940
                }
941
                else if constexpr (2 == nrBlocks) {
942
                        _block[0] = bt(0);
46,801,507✔
943
                        _block[1] = bt(0);
46,801,507✔
944
                }
945
                else if constexpr (3 == nrBlocks) {
946
                        _block[0] = bt(0);
47,927,467✔
947
                        _block[1] = bt(0);
47,927,467✔
948
                        _block[2] = bt(0);
47,927,467✔
949
                }
950
                else if constexpr (4 == nrBlocks) {
951
                        _block[0] = bt(0);
43,359✔
952
                        _block[1] = bt(0);
43,359✔
953
                        _block[2] = bt(0);
43,359✔
954
                        _block[3] = bt(0);
43,359✔
955
                }
956
                else if constexpr (5 == nrBlocks) {
957
                        _block[0] = bt(0);
29,999✔
958
                        _block[1] = bt(0);
29,999✔
959
                        _block[2] = bt(0);
29,999✔
960
                        _block[3] = bt(0);
29,999✔
961
                        _block[4] = bt(0);
29,999✔
962
                }
963
                else if constexpr (6 == nrBlocks) {
964
                        _block[0] = bt(0);
10,016✔
965
                        _block[1] = bt(0);
10,016✔
966
                        _block[2] = bt(0);
10,016✔
967
                        _block[3] = bt(0);
10,016✔
968
                        _block[4] = bt(0);
10,016✔
969
                        _block[5] = bt(0);
10,016✔
970
                }
971
                else if constexpr (7 == nrBlocks) {
972
                        _block[0] = bt(0);
9,999✔
973
                        _block[1] = bt(0);
9,999✔
974
                        _block[2] = bt(0);
9,999✔
975
                        _block[3] = bt(0);
9,999✔
976
                        _block[4] = bt(0);
9,999✔
977
                        _block[5] = bt(0);
9,999✔
978
                        _block[6] = bt(0);
9,999✔
979
                }
980
                else if constexpr (8 == nrBlocks) {
981
                        _block[0] = bt(0);
20,325✔
982
                        _block[1] = bt(0);
20,325✔
983
                        _block[2] = bt(0);
20,325✔
984
                        _block[3] = bt(0);
20,325✔
985
                        _block[4] = bt(0);
20,325✔
986
                        _block[5] = bt(0);
20,325✔
987
                        _block[6] = bt(0);
20,325✔
988
                        _block[7] = bt(0);
20,325✔
989
                }
990
                else {
991
                        for (unsigned i = 0; i < nrBlocks; ++i) {
224,339✔
992
                                _block[i] = bt(0);
203,992✔
993
                        }
994
                }
995
        }
136,133,164✔
996
        constexpr void setzero() noexcept { clear(); }
2,692,157✔
997
        constexpr void setinf(bool sign = true) noexcept {
5,951,083✔
998
                // the Inf encoding is the pattern 0b0'11...11'11...10 for a +inf, and 0b1'11...11'11...110 for a -inf
999
                if constexpr (0 == nrBlocks) {
1000
                        return;
1001
                }
1002
                else if constexpr (1 == nrBlocks) {
1003
                        _block[MSU] = sign ? bt(MSU_MASK ^ LSB_BIT_MASK) : bt(~SIGN_BIT_MASK & (MSU_MASK ^ LSB_BIT_MASK));
2,195,232✔
1004
                }
1005
                else if constexpr (2 == nrBlocks) {
1006
                        _block[0] = BLOCK_MASK ^ LSB_BIT_MASK;
1,750,652✔
1007
                        _block[MSU] = sign ? MSU_MASK : bt(~SIGN_BIT_MASK & MSU_MASK);
1,750,652✔
1008
                }
1009
                else if constexpr (3 == nrBlocks) {
1010
                        _block[0] = BLOCK_MASK ^ LSB_BIT_MASK;
2,002,312✔
1011
                        _block[1] = BLOCK_MASK;
2,002,312✔
1012
                        _block[MSU] = sign ? MSU_MASK : bt(~SIGN_BIT_MASK & MSU_MASK);
2,002,312✔
1013
                }
1014
                else if constexpr (4 == nrBlocks) {
1015
                        _block[0] = BLOCK_MASK ^ LSB_BIT_MASK;
2,603✔
1016
                        _block[1] = BLOCK_MASK;
2,603✔
1017
                        _block[2] = BLOCK_MASK;
2,603✔
1018
                        _block[MSU] = sign ? MSU_MASK : bt(~SIGN_BIT_MASK & MSU_MASK);
2,603✔
1019
                }
1020
                else if constexpr (5 == nrBlocks) {
1021
                        _block[0] = BLOCK_MASK ^ LSB_BIT_MASK;
76✔
1022
                        _block[1] = BLOCK_MASK;
76✔
1023
                        _block[2] = BLOCK_MASK;
76✔
1024
                        _block[3] = BLOCK_MASK;
76✔
1025
                        _block[MSU] = sign ? MSU_MASK : bt(~SIGN_BIT_MASK & MSU_MASK);
76✔
1026
                }
1027
                else if constexpr (6 == nrBlocks) {
1028
                        _block[0] = BLOCK_MASK ^ LSB_BIT_MASK;
36✔
1029
                        _block[1] = BLOCK_MASK;
36✔
1030
                        _block[2] = BLOCK_MASK;
36✔
1031
                        _block[3] = BLOCK_MASK;
36✔
1032
                        _block[4] = BLOCK_MASK;
36✔
1033
                        _block[MSU] = sign ? MSU_MASK : bt(~SIGN_BIT_MASK & MSU_MASK);
36✔
1034
                }
1035
                else if constexpr (7 == nrBlocks) {
1036
                        _block[0] = BLOCK_MASK ^ LSB_BIT_MASK;
49✔
1037
                        _block[1] = BLOCK_MASK;
49✔
1038
                        _block[2] = BLOCK_MASK;
49✔
1039
                        _block[3] = BLOCK_MASK;
49✔
1040
                        _block[4] = BLOCK_MASK;
49✔
1041
                        _block[5] = BLOCK_MASK;
49✔
1042
                        _block[MSU] = sign ? MSU_MASK : bt(~SIGN_BIT_MASK & MSU_MASK);
49✔
1043
                }
1044
                else if constexpr (8 == nrBlocks) {
1045
                        _block[0] = BLOCK_MASK ^ LSB_BIT_MASK;
76✔
1046
                        _block[1] = BLOCK_MASK;
76✔
1047
                        _block[2] = BLOCK_MASK;
76✔
1048
                        _block[3] = BLOCK_MASK;
76✔
1049
                        _block[4] = BLOCK_MASK;
76✔
1050
                        _block[5] = BLOCK_MASK;
76✔
1051
                        _block[6] = BLOCK_MASK;
76✔
1052
                        _block[MSU] = sign ? MSU_MASK : bt(~SIGN_BIT_MASK & MSU_MASK);
76✔
1053
                }
1054
                else {
1055
                        _block[0] = BLOCK_MASK ^ LSB_BIT_MASK;
47✔
1056
                        for (unsigned i = 1; i < nrBlocks - 1; ++i) {
423✔
1057
                                _block[i] = BLOCK_MASK;
376✔
1058
                        }
1059
                        _block[MSU] = sign ? MSU_MASK : bt(~SIGN_BIT_MASK & MSU_MASK);
47✔
1060
                }        
1061
        }
5,951,083✔
1062
        constexpr void setnan(int NaNType = NAN_TYPE_SIGNALLING) noexcept {
7,105,163✔
1063
                // 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
1064
                if constexpr (0 == nrBlocks) {
1065
                        return;
1066
                }
1067
                else if constexpr (1 == nrBlocks) {
1068
                        // fall through
1069
                }
1070
                else if constexpr (2 == nrBlocks) {
1071
                        _block[0] = BLOCK_MASK;
1,994,452✔
1072
                }
1073
                else if constexpr (3 == nrBlocks) {
1074
                        _block[0] = BLOCK_MASK;
1,048,673✔
1075
                        _block[1] = BLOCK_MASK;
1,048,673✔
1076
                }
1077
                else if constexpr (4 == nrBlocks) {
1078
                        _block[0] = BLOCK_MASK;
25✔
1079
                        _block[1] = BLOCK_MASK;
25✔
1080
                        _block[2] = BLOCK_MASK;
25✔
1081
                }
1082
                else if constexpr (5 == nrBlocks) {
1083
                        _block[0] = BLOCK_MASK;
9✔
1084
                        _block[1] = BLOCK_MASK;
9✔
1085
                        _block[2] = BLOCK_MASK;
9✔
1086
                        _block[3] = BLOCK_MASK;
9✔
1087
                }
1088
                else if constexpr (6 == nrBlocks) {
1089
                        _block[0] = BLOCK_MASK;
4✔
1090
                        _block[1] = BLOCK_MASK;
4✔
1091
                        _block[2] = BLOCK_MASK;
4✔
1092
                        _block[3] = BLOCK_MASK;
4✔
1093
                        _block[4] = BLOCK_MASK;
4✔
1094
                }
1095
                else if constexpr (7 == nrBlocks) {
1096
                        _block[0] = BLOCK_MASK;
4✔
1097
                        _block[1] = BLOCK_MASK;
4✔
1098
                        _block[2] = BLOCK_MASK;
4✔
1099
                        _block[3] = BLOCK_MASK;
4✔
1100
                        _block[4] = BLOCK_MASK;
4✔
1101
                        _block[5] = BLOCK_MASK;
4✔
1102
                }
1103
                else if constexpr (8 == nrBlocks) {
1104
                        _block[0] = BLOCK_MASK;
4✔
1105
                        _block[1] = BLOCK_MASK;
4✔
1106
                        _block[2] = BLOCK_MASK;
4✔
1107
                        _block[3] = BLOCK_MASK;
4✔
1108
                        _block[4] = BLOCK_MASK;
4✔
1109
                        _block[5] = BLOCK_MASK;
4✔
1110
                        _block[6] = BLOCK_MASK;
4✔
1111
                }
1112
                else {
1113
                        for (unsigned i = 0; i < nrBlocks - 1; ++i) {
60✔
1114
                                _block[i] = BLOCK_MASK;
54✔
1115
                        }
1116
                }
1117
                _block[MSU] = NaNType == NAN_TYPE_SIGNALLING ? MSU_MASK : bt(~SIGN_BIT_MASK & MSU_MASK);
7,105,163✔
1118
        }
7,105,163✔
1119
        constexpr void setsign(bool sign = true) {
3,534,039✔
1120
                if (sign) {
3,534,039✔
1121
                        _block[MSU] |= SIGN_BIT_MASK;
631,217✔
1122
                }
1123
                else {
1124
                        _block[MSU] &= ~SIGN_BIT_MASK;
2,902,822✔
1125
                }
1126
        }
3,534,039✔
1127
        constexpr bool setexponent(int scale) {
590,516✔
1128
                if (scale < MIN_EXP_SUBNORMAL || scale > MAX_EXP) return false; // this scale cannot be represented
590,516✔
1129
                if constexpr (nbits < 65) {
1130
                        uint32_t exponentBits = static_cast<uint32_t>(scale + EXP_BIAS);
588,975✔
1131
                        if (scale >= MIN_EXP_SUBNORMAL && scale < MIN_EXP_NORMAL) {
588,975✔
1132
                                // we are a subnormal number: all exponent bits are 0
1133
                                exponentBits = 0;
45✔
1134
                        }
1135
                        // TODO: optimize
1136
                        uint32_t mask = (1ul << (es - 1));
588,975✔
1137
                        for (unsigned i = nbits - 2; i > nbits - 2 - es; --i) {
5,084,092✔
1138
                                setbit(i, (mask & exponentBits));
4,495,117✔
1139
                                mask >>= 1;
4,495,117✔
1140
                        }
1141
                }
1142
                else {
1143
                        uint32_t exponentBits = static_cast<uint32_t>(scale + EXP_BIAS);
1,541✔
1144
                        uint32_t mask = (1ul << (es - 1));
1,541✔
1145
                        for (unsigned i = nbits - 2; i > nbits - 2 - es; --i) {
25,156✔
1146
                                setbit(i, (mask & exponentBits));
23,615✔
1147
                                mask >>= 1;
23,615✔
1148
                        }
1149
                }
1150
                return true;
590,516✔
1151
        }
1152
        constexpr void setfraction(uint64_t raw_bits) {
610✔
1153
                // unoptimized as it is not meant to be an end-user API, it is a test API
1154
                // raw_bits is uint64_t so can have at most 64 bits of fraction data
1155
                constexpr unsigned bitsToSet = (fbits < 64) ? fbits : 64;
610✔
1156
                uint64_t mask{ 1ull };
610✔
1157
                for (unsigned i = 0; i < bitsToSet; ++i) {
24,744✔
1158
                        setbit(i, (mask & raw_bits));
24,134✔
1159
                        mask <<= 1;
24,134✔
1160
                }
1161
        }
610✔
1162
        constexpr void setbit(unsigned i, bool v = true) noexcept {
7,476,577✔
1163
                unsigned blockIndex = i / bitsInBlock;
7,476,577✔
1164
                if (blockIndex < nrBlocks) {
7,476,577✔
1165
                        bt block = _block[blockIndex];
7,476,577✔
1166
                        bt null = ~(1ull << (i % bitsInBlock));
7,476,577✔
1167
                        bt bit = bt(v ? 1 : 0);
7,476,577✔
1168
                        bt mask = bt(bit << (i % bitsInBlock));
7,476,577✔
1169
                        _block[blockIndex] = bt((block & null) | mask);
7,476,577✔
1170
                }
1171
        }
7,476,577✔
1172
        constexpr cfloat& setbits(uint64_t raw_bits) noexcept {
294,354,327✔
1173
                if constexpr (0 == nrBlocks) {
1174
                        return *this;
1175
                }
1176
                else if constexpr (1 == nrBlocks) {
1177
                        _block[0] = raw_bits & storageMask;
89,194,714✔
1178
                }
1179
                else if constexpr (2 == nrBlocks) {
1180
                        if constexpr (bitsInBlock < 64) {
1181
                                _block[0] = raw_bits & storageMask;
101,167,839✔
1182
                                raw_bits >>= bitsInBlock;
101,167,839✔
1183
                                _block[1] = raw_bits & storageMask;
101,167,839✔
1184
                        }
1185
                        else {
1186
                                _block[0] = raw_bits & storageMask;
1187
                                _block[1] = 0;
1188
                        }
1189
                }
1190
                else if constexpr (3 == nrBlocks) {
1191
                        if constexpr (bitsInBlock < 64) {
1192
                                _block[0] = raw_bits & storageMask;
103,831,924✔
1193
                                raw_bits >>= bitsInBlock;
103,831,924✔
1194
                                _block[1] = raw_bits & storageMask;
103,831,924✔
1195
                                raw_bits >>= bitsInBlock;
103,831,924✔
1196
                                _block[2] = raw_bits & storageMask;
103,831,924✔
1197
                        }
1198
                        else {
1199
                                _block[0] = raw_bits & storageMask;
1200
                                _block[1] = 0;
1201
                                _block[2] = 0;
1202
                        }
1203
                }
1204
                else if constexpr (4 == nrBlocks) {
1205
                        if constexpr (bitsInBlock < 64) {
1206
                                _block[0] = raw_bits & storageMask;
70,092✔
1207
                                raw_bits >>= bitsInBlock;
70,092✔
1208
                                _block[1] = raw_bits & storageMask;
70,092✔
1209
                                raw_bits >>= bitsInBlock;
70,092✔
1210
                                _block[2] = raw_bits & storageMask;
70,092✔
1211
                                raw_bits >>= bitsInBlock;
70,092✔
1212
                                _block[3] = raw_bits & storageMask;
70,092✔
1213
                        }
1214
                        else {
1215
                                _block[0] = raw_bits & storageMask;
1216
                                _block[1] = 0;
1217
                                _block[2] = 0;
1218
                                _block[3] = 0;
1219
                        }
1220
                }
1221
                else {
1222
                        if constexpr (bitsInBlock < 64) {
1223
                                for (unsigned i = 0; i < nrBlocks; ++i) {
728,696✔
1224
                                        _block[i] = raw_bits & storageMask;
638,938✔
1225
                                        raw_bits >>= bitsInBlock;
638,938✔
1226
                                }
1227
                        }
1228
                        else {
1229
                                _block[0] = raw_bits & storageMask;
1230
                                for (unsigned i = 1; i < nrBlocks; ++i) {
1231
                                        _block[i] = 0;
1232
                                }
1233
                        }
1234
                }
1235
                _block[MSU] &= MSU_MASK; // enforce precondition for fast comparison by properly nulling bits that are outside of nbits
294,354,327✔
1236
                return *this;
294,354,327✔
1237
        }
1238
        constexpr void setblock(unsigned b, bt data) noexcept {
542,416✔
1239
                if (b < nrBlocks) _block[b] = data;
542,416✔
1240
        }
542,416✔
1241
        
1242
        // create specific number system values of interest
1243
        constexpr cfloat& maxpos() noexcept {
635,208✔
1244
                if constexpr (isSaturating) {
1245
                        // in a saturating encoding with max-exponent values we are removing the Inf encoding pattern 0b0'11...11'11...10 for a +inf, 
1246
                        // and 0b1'11...11'11...110 for a -inf and using it as a value
1247
                        if constexpr (hasMaxExpValues) {
1248
                                // maximum positive value has this bit pattern: 0-1...1-111...110, that is, sign = 0, e = 11..11, f = 111...110
1249
                                clear();
634,552✔
1250
                                flip();
634,552✔
1251
                                setbit(nbits - 1ull, false); // sign = 0
634,552✔
1252
                                setbit(0ull, false); // bit0 = 0
634,552✔
1253
                        }
1254
                        else {
1255
                                // maximum positive value has this bit pattern: 0-11...10-111...111, that is, sign = 0, e = 11..10, f = 111...111
1256
                                clear();
417✔
1257
                                flip();
417✔
1258
                                setbit(fbits, false); // set least significant exponent bit to 0
417✔
1259
                                setbit(nbits - 1ull, false); // set sign to 0
417✔
1260
                        }
1261
                }
1262
                else {
1263
                        // the Inf encoding is the pattern 0b0'11...11'11...10 for a +inf, and 0b1'11...11'11...110 for a -inf
1264
                        // the maxpos is the encoding before that
1265
                        if constexpr (hasMaxExpValues) {
1266
                                // maximum positive value has this bit pattern: 0-1...1-111...101, that is, sign = 0, e = 11..11, f = 111...101
1267
                                clear();
70✔
1268
                                flip();
70✔
1269
                                setbit(nbits - 1ull, false); // sign = 0
70✔
1270
                                setbit(1ull, false); // bit1 = 0
70✔
1271
                        }
1272
                        else {
1273
                                // maximum positive value has this bit pattern: 0-1...0-111...111, that is, sign = 0, e = 11..10, f = 111...111
1274
                                clear();
169✔
1275
                                flip();
169✔
1276
                                setbit(fbits, false); // set least significant exponent bit to 0
169✔
1277
                                setbit(nbits - 1ull, false); // set sign to 0
169✔
1278
                        }
1279
                }
1280
                return *this;
635,208✔
1281
        }
1282
        constexpr cfloat& minpos() noexcept {
13,201✔
1283
                // minpos encoding is not impacted by saturating encodings, which only affects maxpos and inf
1284
                if constexpr (hasSubnormals) {
1285
                        // minimum positive value has this bit pattern: 0-000-00...01, that is, sign = 0, e = 000, f = 00001
1286
                        clear();
327✔
1287
                        setbit(0);
327✔
1288
                }
1289
                else {
1290
                        // minimum positive value has this bit pattern: 0-001-00...0, that is, sign = 0, e = 001, f = 0000
1291
                        clear();
12,874✔
1292
                        setbit(fbits);
12,874✔
1293
                }
1294
                return *this;
13,201✔
1295
        }
1296
        constexpr cfloat& zero() noexcept {
1✔
1297
                // the zero value
1298
                clear();
1✔
1299
                return *this;
1✔
1300
        }
1301
        constexpr cfloat& minneg() noexcept {
11✔
1302
                // minneg encoding is not impacted by saturating encodings, which only affects maxpos and inf
1303
                if constexpr (hasSubnormals) {
1304
                        // minimum negative value has this bit pattern: 1-000-00...01, that is, sign = 1, e = 00, f = 00001
1305
                        clear();
8✔
1306
                        setbit(nbits - 1ull);
8✔
1307
                        setbit(0);
8✔
1308
                }
1309
                else {
1310
                        // minimum negative value has this bit pattern: 1-001-00...0, that is, sign = 1, e = 001, f = 0000
1311
                        clear();
3✔
1312
                        setbit(fbits);
3✔
1313
                        setbit(nbits - 1ull);
3✔
1314
                }
1315
                return *this;
11✔
1316
        }
1317
        constexpr cfloat& maxneg() noexcept {
634,959✔
1318
                if constexpr (isSaturating) {
1319
                        // in a saturating encoding with max-exponent values we are removing the Inf encoding pattern 0b0'11...11'11...10 for a +inf, 
1320
                        // and 0b1'11...11'11...110 for a -inf and using it as a value
1321
                        if constexpr (hasMaxExpValues) {
1322
                                // maximum negative value has this bit pattern: 1-1...1-111...110, that is, sign = 1, e = 1..1, f = 111...110
1323
                                clear();
634,501✔
1324
                                flip();
634,501✔
1325
                                setbit(0ull, false);
634,501✔
1326
                        }
1327
                        else {
1328
                                // maximum negative value has this bit pattern: 1-1...0-111...111, that is, sign = 1, e = 11..10, f = 111...111
1329
                                clear();
400✔
1330
                                flip();
400✔
1331
                                setbit(fbits, false);
400✔
1332
                        }
1333
                }
1334
                else {
1335
                        if constexpr (hasMaxExpValues) {
1336
                                // maximum negative value has this bit pattern: 1-1...1-111...101, that is, sign = 1, e = 1..1, f = 111...101
1337
                                clear();
12✔
1338
                                flip();
12✔
1339
                                setbit(1ull, false);
12✔
1340
                        }
1341
                        else {
1342
                                // maximum negative value has this bit pattern: 1-1...0-111...111, that is, sign = 1, e = 11..10, f = 111...111
1343
                                clear();
46✔
1344
                                flip();
46✔
1345
                                setbit(fbits, false);
46✔
1346
                        }
1347
                }
1348
                return *this;
634,959✔
1349
        }
1350

1351

1352
        /// <summary>
1353
        /// assign the value of the string representation to the cfloat
1354
        /// </summary>
1355
        /// <param name="stringRep">decimal scientific notation of a real number to be assigned</param>
1356
        /// <returns>reference to this cfloat</returns>
1357
        /// Clang doesn't support constexpr yet on string manipulations, so we need to make it conditional
1358
        CONSTEXPRESSION cfloat& assign(const std::string& str) noexcept {
7✔
1359
                clear();
7✔
1360
                unsigned nrChars = static_cast<unsigned>(str.size());
7✔
1361
                unsigned nrBits = 0;
7✔
1362
                unsigned nrDots = 0;
7✔
1363
                std::string bits;
7✔
1364
                if (nrChars > 2) {
7✔
1365
                        if (str[0] == '0' && str[1] == 'b') {
7✔
1366
                                for (unsigned i = 2; i < nrChars; ++i) {
211✔
1367
                                        char c = str[i];
204✔
1368
                                        switch (c) {
204✔
1369
                                        case '0':
186✔
1370
                                        case '1':
1371
                                                ++nrBits;
186✔
1372
                                                bits += c;
186✔
1373
                                                break;
186✔
1374
                                        case '.':
14✔
1375
                                                ++nrDots;
14✔
1376
                                                bits += c;
14✔
1377
                                                break;
14✔
1378
                                        case '\'':
4✔
1379
                                                // consume this delimiting character
1380
                                                break;
4✔
1381
                                        default:
×
1382
                                                std::cerr << "string contained a non-standard character: " << c << '\n';
×
1383
                                                return *this;
×
1384
                                        }
1385
                                }
1386
                        }
1387
                        else {
1388
                                std::cerr << "string must start with 0b: instead input pattern was " << str << '\n';
×
1389
                                return *this;
×
1390
                        }
1391
                }
1392
                else {
1393
                        std::cerr << "string is too short\n";
×
1394
                        return *this;
×
1395
                }
1396

1397
                if (nrBits != nbits) {
7✔
1398
                        std::cerr << "number of bits in the string is " << nrBits << " and needs to be " << nbits << '\n';
×
1399
                        return *this;
×
1400
                }
1401
                if (nrDots != 2) {
7✔
1402
                        std::cerr << "number of segment delimiters in string is " << nrDots << " and needs to be 2 for a cfloat<>\n";
×
1403
                        return *this;
×
1404
                }
1405

1406
                // assign the bits
1407
                int field{ 0 };  // three fields: sign, exponent, mantissa: fields are separated by a '.'
7✔
1408
                int nrExponentBits{ -1 };
7✔
1409
                unsigned bit = nrBits;
7✔
1410
                for (unsigned i = 0; i < bits.size(); ++i) {
207✔
1411
                        char c = bits[i];
200✔
1412
                        if (c == '.') {
200✔
1413
                                ++field;
14✔
1414
                                if (field == 2) { // just finished parsing exponent field: we can now check the number of exponent bits
14✔
1415
                                        if (nrExponentBits != es) {
7✔
1416
                                                std::cerr << "provided binary string representation does not contain " << es << " exponent bits. Found " << nrExponentBits << ". Reset to 0\n";
×
1417
                                                clear();
×
1418
                                                return *this;
×
1419
                                        }
1420
                                }
1421
                        }
1422
                        else {
1423
                                setbit(--bit, c == '1');
186✔
1424
                        }
1425
                        if (field == 1) { // exponent field
200✔
1426
                                ++nrExponentBits;
51✔
1427
                        }
1428
                }
1429
                if (field != 2) {
7✔
1430
                        std::cerr << "provided binary string did not contain three fields separated by '.': Reset to 0\n";
×
1431
                        clear();
×
1432
                        return *this;
×
1433
                }
1434
                return *this;
7✔
1435
        }
7✔
1436

1437
        // selectors
1438
        constexpr bool sign() const noexcept { return (_block[MSU] & SIGN_BIT_MASK) == SIGN_BIT_MASK; }
257,737,560✔
1439
        constexpr int  scale() const noexcept {
60,878,483✔
1440
                int e{ 0 };
60,878,483✔
1441
                if constexpr (MSU_CAPTURES_EXP) {
1442
                        e = static_cast<int>((_block[MSU] & ~SIGN_BIT_MASK) >> EXP_SHIFT);
49,230,865✔
1443
                        if (e == 0) {
49,230,865✔
1444
                                // subnormal scale is determined by fraction
1445
                                // subnormals: (-1)^s * 2^(2-2^(es-1)) * (f/2^fbits))
1446
                                e = (2l - (1l << (es - 1ull))) - 1;
6,529,819✔
1447
                                if constexpr (nbits > 2 + es) {
1448
                                        for (unsigned i = nbits - 2ull - es; i > 0; --i) {
12,571,179✔
1449
                                                if (test(i)) break;
12,466,782✔
1450
                                                --e;
6,063,885✔
1451
                                        }
1452
                                }
1453
                        }
1454
                        else {
1455
                                e -= EXP_BIAS;
42,701,046✔
1456
                        }
1457
                }
1458
                else {
1459
                        blockbinary<es, bt> ebits{};
11,647,792✔
1460
                        exponent(ebits);
11,647,618✔
1461
                        if (ebits.iszero()) {
11,647,618✔
1462
                                // subnormal scale is determined by fraction
1463
                                // subnormals: (-1)^s * 2^(2-2^(es-1)) * (f/2^fbits))
1464
                                e = (2l - (1l << (es - 1ull))) - 1;
1,599,561✔
1465
                                if constexpr (nbits > 2 + es) {
1466
                                        for (unsigned i = nbits - 2ull - es; i > 0; --i) {
3,158,572✔
1467
                                                if (test(i)) break;
3,151,456✔
1468
                                                --e;
1,559,022✔
1469
                                        }
1470
                                }
1471
                        }
1472
                        else {
1473
                                e = static_cast<int>(unsigned(ebits) - EXP_BIAS);
10,048,057✔
1474
                        }
1475
                }
1476
                return e;
60,878,483✔
1477
        }
1478
        constexpr bool isneg() const noexcept {
558✔
1479
                if (isnan()) return false;
558✔
1480
                return sign(); 
541✔
1481
        }
1482
        constexpr bool ispos() const noexcept { 
34,472✔
1483
                if (isnan()) return false;
34,472✔
1484
                return !sign(); 
34,472✔
1485
        }
1486
        constexpr bool iszero() const noexcept {
298,036,312✔
1487
                // NOTE: this is a very specific design that makes the decsion that
1488
                // for subnormal encodings found in a configuration that doesn't
1489
                // support them, we assume that these values map to 0.
1490
                if constexpr (hasSubnormals) {
1491
                        return iszeroencoding();
160,900,140✔
1492
                }
1493
                else { // all subnormals round to 0
1494
                        blockbinary<es, bt> ebits{};
137,136,614✔
1495
                        exponent(ebits);
137,136,172✔
1496
                        if (ebits.iszero()) return true; else return false;
137,136,172✔
1497
                }
1498
        }
1499
        constexpr bool isone() const noexcept {
1500
                // unbiased exponent = scale = 0, fraction = 0
1501
                int s = scale();
1502
                if (s == 0) {
1503
                        blockbinary<fbits, bt> f{};
1504
                        fraction(f);
1505
                        return f.iszero();
1506
                }
1507
                return false;
1508
        }
1509
        constexpr bool isinf(int InfType = INF_TYPE_EITHER) const noexcept {
373,577,650✔
1510
                // the bit pattern encoding of Inf is independent of the max-exponent value configuration
1511
                bool isNegInf = false;
373,577,650✔
1512
                bool isPosInf = false;
373,577,650✔
1513
                if constexpr (0 == nrBlocks) {
1514
                        return false;
1515
                }
1516
                else if constexpr (1 == nrBlocks) {
1517
                        isNegInf = (_block[MSU] & MSU_MASK) == (MSU_MASK ^ LSB_BIT_MASK);
167,822,430✔
1518
                        isPosInf = (_block[MSU] & MSU_MASK) == ((MSU_MASK ^ SIGN_BIT_MASK) ^ LSB_BIT_MASK);
167,822,430✔
1519
                }
1520
                else if constexpr (2 == nrBlocks) {
1521
                        bool isInf = (_block[0] == (BLOCK_MASK ^ LSB_BIT_MASK));
144,225,861✔
1522
                        isNegInf = isInf && ((_block[MSU] & MSU_MASK) == MSU_MASK);
144,225,861✔
1523
                        isPosInf = isInf && (_block[MSU] & MSU_MASK) == (MSU_MASK ^ SIGN_BIT_MASK);
144,225,861✔
1524
                }
1525
                else if constexpr (3 == nrBlocks) {
1526
                        bool isInf = (_block[0] == (BLOCK_MASK ^ LSB_BIT_MASK)) && (_block[1] == BLOCK_MASK);
61,337,270✔
1527
                        isNegInf = isInf && ((_block[MSU] & MSU_MASK) == MSU_MASK);
61,337,270✔
1528
                        isPosInf = isInf && (_block[MSU] & MSU_MASK) == (MSU_MASK ^ SIGN_BIT_MASK);
61,337,270✔
1529
                }
1530
                else if constexpr (4 == nrBlocks) {
1531
                        bool isInf = (_block[0] == (BLOCK_MASK ^ LSB_BIT_MASK)) && (_block[1] == BLOCK_MASK) && (_block[2] == BLOCK_MASK);
100,144✔
1532
                        isNegInf = isInf && ((_block[MSU] & MSU_MASK) == MSU_MASK);
100,144✔
1533
                        isPosInf = isInf && (_block[MSU] & MSU_MASK) == (MSU_MASK ^ SIGN_BIT_MASK);
100,144✔
1534
                }
1535
                else {
1536
                        bool isInf = (_block[0] == (BLOCK_MASK ^ LSB_BIT_MASK));
91,945✔
1537
                        for (unsigned i = 1; i < nrBlocks - 1; ++i) {
93,624✔
1538
                                if (_block[i] != BLOCK_MASK) {
93,306✔
1539
                                        isInf = false;
91,627✔
1540
                                        break;
91,627✔
1541
                                }
1542
                        }
1543
                        isNegInf = isInf && ((_block[MSU] & MSU_MASK) == MSU_MASK);
91,945✔
1544
                        isPosInf = isInf && (_block[MSU] & MSU_MASK) == (MSU_MASK ^ SIGN_BIT_MASK);
91,945✔
1545
                }
1546

1547
                return (InfType == INF_TYPE_EITHER ? (isNegInf || isPosInf) :
424,306,463✔
1548
                        (InfType == INF_TYPE_NEGATIVE ? isNegInf :
76,093,520✔
1549
                                (InfType == INF_TYPE_POSITIVE ? isPosInf : false)));
424,307,064✔
1550
        }
50,728,813✔
1551
        constexpr bool isnan(int NaNType = NAN_TYPE_EITHER) const noexcept {
963,155,670✔
1552
                if constexpr (hasMaxExpValues) {
1553
                        return isnanencoding(NaNType);
564,912,689✔
1554
                }
1555
                else {
1556
                        if (ismaxexpvalue()) {
398,242,981✔
1557
                                // all these max-exponent encodings are NANs, except for the encoding representing INF
1558
                                bool isNaN = isinf() ? false : true;
24,472,372✔
1559
                                bool isNegNaN = isNaN && sign();
24,472,372✔
1560
                                bool isPosNaN = isNaN && !sign();
24,472,372✔
1561
                                return (NaNType == NAN_TYPE_EITHER ? (isNaN) :
27,860,989✔
1562
                                        (NaNType == NAN_TYPE_SIGNALLING ? isNegNaN :
4,513,407✔
1563
                                                (NaNType == NAN_TYPE_QUIET ? isPosNaN : false)));
26,721,952✔
1564
                        }
1565
                        else {
1566
                                return false;
373,770,609✔
1567
                        }
1568
                }
1569
        }
24,472,372✔
1570
        // iszeroencoding returns true if it finds a pure -0 or +0 pattern and returns false otherwise
1571
        constexpr bool iszeroencoding() const noexcept {
266,803,894✔
1572
                if constexpr (0 == nrBlocks) {
1573
                        return true;
1574
                }
1575
                else if constexpr (1 == nrBlocks) {
1576
                        return (_block[MSU] & ~SIGN_BIT_MASK) == 0;
113,224,470✔
1577
                }
1578
                else if constexpr (2 == nrBlocks) {
1579
                        return (_block[0] == 0) && (_block[MSU] & ~SIGN_BIT_MASK) == 0;
120,500,367✔
1580
                }
1581
                else if constexpr (3 == nrBlocks) {
1582
                        return (_block[0] == 0) && _block[1] == 0 && (_block[MSU] & ~SIGN_BIT_MASK) == 0;
32,863,285✔
1583
                }
1584
                else if constexpr (4 == nrBlocks) {
1585
                        return (_block[0] == 0) && _block[1] == 0 && _block[2] == 0 && (_block[MSU] & ~SIGN_BIT_MASK) == 0;
123,267✔
1586
                }
1587
                else {
1588
                        for (unsigned i = 0; i < nrBlocks - 1; ++i) if (_block[i] != 0) return false;
337,787✔
1589
                        return (_block[MSU] & ~SIGN_BIT_MASK) == 0;
806✔
1590
                }
1591
        }
1592
        // isminnegencoding returns true if it find the pattern 1.00.00001 and returns false otherwise
1593
        constexpr bool isminnegencoding() const noexcept {
66,947✔
1594
                if constexpr (0 == nrBlocks) {
1595
                        return false;
1596
                }
1597
                else if constexpr (1 == nrBlocks) {
1598
                        return (_block[MSU] & (SIGN_BIT_MASK | 1ul));
1599
                }
1600
                else if constexpr (2 == nrBlocks) {
1601
                        return ((_block[0] == 1ul) && (_block[1] == SIGN_BIT_MASK));
1,408✔
1602
                }
1603
                else if constexpr (3 == nrBlocks) {
1604
                        return ((_block[0] == 1ul) && (_block[1] == 0) && (_block[2] == SIGN_BIT_MASK));
65,536✔
1605
                }
1606
                else if constexpr (4 == nrBlocks) {
1607
                        return ((_block[0] == 1ul) && (_block[1] == 0) && (_block[2] == 0) && (_block[3] == SIGN_BIT_MASK));
3✔
1608
                }
1609
                else {
1610
                        if (_block[0] != 1ul) return false;
×
1611
                        for (unsigned i = 1; i < nrBlocks - 2; ++i) if (_block[i] != 0) return false;
×
1612
                        return (_block[MSU] == SIGN_BIT_MASK);
×
1613
                }
1614
        }
1615
        constexpr bool isnanencoding(int NaNType = NAN_TYPE_EITHER) const noexcept {
568,580,125✔
1616
                // the bit encoding of NaN is independent of the gradual overflow configuration
1617
                bool isNaN = true;
568,580,125✔
1618
                bool isNegNaN = false;
568,580,125✔
1619
                bool isPosNaN = false;
568,580,125✔
1620

1621
                if constexpr (0 == nrBlocks) {
1622
                        return false;
1623
                }
1624
                else if constexpr (1 == nrBlocks) {
1625
                }
1626
                else if constexpr (2 == nrBlocks) {
1627
                        isNaN = (_block[0] == BLOCK_MASK);
218,589,088✔
1628
                }
1629
                else if constexpr (3 == nrBlocks) {
1630
                        isNaN = (_block[0] == BLOCK_MASK) && (_block[1] == BLOCK_MASK);
175,051,929✔
1631
                }
1632
                else if constexpr (4 == nrBlocks) {
1633
                        isNaN = (_block[0] == BLOCK_MASK) && (_block[1] == BLOCK_MASK) && (_block[2] == BLOCK_MASK);
226,764✔
1634
                }
1635
                else {
1636
                        for (unsigned i = 0; i < nrBlocks - 1; ++i) {
269,798✔
1637
                                if (_block[i] != BLOCK_MASK) {
269,755✔
1638
                                        isNaN = false;
269,505✔
1639
                                        break;
269,505✔
1640
                                }
1641
                        }
1642
                }
1643
                isNegNaN = isNaN && ((_block[MSU] & MSU_MASK) == MSU_MASK);
568,580,125✔
1644
                isPosNaN = isNaN && (_block[MSU] & MSU_MASK) == (MSU_MASK ^ SIGN_BIT_MASK);
568,580,125✔
1645

1646
                return (NaNType == NAN_TYPE_EITHER ? (isNegNaN || isPosNaN) :
772,714,845✔
1647
                        (NaNType == NAN_TYPE_SIGNALLING ? isNegNaN :
306,054,270✔
1648
                                (NaNType == NAN_TYPE_QUIET ? isPosNaN : false)));
772,419,225✔
1649
        }
204,134,720✔
1650
        // isnormal returns true if 0 or exponent bits are not all zero or one, false otherwise
1651
        constexpr bool isnormal() const noexcept {
60,666,008✔
1652
                if (iszeroencoding()) return true; // filter out the one special case
60,666,008✔
1653
                blockbinary<es, bt> e{};
60,666,181✔
1654
                exponent(e);
60,666,007✔
1655
                return !e.iszero() && !e.all();
60,666,007✔
1656
        }
1657
        // isdenormal returns true if exponent bits are all zero, false otherwise
1658
        constexpr bool isdenormal() const noexcept {
45,170,788✔
1659
                if (iszeroencoding()) return false; // filter out the one special case
45,170,788✔
1660
                blockbinary<es, bt> e{};
45,162,009✔
1661
                exponent(e);
45,162,009✔
1662
                return e.iszero(); 
45,162,009✔
1663
        }
1664
        // ismaxexpvalue returns true if exponent bits are all one, false otherwise
1665
        constexpr bool ismaxexpvalue() const noexcept {
401,488,537✔
1666
                blockbinary<es, bt> e{};
401,489,551✔
1667
                exponent(e);
401,488,537✔
1668
                return e.all();
401,488,537✔
1669
        }
1670
        // isinteger is TBD
1671
        constexpr bool isinteger() const noexcept { return false; } // return (floor(*this) == *this) ? true : false; }
1672
        
1673
        template<typename NativeReal>
1674
        constexpr bool inrange(NativeReal v) const noexcept {
9,306,296✔
1675
                // the valid range for this cfloat includes the interval between 
1676
                // maxpos and the value that would round down to maxpos
1677
                bool bIsInRange = true;                
9,306,296✔
1678
                if (v > 0) {
9,306,296✔
1679
                        cfloat c(SpecificValue::maxpos);
4,427,047✔
1680
                        cfloat<nbits + 1, es, BlockType, hasSubnormals, hasMaxExpValues, isSaturating> d{};
8,027,370✔
1681
                        d = NativeReal(c);
4,427,047✔
1682
                        ++d;
4,427,047✔
1683
                        if (v >= NativeReal(d)) bIsInRange = false;
4,427,047✔
1684
                }
1685
                else {
1686
                        cfloat c(SpecificValue::maxneg);
4,879,249✔
1687
                        cfloat<nbits + 1, es, BlockType, hasSubnormals, hasMaxExpValues, isSaturating> d{};
8,816,662✔
1688
                        d = NativeReal(c);
4,879,249✔
1689
                        --d;
4,879,249✔
1690
                        if (v <= NativeReal(d)) bIsInRange = false;
4,879,249✔
1691
                }
1692

1693
                return bIsInRange;
9,306,296✔
1694
        }
1695
        constexpr bool test(unsigned bitIndex) const noexcept {
15,628,900✔
1696
                return at(bitIndex);
15,628,900✔
1697
        }
1698
        constexpr bool at(unsigned bitIndex) const noexcept {
1,824,573,082✔
1699
                if (bitIndex < nbits) {
1,824,573,082✔
1700
                        bt word = _block[bitIndex / bitsInBlock];
1,824,573,082✔
1701
                        bt mask = bt(1ull << (bitIndex % bitsInBlock));
1,824,573,082✔
1702
                        return (word & mask);
1,824,573,082✔
1703
                }
1704
                return false;
×
1705
        }
1706
        constexpr uint8_t nibble(unsigned n) const noexcept {
640✔
1707
                if (n < (1 + ((nbits - 1) >> 2))) {
640✔
1708
                        bt word = _block[(n * 4) / bitsInBlock];
640✔
1709
                        int nibbleIndexInWord = int(n % (bitsInBlock >> 2ull));
640✔
1710
                        bt mask = bt(0xF << (nibbleIndexInWord * 4));
640✔
1711
                        bt nibblebits = bt(mask & word);
640✔
1712
                        return uint8_t(nibblebits >> (nibbleIndexInWord * 4));
640✔
1713
                }
1714
                return 0;
×
1715
        }
1716
        constexpr bt block(unsigned b) const noexcept {
1717
                if (b < nrBlocks) {
1718
                        return _block[b];
1719
                }
1720
                return 0;
1721
        }
1722

1723
        constexpr void sign(bool& s) const {
800✔
1724
                s = sign();
800✔
1725
        }
800✔
1726
        constexpr void exponent(blockbinary<es, bt>& e) const {
762,370,636✔
1727
                e.clear();
762,370,636✔
1728
                if constexpr (0 == nrBlocks) return;
1729
                else if constexpr (1 == nrBlocks) {
1730
                        bt ebits = bt(_block[MSU] & ~SIGN_BIT_MASK);
351,596,617✔
1731
                        e.setbits(uint64_t(ebits >> EXP_SHIFT));
351,596,617✔
1732
                }
1733
                else if constexpr (nrBlocks > 1) {
1734
                        if (MSU_CAPTURES_EXP) {
1735
                                bt ebits = bt(_block[MSU] & ~SIGN_BIT_MASK);
239,453,627✔
1736
                                e.setbits(uint64_t(ebits >> ((nbits - 1ull - es) % bitsInBlock)));
239,453,627✔
1737
                        }
1738
                        else {
1739
                                for (unsigned i = 0; i < es; ++i) { e.setbit(i, at(nbits - 1ull - es + i)); }
772,324,589✔
1740
                        }
1741
                }
1742
        }
762,370,636✔
1743
        template<unsigned targetFractionBits>
1744
        constexpr blockbinary<targetFractionBits, bt>& fraction(blockbinary<targetFractionBits, bt>& f) const {
34,084✔
1745
                static_assert(targetFractionBits >= fbits, "target blockbinary is too small and can't receive all fraction bits");
1746
                f.clear();
34,084✔
1747
                if constexpr (0 == nrBlocks) return f;
1748
                else if constexpr (1 == nrBlocks) {
1749
                        bt fraction = bt(_block[MSU] & ~MSU_EXP_MASK);
1,001✔
1750
                        f.setbits(fraction);
1,001✔
1751
                }
1752
                else if constexpr (nrBlocks > 1) {
1753
                        for (unsigned i = 0; i < fbits; ++i) { f.setbit(i, at(i)); }
242,396✔
1754
                }
1755
                return f;
34,084✔
1756
        }
1757
        constexpr uint64_t fraction_ull() const {
59,610,456✔
1758
                uint64_t raw{ 0 };
59,610,456✔
1759
                if constexpr (nbits - es - 1ull < 65ull) { // no-op if precondition doesn't hold
1760
                        if constexpr (1 == nrBlocks) {
1761
                                uint64_t fbitMask = 0xFFFF'FFFF'FFFF'FFFF >> (64 - fbits);
26,560,351✔
1762
                                raw = fbitMask & uint64_t(_block[0]);
26,560,351✔
1763
                        }
1764
                        else if constexpr (2 == nrBlocks) {
1765
                                uint64_t fbitMask = 0xFFFF'FFFF'FFFF'FFFF >> (64 - fbits);
22,777,998✔
1766
                                raw = fbitMask & ((uint64_t(_block[1]) << bitsInBlock) | uint64_t(_block[0]));
22,777,998✔
1767
                        }
1768
                        else if constexpr (3 == nrBlocks) {
1769
                                uint64_t fbitMask = 0xFFFF'FFFF'FFFF'FFFF >> (64 - fbits);
10,249,549✔
1770
                                raw = fbitMask & ((uint64_t(_block[2]) << (2 * bitsInBlock)) | (uint64_t(_block[1]) << bitsInBlock) | uint64_t(_block[0]));
10,249,549✔
1771
                        }
1772
                        else if constexpr (4 == nrBlocks) {
1773
                                uint64_t fbitMask = 0xFFFF'FFFF'FFFF'FFFF >> (64 - fbits);
22,320✔
1774
                                raw = fbitMask & ((uint64_t(_block[3]) << (3 * bitsInBlock)) | (uint64_t(_block[2]) << (2 * bitsInBlock)) | (uint64_t(_block[1]) << bitsInBlock) | uint64_t(_block[0]));
22,320✔
1775
                        }
1776
                        else {
1777
                                uint64_t mask{ 1 };
238✔
1778
                                for (unsigned i = 0; i < fbits; ++i) { 
10,900✔
1779
                                        if (test(i)) {
10,662✔
1780
                                                raw |= mask;
1,983✔
1781
                                        }
1782
                                        mask <<= 1;
10,662✔
1783
                                }
1784
                        }
1785
                }
1786
                return raw;
59,610,456✔
1787
        }
1788
        // construct the significant from the encoding, returns normalization offset
1789
        constexpr unsigned significant(blockbinary<fhbits, bt>& s, bool isNormal = true) const {
23✔
1790
                unsigned shift = 0;
23✔
1791
                if (iszero()) return 0;
23✔
1792
                if constexpr (0 == nrBlocks) return 0;
1793
                else if constexpr (1 == nrBlocks) {
1794
                        bt significant = bt(_block[MSU] & ~MSU_EXP_MASK & ~SIGN_BIT_MASK);
23✔
1795
                        if (isNormal) {
23✔
1796
                                significant |= (bt(0x1ul) << fbits);
×
1797
                        }
1798
                        else {
1799
                                unsigned msb = find_msb(significant);
23✔
1800
//                                std::cout << "msb : " << msb << " : fhbits : " << fhbits << " : " << to_binary(significant, true) << std::endl;
1801
                                shift = fhbits - msb;
23✔
1802
                                significant <<= shift;
23✔
1803
                        }
1804
                        s.setbits(significant);
23✔
1805
                }
1806
                else if constexpr (nrBlocks > 1) {
1807
                        s.clear();
1808
                        // TODO: design and implement a block-oriented algorithm, this sequential algorithm is super slow
1809
                        if (isNormal) {
1810
                                s.setbit(fbits);
1811
                                for (unsigned i = 0; i < fbits; ++i) { s.setbit(i, at(i)); }
1812
                        }
1813
                        else {
1814
                                // Find the MSB of the subnormal: 
1815
                                unsigned msb = 0;
1816
                                for (unsigned i = 0; i < fbits; ++i) { // msb protected from not being assigned through iszero test at prelude of function
1817
                                        msb = fbits - 1ull - i;
1818
                                        if (test(msb)) break;
1819
                                }
1820
                                //      m-----lsb
1821
                                // h00001010101
1822
                                // 101010100000
1823
                                for (unsigned i = 0; i <= msb; ++i) {
1824
                                        s.setbit(fbits - msb + i, at(i));
1825
                                }
1826
                                shift = fhbits - msb;
1827
                        }
1828
                }
1829
                return shift;
23✔
1830
        }
1831
        template<unsigned targetbits>
1832
        constexpr void bits(blockbinary<targetbits, bt>& b) const {
640✔
1833
                unsigned upperbound = (nbits > targetbits ? targetbits : nbits);
640✔
1834
                b.clear();
640✔
1835
                for (unsigned i = 0; i < upperbound; ++i) { b.setbit(i, at(i)); }
3,840✔
1836
        }
640✔
1837

1838
        // casts to native types
1839
        int to_int() const {
59,976✔
1840
                if (isnan()) return 0;
59,976✔
1841
                if (isinf()) return sign() ? std::numeric_limits<int>::min() : std::numeric_limits<int>::max();
56,644✔
1842
                return int(to_native<float>());
50,308✔
1843
        }
1844
        long to_long() const {
1845
                if (isnan()) return 0;
1846
                if (isinf()) return sign() ? std::numeric_limits<long>::min() : std::numeric_limits<long>::max();
1847
                return long(to_native<double>());
1848
        }
1849
        long long to_long_long() const {
1✔
1850
                if (isnan()) return 0;
1✔
1851
                if (isinf()) return sign() ? std::numeric_limits<long long>::min() : std::numeric_limits<long long>::max();
1✔
1852
                return (long long)(to_native<double>());
1✔
1853
        }
1854

1855
        // transform an cfloat to a native C++ floating-point. We are using the native
1856
        // precision to compute, which means that all sub-values need to be representable 
1857
        // by the native precision.
1858
        // A more accurate approximation would require an adaptive precision algorithm
1859
        // with a final rounding step.
1860
        template<typename TargetFloat>
1861
        TargetFloat to_native() const { 
113,460,959✔
1862
                TargetFloat v{ 0.0 };
113,460,959✔
1863
                if (iszero()) {
113,460,959✔
1864
                        // the optimizer might destroy the sign
1865
                        return sign() ? -TargetFloat(0) : TargetFloat(0);
910,576✔
1866
                }
1867
                else if (isnan()) {
112,550,383✔
1868
                        v = sign() ? std::numeric_limits<TargetFloat>::signaling_NaN() : std::numeric_limits<TargetFloat>::quiet_NaN();
6,137,548✔
1869
                }
1870
                else if (isinf()) {
106,412,835✔
1871
                        v = sign() ? -std::numeric_limits<TargetFloat>::infinity() : std::numeric_limits<TargetFloat>::infinity();
143,342✔
1872
                }
1873
                else { // TODO: this approach has catastrophic cancellation when nbits is large and native target float is too small
1874
                        TargetFloat f{ 0 };
106,269,493✔
1875
                        TargetFloat fbit{ 0.5 };
106,269,493✔
1876
                        for (int i = static_cast<int>(nbits - 2ull - es); i >= 0; --i) {
1,313,951,655✔
1877
                                f += at(static_cast<unsigned>(i)) ? fbit : TargetFloat(0);
1,207,682,162✔
1878
                                fbit *= TargetFloat(0.5);
1,207,682,162✔
1879
                        }
1880
                        blockbinary<es, bt> ebits;
1881
                        exponent(ebits);
106,269,493✔
1882
                        if constexpr (hasSubnormals) {
1883
                                if (ebits.iszero()) {
66,507,137✔
1884
                                        // subnormals: (-1)^s * 2^(2-2^(es-1)) * (f/2^fbits))
1885
                                        TargetFloat exponentiation = TargetFloat(subnormal_exponent[es]); // precomputed values for 2^(2-2^(es-1))
14,672,511✔
1886
                                        v = exponentiation * f;  // f is already f/2^fbits
14,672,511✔
1887
                                        return sign() ? -v : v;
14,672,511✔
1888
                                }
1889
                        }
1890
                        else {
1891
                                if (ebits.iszero()) { // underflow to 0
39,762,356✔
1892
                                        // compiler fast float optimization might destroy the sign
1893
                                        return sign() ? -TargetFloat(0) : TargetFloat(0);
×
1894
                                }
1895
                        }
1896
                        if constexpr (hasMaxExpValues) {
1897
                                // regular: (-1)^s * 2^(e+1-2^(es-1)) * (1 + f/2^fbits))
1898
                                int exponent = static_cast<int>(unsigned(ebits) - EXP_BIAS);
52,318,538✔
1899
                                if (-64 < exponent && exponent < 64) {
52,318,538✔
1900
                                        TargetFloat exponentiation = (exponent >= 0 ? TargetFloat(1ull << exponent) : (1.0f / TargetFloat(1ull << -exponent)));
51,994,709✔
1901
                                        v = exponentiation * (TargetFloat(1.0) + f);
51,994,709✔
1902
                                }
51,994,709✔
1903
                                else {
1904
                                        double exponentiation = ipow(exponent);
323,829✔
1905
                                        v = TargetFloat(exponentiation * (1.0 + f));
323,829✔
1906
                                }
1907
                        }
1908
                        else {
1909
                                if (ebits.all()) {
39,278,444✔
1910
                                        // max-exponent values are mapped to quiet NaNs
1911
                                        v = std::numeric_limits<TargetFloat>::quiet_NaN();
×
1912
                                        return v;
×
1913
                                }
1914
                                else {
1915
                                        // regular: (-1)^s * 2^(e+1-2^(es-1)) * (1 + f/2^fbits))
1916
                                        int exponent = static_cast<int>(unsigned(ebits) - EXP_BIAS);
39,278,444✔
1917
                                        if (-64 < exponent && exponent < 64) {
39,278,444✔
1918
                                                TargetFloat exponentiation = (exponent >= 0 ? TargetFloat(1ull << exponent) : (1.0f / TargetFloat(1ull << -exponent)));
39,077,043✔
1919
                                                v = exponentiation * (TargetFloat(1.0) + f);
39,077,043✔
1920
                                        }
39,077,043✔
1921
                                        else {
1922
                                                double exponentiation = ipow(exponent);
201,401✔
1923
                                                v = TargetFloat(exponentiation * (1.0 + f));
201,401✔
1924
                                        }
1925
                                }
1926
                        }
1927
                        v = sign() ? -v : v;
91,596,982✔
1928
                }
1929
                return v;
97,877,872✔
1930
        }
1931

1932
        // convert a cfloat to a blocktriple with the fraction format 1.ffff
1933
        // we are using the same block type so that we can use block copies to move bits around.
1934
        // Since we tend to have at least two exponent bits, this will lead to
1935
        // most cfloat<->blocktriple cases being efficient as the block types are aligned.
1936
        // The relationship between the source cfloat and target blocktriple is not
1937
        // arbitrary, enforce it by setting the blocktriple fbits to the cfloat's (nbits - es - 1)
1938
        template<typename TargetBlockType = bt>
1939
        constexpr void normalize(blocktriple<fbits, BlockTripleOperator::REP, TargetBlockType>& tgt) const {
37✔
1940
                // test special cases
1941
                if (isnan()) {
37✔
1942
                        tgt.setnan();
×
1943
                }
1944
                else if (isinf()) {
37✔
1945
                        tgt.setinf();
×
1946
                }
1947
                else if (iszero()) {
37✔
1948
                        tgt.setzero();
×
1949
                }
1950
                else {
1951
                        tgt.setnormal(); // a blocktriple is always normalized
37✔
1952
                        int scale = this->scale();
37✔
1953
                        tgt.setsign(sign());
37✔
1954
                        tgt.setscale(scale);
37✔
1955
                        // set significant
1956
                        // we are going to unify to the format 01.ffffeeee
1957
                        // where 'f' is a fraction bit, and 'e' is an extension bit
1958
                        // so that normalize can be used to generate blocktriples for add/sub/mul/div/sqrt
1959
                        if (isnormal()) {
37✔
1960
                                if constexpr (fbits < 64) { // max 63 bits of fraction to yield 64bit of raw significant bits
1961
                                        uint64_t raw = fraction_ull();
37✔
1962
                                        raw |= (1ull << fbits);
37✔
1963
                                        tgt.setbits(raw);
37✔
1964
                                }
1965
                                else {
1966
                                        blockcopy(tgt);
1967
                                        tgt.setbit(fbits);
1968
                                }
1969
                        }
1970
                        else { // it is a subnormal encoding in this target cfloat
1971
                                int shift = MIN_EXP_NORMAL - scale;
×
1972
                                if constexpr (fbits < 64) {
1973
                                        uint64_t raw = fraction_ull();
×
1974
                                        raw <<= shift;
×
1975
                                        raw |= (1ull << fbits);
×
1976
                                        tgt.setbits(raw);
×
1977
                                }
1978
                                else {
1979
                                        blockcopy(tgt);
1980
                                        tgt <<= shift;
1981
                                        tgt.setbit(fbits);
1982
                                }
1983
                        }
1984
                }
1985
        }
37✔
1986

1987
        // normalize a cfloat to a blocktriple used in add/sub, which has the form 00h.fffff
1988
        // that is 3 + fbits, the 3 extra bits are required to be able to use 2's complement 
1989
        // and capture the largest value of an addition/subtraction.
1990
        // TODO: currently abits = 2*fhbits as the worst case input argument size to
1991
        // capture the smallest normal value in aligned form. There is a faster/smaller
1992
        // implementation where the input is constrainted to just the round, guard, and sticky bits.
1993
        constexpr void normalizeAddition(blocktriple<fbits, BlockTripleOperator::ADD, bt>& tgt) const {
39,479,357✔
1994
                using BlockTripleConfiguration = blocktriple<fbits, BlockTripleOperator::ADD, bt>;
1995
                // test special cases
1996
                if (isnan()) {
39,479,357✔
1997
                        tgt.setnan();
396,494✔
1998
                }
1999
                else if (isinf()) {
39,082,863✔
2000
                        tgt.setinf();
326✔
2001
                }
2002
                else if (iszero()) {
39,082,537✔
2003
                        tgt.setzero();
325,672✔
2004
                }
2005
                else {
2006
                        tgt.setnormal(); // a blocktriple is always normalized
38,756,865✔
2007
                        int scale = this->scale();
38,756,865✔
2008
                        tgt.setsign(sign());
38,756,865✔
2009
                        tgt.setscale(scale);
38,756,865✔
2010
                        // set significant
2011
                        // we are going to unify to the format 001.ffffeeee
2012
                        // where 'f' is a fraction bit, and 'e' is an extension bit
2013
                        // so that normalize can be used to generate blocktriples for add/sub/mul/div/sqrt
2014
                        if (isnormal()) {
38,756,865✔
2015
                                if constexpr (fbits < 64 && BlockTripleConfiguration::rbits < (64 - fbits)) {
2016
                                        uint64_t raw = fraction_ull();
26,002,198✔
2017
                                        raw |= (1ull << fbits); // add the hidden bit
26,002,198✔
2018
                                        //std::cout << "normalize      : " << *this << '\n';
2019
                                        //std::cout << "significant    : " << to_binary(raw, fbits + 2) << '\n';
2020
                                        raw <<= BlockTripleConfiguration::rbits;  // rounding bits required for correct rounding
26,002,198✔
2021
                                        //std::cout << "rounding shift : " << to_binary(raw, fbits + 2 + BlockTripleConfiguration::rbits) << '\n';
2022
                                        tgt.setbits(raw);
26,002,198✔
2023
                                }
2024
                                else {
2025
                                        blockcopy(tgt);
962✔
2026
                                        tgt.setradix();
962✔
2027
                                        tgt.setbit(fbits); // add the hidden bit
962✔
2028
                                        tgt.bitShift(BlockTripleConfiguration::rbits);  // rounding bits required for correct rounding
962✔
2029
                                }
2030
                        }
2031
                        else {
2032
                                if (isdenormal()) { // it is a subnormal encoding in this target cfloat
12,753,705✔
2033
                                        if constexpr (hasSubnormals) {
2034
                                                if constexpr (BlockTripleConfiguration::rbits < (64 - fbits)) {
2035
                                                        uint64_t raw = fraction_ull();
6,462,043✔
2036
                                                        int shift = MIN_EXP_NORMAL - scale;
6,462,043✔
2037
                                                        raw <<= shift; // shift but do NOT add a hidden bit as the MSB of the subnormal is shifted in the hidden bit position
6,462,043✔
2038
                                                        raw <<= BlockTripleConfiguration::rbits;  // rounding bits required for correct rounding
6,462,043✔
2039
                                                        tgt.setbits(raw);
6,462,043✔
2040
                                                }
2041
                                                else {
2042
                                                        blockcopy(tgt);
2043
                                                        tgt.setradix();
2044
                                                        int shift = MIN_EXP_NORMAL - scale;
2045
                                                        tgt.bitShift(shift + BlockTripleConfiguration::rbits);  // rounding bits required for correct rounding
2046
                                                }
2047
                                        }
2048
                                        else {  // this cfloat has no subnormals
2049
                                                tgt.setzero(tgt.sign()); // preserve the sign
×
2050
                                        }
2051
                                }
2052
                                else {
2053
                                        // by design, a cfloat is either normal, subnormal, or max-exponent value, so this else clause is by deduction covering a max-exponent value
2054
                                        if constexpr (hasMaxExpValues) {
2055
                                                if constexpr (fbits < 64 && BlockTripleConfiguration::rbits < (64 - fbits)) {
2056
                                                        uint64_t raw = fraction_ull();
6,291,662✔
2057
                                                        raw |= (1ull << fbits); // add the hidden bit
6,291,662✔
2058
                                                        raw <<= BlockTripleConfiguration::rbits;  // rounding bits required for correct rounding
6,291,662✔
2059
                                                        tgt.setbits(raw);
6,291,662✔
2060
                                                }
2061
                                                else {
2062
                                                        blockcopy(tgt);
×
2063
                                                        tgt.setradix();
×
2064
                                                        tgt.setbit(fbits); // add the hidden bit
×
2065
                                                        tgt.bitShift(BlockTripleConfiguration::rbits);  // rounding bits required for correct rounding
×
2066
                                                }
2067
                                        }
2068
                                        else {  // this cfloat has no max-exponent values and thus this represents a nan, signalling or quiet determined by the sign
2069
                                                tgt.setnan(tgt.sign());
×
2070
                                        }                        
2071
                                }
2072
                        }
2073
                }
2074
                // tgt.setradix(radix);
2075
        }
39,479,357✔
2076

2077
        // Normalize a cfloat to a blocktriple used in mul, which has the form 0'00001.fffff
2078
        // that is 2*fbits, plus 1 overflow bit, and the radix set at <fbits>.
2079
        // The result radix will go to 2*fbits after multiplication.
2080
        constexpr void normalizeMultiplication(blocktriple<fbits, BlockTripleOperator::MUL, bt>& tgt) const {
13,914,390✔
2081
                // test special cases
2082
                if (isnan()) {
13,914,390✔
2083
                        tgt.setnan();
450,696✔
2084
                }
2085
                else if (isinf()) {
13,463,694✔
2086
                        tgt.setinf();
652✔
2087
                }
2088
                else if (iszero()) {
13,463,042✔
2089
                        tgt.setzero();
450,960✔
2090
                }
2091
                else {
2092
                        tgt.setnormal(); // a blocktriple is always normalized
13,012,082✔
2093
                        int scale = this->scale();
13,012,082✔
2094
                        tgt.setsign(sign());
13,012,082✔
2095
                        tgt.setscale(scale);
13,012,082✔
2096

2097
                        // set significant
2098
                        // we are going to unify to the format 01.ffffeeee
2099
                        // where 'f' is a fraction bit, and 'e' is an extension bit
2100
                        // so that normalize can be used to generate blocktriples for add/sub/mul/div/sqrt
2101
                        if (isnormal() || ismaxexpvalue()) {
13,012,082✔
2102
                                if constexpr (fbits < 64) { // max 63 bits of fraction to yield 64bit of raw significant bits
2103
                                        uint64_t raw = fraction_ull();
11,793,902✔
2104
                                        raw |= (1ull << fbits);
11,793,902✔
2105
                                        tgt.setbits(raw);
11,793,902✔
2106
                                }
2107
                                else {
2108
                                        blockcopy(tgt);
832✔
2109
                                        tgt.setradix();
832✔
2110
                                        tgt.setbit(fbits); // add the hidden bit
832✔
2111
                                }
2112
                        }
2113
                        else { 
2114
                                // it is a subnormal encoding in this target cfloat
2115
                                if constexpr (hasSubnormals) {
2116
                                        if constexpr (fbits < 64) {
2117
                                                uint64_t raw = fraction_ull();
1,217,348✔
2118
                                                int shift = MIN_EXP_NORMAL - scale;
1,217,348✔
2119
                                                raw <<= shift;
1,217,348✔
2120
                                                raw |= (1ull << fbits);
1,217,348✔
2121
                                                tgt.setbits(raw);
1,217,348✔
2122
                                        }
2123
                                        else {
2124
                                                blockcopy(tgt);
×
2125
                                                int shift = MIN_EXP_NORMAL - scale;
×
2126
                                                tgt.bitShift(shift);
×
2127
                                                tgt.setbit(fbits);
×
2128
                                        }
2129
                                }
2130
                                else { // this cfloat has no subnormals
2131
                                        tgt.setzero(tgt.sign()); // preserve the sign
×
2132
                                }
2133
                        }
2134
                }
2135
                tgt.setradix(fbits); // override the radix with the input scale for accurate value printing
13,914,390✔
2136
        }
13,914,390✔
2137

2138
        // normalize a cfloat to a blocktriple used in div, which has the form 0'00000'00001.fffff
2139
        // that is 3*fbits, plus 1 overflow bit, and the radix set at <fbits>.
2140
        // the result radix will go to 2*fbits after multiplication.
2141
        // TODO: needs implementation
2142
        constexpr void normalizeDivision(blocktriple<fbits, BlockTripleOperator::DIV, bt>& tgt) const {
8,897,106✔
2143
                constexpr unsigned divshift = blocktriple<fbits, BlockTripleOperator::DIV, bt>::divshift;
8,897,106✔
2144
                // test special cases
2145
                if (isnan()) {
8,897,106✔
2146
                        tgt.setnan();
38✔
2147
                }
2148
                else if (isinf()) {
8,897,068✔
2149
                        tgt.setinf();
6✔
2150
                }
2151
                else if (iszero()) {
8,897,062✔
2152
                        tgt.setzero();
44✔
2153
                }
2154
                else {
2155
                        tgt.setnormal(); // a blocktriple is always normalized
8,897,018✔
2156
                        int scale = this->scale();
8,897,018✔
2157
                        tgt.setsign(sign());
8,897,018✔
2158
                        tgt.setscale(scale);
8,897,018✔
2159
                        // set significant
2160
                        // we are going to unify to the format 01.ffffeeee
2161
                        // where 'f' is a fraction bit, and 'e' is an extension bit
2162
                        // so that normalize can be used to generate blocktriples for add/sub/mul/div/sqrt
2163
                        if (isnormal() || ismaxexpvalue()) {
8,897,018✔
2164
                                if constexpr (fbits < 64 && divshift < (64 - fbits)) {
2165
                                        uint64_t raw = fraction_ull();
7,397,202✔
2166
                                        raw |= (1ull << fbits);
7,397,202✔
2167
                                        raw <<= divshift; // shift the input value to the output radix
7,397,202✔
2168
                                        tgt.setbits(raw);
7,397,202✔
2169
                                }
2170
                                else {
2171
                                        // brute force copy of blocks
2172
                                        blockcopy(tgt);
1,054,350✔
2173
                                        tgt.setbit(fbits);
1,054,350✔
2174
                                        tgt.bitShift(divshift); // shift the input value to the output radix
1,054,350✔
2175
                                }
2176
                        }
2177
                        else { // it is a subnormal encoding in this target cfloat
2178
                                if constexpr (fbits < 64 && divshift < (64 - fbits)) {
2179
                                        uint64_t raw = fraction_ull();
445,464✔
2180
                                        int shift = MIN_EXP_NORMAL - scale;
445,464✔
2181
                                        raw <<= shift;
445,464✔
2182
                                        raw |= (1ull << fbits);
445,464✔
2183
                                        raw <<= divshift; // shift the input value to the output radix
445,464✔
2184
                                        tgt.setbits(raw);
445,464✔
2185
                                }
2186
                                else {
2187
                                        blockcopy(tgt);
2✔
2188
                                        int shift = MIN_EXP_NORMAL - scale;
2✔
2189
                                        tgt.bitShift(shift);
2✔
2190
                                        tgt.setbit(fbits);
2✔
2191
                                        tgt.bitShift(divshift); // shift the input value to the output radix
2✔
2192
                                }
2193
                        }
2194
                }
2195
                tgt.setradix(blocktriple<fbits, BlockTripleOperator::DIV, bt>::radix);
8,897,106✔
2196
        }
8,897,106✔
2197

2198
        // helper debug function
2199
        void constexprClassParameters() const noexcept {
8✔
2200
                std::cout << "-------------------------------------------------------------\n";
8✔
2201
                std::cout << "type              : " << typeid(*this).name() << '\n';
8✔
2202
                std::cout << "nbits             : " << nbits << '\n';
8✔
2203
                std::cout << "es                : " << es << std::endl;
8✔
2204
                std::cout << "hasSubnormals     : " << (hasSubnormals ? "true" : "false") << '\n';
8✔
2205
                std::cout << "hasMaxExpValues   : " << (hasMaxExpValues ? "true" : "false") << '\n';
8✔
2206
                std::cout << "isSaturating      : " << (isSaturating ? "true" : "false") << '\n';
8✔
2207
                std::cout << "ALL_ONES          : " << to_binary(ALL_ONES, 0, true) << '\n';
8✔
2208
                std::cout << "BLOCK_MASK        : " << to_binary(BLOCK_MASK, 0, true) << '\n';
8✔
2209
                std::cout << "nrBlocks          : " << nrBlocks << '\n';
8✔
2210
                std::cout << "bits in MSU       : " << bitsInMSU << '\n';
8✔
2211
                std::cout << "MSU               : " << MSU << '\n';
8✔
2212
                std::cout << "MSU MASK          : " << to_binary(MSU_MASK, 0, true) << '\n';
8✔
2213
                std::cout << "SIGN_BIT_MASK     : " << to_binary(SIGN_BIT_MASK, 0, true) << '\n';
8✔
2214
                std::cout << "LSB_BIT_MASK      : " << to_binary(LSB_BIT_MASK, 0, true) << '\n';
8✔
2215
                std::cout << "MSU CAPTURES_EXP  : " << (MSU_CAPTURES_EXP ? "yes\n" : "no\n");
8✔
2216
                std::cout << "EXP_SHIFT         : " << EXP_SHIFT << '\n';
8✔
2217
                std::cout << "MSU EXP MASK      : " << to_binary(MSU_EXP_MASK, 0, true) << '\n';
8✔
2218
                std::cout << "ALL_ONE_MASK_ES   : " << to_binary(ALL_ONES_ES) << '\n';
8✔
2219
                std::cout << "EXP_BIAS          : " << EXP_BIAS << '\n';
8✔
2220
                std::cout << "MAX_EXP           : " << MAX_EXP << '\n';
8✔
2221
                std::cout << "MIN_EXP_NORMAL    : " << MIN_EXP_NORMAL << '\n';
8✔
2222
                std::cout << "MIN_EXP_SUBNORMAL : " << MIN_EXP_SUBNORMAL << '\n';
8✔
2223
                std::cout << "fraction Blocks   : " << fBlocks << '\n';
8✔
2224
                std::cout << "bits in FSU       : " << bitsInFSU << '\n';
8✔
2225
                std::cout << "FSU               : " << FSU << '\n';
8✔
2226
                std::cout << "FSU MASK          : " << to_binary(FSU_MASK, 0, true) << '\n';
8✔
2227
                std::cout << "topfbits          : " << topfbits << '\n';
8✔
2228
                std::cout << "ALL_ONE_MASK_FR   : " << to_binary(ALL_ONES_FR) << '\n';
8✔
2229
        }
8✔
2230
        void showLimbs() const {
2231
                for (unsigned b = 0; b < nrBlocks; ++b) {
2232
                        std::cout << to_binary(_block[nrBlocks - b - 1], sizeof(bt) * 8) << ' ';
2233
                }
2234
                std::cout << '\n';
2235
        }
2236

2237
protected:
2238
        // HELPER methods
2239

2240
        /// <summary>
2241
        /// 1's complement of the encoding used to set up specific encoding patterns.
2242
        /// This is not an arithmetic operator that makes sense for floating-point numbers.
2243
        /// </summary>
2244
        /// <returns>reference to this cfloat object</returns>
2245
        constexpr cfloat& flip() noexcept { // in-place one's complement
1,270,167✔
2246
                for (unsigned i = 0; i < nrBlocks; ++i) {
4,823,102✔
2247
                        _block[i] = bt(~_block[i]);
3,552,935✔
2248
                }
2249
                _block[MSU] &= MSU_MASK; // assert precondition of properly nulled leading non-bits
1,270,167✔
2250
                return *this;
1,270,167✔
2251
        }
2252

2253
        /// <summary>
2254
        /// shift left is a bit level encoding helper for fast limb-based conversions between different cfloats
2255
        /// </summary>
2256
        /// <param name="bitsToShift"></param>
2257
        void shiftLeft(unsigned bitsToShift) {
60,139✔
2258
                if (bitsToShift == 0) return;
60,139✔
2259
                if (bitsToShift > nbits) {
60,139✔
2260
                        setzero();
×
2261
                }
2262
                if (bitsToShift >= bitsInBlock) {
60,139✔
2263
                        int blockShift = static_cast<int>(bitsToShift / bitsInBlock);
60,139✔
2264
                        for (int i = static_cast<int>(MSU); i >= blockShift; --i) {
140,267✔
2265
                                _block[i] = _block[i - blockShift];
80,128✔
2266
                        }
2267
                        for (int i = blockShift - 1; i >= 0; --i) {
340,788✔
2268
                                _block[i] = bt(0);
280,649✔
2269
                        }
2270
                        // adjust the shift
2271
                        bitsToShift -= blockShift * bitsInBlock;
60,139✔
2272
                        if (bitsToShift == 0) return;
60,139✔
2273
                }
2274
                if constexpr (MSU > 0) {
2275
                        // construct the mask for the upper bits in the block that need to move to the higher word
2276
                        bt mask = 0xFFFFFFFFFFFFFFFF << (bitsInBlock - bitsToShift);
60,093✔
2277
                        for (unsigned i = MSU; i > 0; --i) {
360,149✔
2278
                                _block[i] <<= bitsToShift;
300,056✔
2279
                                // mix in the bits from the right
2280
                                bt bits = bt(mask & _block[i - 1]);
300,056✔
2281
                                _block[i] |= (bits >> (bitsInBlock - bitsToShift));
300,056✔
2282
                        }
2283
                }
2284
                _block[0] <<= bitsToShift;
60,093✔
2285
        }
2286

2287
        // convert an unsigned integer into a cfloat
2288
        // TODO: this method does not protect against being called with a signed integer
2289
        template<typename Ty>
2290
        constexpr cfloat& convert_unsigned_integer(const Ty& rhs) noexcept {
140✔
2291
                clear();
140✔
2292
                if (0 == rhs) return *this;
140✔
2293

2294
                uint64_t raw = static_cast<uint64_t>(rhs);
137✔
2295
                int msb = static_cast<int>(find_msb(raw)) - 1; // msb > 0 due to zero test above 
137✔
2296
                int exponent = msb;
137✔
2297
                // remove the MSB as it represents the hidden bit in the cfloat representation
2298
                uint64_t hmask = ~(1ull << msb);
137✔
2299
                raw &= hmask;
137✔
2300

2301
                constexpr uint32_t sizeInBits = 8 * sizeof(Ty);
137✔
2302
                uint32_t shift = sizeInBits - exponent - 1;
137✔
2303
                raw <<= shift;
137✔
2304
                raw = round<sizeInBits, uint64_t>(raw, exponent);
137✔
2305

2306
                // construct the target cfloat
2307
                if constexpr (fbits < (64 - es)) {
2308
                        uint64_t biasedExponent = static_cast<uint64_t>(static_cast<long long>(exponent) + static_cast<long long>(EXP_BIAS));
107✔
2309
                        uint64_t bits = 0;
107✔
2310
                        bits <<= es;
107✔
2311
                        bits |= biasedExponent;
107✔
2312
                        bits <<= fbits;
107✔
2313
                        bits |= raw;
107✔
2314
                        setbits(bits);
107✔
2315
                }
2316
                else {
2317
                        setsign(false);
30✔
2318
                        setexponent(exponent);
30✔
2319
                        // For large types, place fraction bits at the TOP of the fraction field
2320
                        // After shift, raw has fraction bits at positions (sizeInBits-2) down to (sizeInBits-1-exponent)
2321
                        // We need to place them at positions (fbits-1) down to (fbits-exponent)
2322
                        for (int i = 0; i < exponent; ++i) {
291✔
2323
                                bool bit = (raw >> (sizeInBits - 2 - i)) & 1;
261✔
2324
                                setbit(static_cast<unsigned>(fbits - 1 - i), bit);
261✔
2325
                        }
2326
                }
2327
                return *this;
137✔
2328
        }
2329
        // convert a signed integer into a cfloat
2330
        // TODO: this method does not protect against being called with a signed integer
2331
        template<typename Ty>
2332
        constexpr cfloat& convert_signed_integer(const Ty& rhs) noexcept {
174,972✔
2333
                clear();
174,972✔
2334
                if (0 == rhs) return *this;
174,972✔
2335
                bool s = (rhs < 0);
33,377✔
2336
                using UnsignedTy = std::make_unsigned_t<Ty>;
2337
                UnsignedTy urhs = static_cast<UnsignedTy>(rhs);
33,377✔
2338
                uint64_t raw = static_cast<uint64_t>(s ? (UnsignedTy(0) - urhs) : urhs);
33,377✔
2339

2340
                int msb = static_cast<int>(find_msb(raw)) - 1; // msb > 0 due to zero test above 
33,377✔
2341
                int exponent = msb;
33,377✔
2342
                // remove the MSB as it represents the hidden bit in the cfloat representation
2343
                uint64_t hmask = ~(1ull << msb);
33,377✔
2344
                raw &= hmask;
33,377✔
2345

2346
                // shift the msb to the msb of the fraction
2347
                constexpr uint32_t sizeInBits = 8 * sizeof(Ty);
33,377✔
2348
                uint32_t shift = sizeInBits - exponent - 1;
33,377✔
2349
                raw <<= shift;
33,377✔
2350
                raw = round<sizeInBits, uint64_t>(raw, exponent);
33,377✔
2351

2352
                // construct the target cfloat
2353
                if constexpr (fbits < (64 - es)) {
2354
                        uint64_t biasedExponent = static_cast<uint64_t>(static_cast<long long>(exponent) + static_cast<long long>(EXP_BIAS));
33,006✔
2355
                        uint64_t bits = (s ? 1ull : 0ull);
33,006✔
2356
                        bits <<= es;
33,006✔
2357
                        bits |= biasedExponent;
33,006✔
2358
                        bits <<= fbits;
33,006✔
2359
                        bits |= raw;
33,006✔
2360
                        setbits(bits);
33,006✔
2361
                }
2362
                else {
2363
                        setsign(s);
371✔
2364
                        setexponent(exponent);
371✔
2365
                        // For large types, place fraction bits at the TOP of the fraction field
2366
                        // After shift, raw has fraction bits at positions (sizeInBits-2) down to (sizeInBits-1-exponent)
2367
                        // We need to place them at positions (fbits-1) down to (fbits-exponent)
2368
                        for (int i = 0; i < exponent; ++i) {
3,120✔
2369
                                bool bit = (raw >> (sizeInBits - 2 - i)) & 1;
2,749✔
2370
                                setbit(static_cast<unsigned>(fbits - 1 - i), bit);
2,749✔
2371
                        }
2372
                }
2373
                return *this;
33,377✔
2374
        }
2375

2376
public:
2377
        template<typename Real>
2378
        CONSTEXPRESSION cfloat& convert_ieee754(Real rhs) noexcept {
131,528,903✔
2379
                if constexpr (nbits == 32 && es == 8 && sizeof(Real) == 4) {
2380
                        // we CANNOT use the native conversion to float as cfloats have max-exponent values
2381
                        // which IEEE-754 does not have and thus a native conversion would destroy
2382
                        // only if the Real type is a float can we use the direct conversion
2383

2384
                        // when our cfloat is a perfect match to single precision IEEE-754
2385
                        bool s{ false };
65,749✔
2386
                        uint64_t rawExponent{ 0 };
65,749✔
2387
                        uint64_t rawFraction{ 0 };
65,749✔
2388
                        uint64_t bits{ 0 };
65,749✔
2389
                        extractFields(rhs, s, rawExponent, rawFraction, bits);
65,749✔
2390
                        if (rawExponent == ieee754_parameter<Real>::eallset) { // nan and inf need to be remapped
65,749✔
2391
                                if (rawFraction == (ieee754_parameter<Real>::fmask & ieee754_parameter<Real>::snanmask) ||
24✔
2392
                                        rawFraction == (ieee754_parameter<Real>::fmask & (ieee754_parameter<Real>::qnanmask | ieee754_parameter<Real>::snanmask))) {
18✔
2393
                                        // 1.11111111.00000000.......00000001 signalling nan
2394
                                        // 0.11111111.00000000000000000000001 signalling nan
2395
                                        // MSVC
2396
                                        // 1.11111111.10000000.......00000001 signalling nan
2397
                                        // 0.11111111.10000000.......00000001 signalling nan
2398
                                        setnan(NAN_TYPE_SIGNALLING);
6✔
2399
                                        //setsign(s);  a cfloat encodes a signalling nan with sign = 1, and a quiet nan with sign = 0
2400
                                        return *this;
6✔
2401
                                }
2402
                                if (rawFraction == (ieee754_parameter<Real>::fmask & ieee754_parameter<Real>::qnanmask)) {
18✔
2403
                                        // 1.11111111.10000000.......00000000 quiet nan
2404
                                        // 0.11111111.10000000.......00000000 quiet nan
2405
                                        setnan(NAN_TYPE_QUIET);
5✔
2406
                                        //setsign(s);  a cfloat encodes a signalling nan with sign = 1, and a quiet nan with sign = 0
2407
                                        return *this;
5✔
2408
                                }
2409
                                if (rawFraction == 0ull) {
13✔
2410
                                        // 1.11111111.0000000.......000000000 -inf
2411
                                        // 0.11111111.0000000.......000000000 +inf
2412
                                        setinf(s);
13✔
2413
                                        return *this;
13✔
2414
                                }
2415
                        }
2416
                        uint64_t raw{ s ? 1ull : 0ull };
65,725✔
2417
                        raw <<= 31;
65,725✔
2418
                        raw |= (rawExponent << fbits);
65,725✔
2419
                        raw |= rawFraction;
65,725✔
2420
                        setbits(raw);
65,725✔
2421
                        return *this;
65,725✔
2422
                }
2423
                else if constexpr (nbits == 64 && es == 11 && sizeof(Real) == 8) {
2424
                        // when our cfloat is a perfect match to double precision IEEE-754
2425
                        bool s{ false };
12,582✔
2426
                        uint64_t rawExponent{ 0 };
12,582✔
2427
                        uint64_t rawFraction{ 0 };
12,582✔
2428
                        uint64_t bits{ 0 };
12,582✔
2429
                        extractFields(rhs, s, rawExponent, rawFraction, bits);
12,582✔
2430
                        if (rawExponent == ieee754_parameter<Real>::eallset) { // nan and inf need to be remapped
12,582✔
2431
                                if (rawFraction == (ieee754_parameter<Real>::fmask & ieee754_parameter<Real>::snanmask) ||
22✔
2432
                                        rawFraction == (ieee754_parameter<Real>::fmask & (ieee754_parameter<Real>::qnanmask | ieee754_parameter<Real>::snanmask))) {
15✔
2433
                                        // 1.11111111.00000000.......00000001 signalling nan
2434
                                        // 0.11111111.00000000000000000000001 signalling nan
2435
                                        // MSVC
2436
                                        // 1.11111111.10000000.......00000001 signalling nan
2437
                                        // 0.11111111.10000000.......00000001 signalling nan
2438
                                        setnan(NAN_TYPE_SIGNALLING);
7✔
2439
                                        //setsign(s);  a cfloat encodes a signalling nan with sign = 1, and a quiet nan with sign = 0
2440
                                        return *this;
7✔
2441
                                }
2442
                                if (rawFraction == (ieee754_parameter<Real>::fmask & ieee754_parameter<Real>::qnanmask)) {
15✔
2443
                                        // 1.11111111.10000000.......00000000 quiet nan
2444
                                        // 0.11111111.10000000.......00000000 quiet nan
2445
                                        setnan(NAN_TYPE_QUIET);
5✔
2446
                                        //setsign(s);  a cfloat encodes a signalling nan with sign = 1, and a quiet nan with sign = 0
2447
                                        return *this;
5✔
2448
                                }
2449
                                if (rawFraction == 0ull) {
10✔
2450
                                        // 1.11111111.0000000.......000000000 -inf
2451
                                        // 0.11111111.0000000.......000000000 +inf
2452
                                        setinf(s);
10✔
2453
                                        return *this;
10✔
2454
                                }
2455
                        }
2456
                        // normal and subnormal handling
2457
                        uint64_t raw{ s ? 1ull : 0ull };
12,560✔
2458
                        raw <<= 63;
12,560✔
2459
                        raw |= (rawExponent << fbits);
12,560✔
2460
                        raw |= rawFraction;
12,560✔
2461
                        setbits(raw);
12,560✔
2462
                        return *this;
12,560✔
2463
                }
2464
                else {
2465
                        clear();
131,450,572✔
2466
                        // extract raw IEEE-754 bits
2467
                        bool s{ false };
131,450,572✔
2468
                        uint64_t rawExponent{ 0 };
131,450,572✔
2469
                        uint64_t rawFraction{ 0 };
131,450,572✔
2470
                        uint64_t bits{ 0 };
131,450,572✔
2471
                        extractFields(rhs, s, rawExponent, rawFraction, bits);
131,450,572✔
2472
                        // special case handling
2473
                        if (rawExponent == ieee754_parameter<Real>::eallset) { // nan and inf
131,450,572✔
2474
                                if (rawFraction == (ieee754_parameter<Real>::fmask & ieee754_parameter<Real>::snanmask) ||
5,100,744✔
2475
                                        rawFraction == (ieee754_parameter<Real>::fmask & (ieee754_parameter<Real>::qnanmask | ieee754_parameter<Real>::snanmask))) {
2,585,143✔
2476
                                        // 1.11111111.00000000.......00000001 signalling nan
2477
                                        // 0.11111111.00000000000000000000001 signalling nan
2478
                                        // MSVC
2479
                                        // 1.11111111.10000000.......00000001 signalling nan
2480
                                        // 0.11111111.10000000.......00000001 signalling nan
2481
                                        setnan(NAN_TYPE_SIGNALLING);
2,520,903✔
2482
                                        //setsign(s);  a cfloat encodes a signalling nan with sign = 1, and a quiet nan with sign = 0
2483
                                        return *this;
2,520,903✔
2484
                                }
2485
                                if (rawFraction == (ieee754_parameter<Real>::fmask & ieee754_parameter<Real>::qnanmask)) {
2,579,841✔
2486
                                        // 1.11111111.10000000.......00000000 quiet nan
2487
                                        // 0.11111111.10000000.......00000000 quiet nan
2488
                                        setnan(NAN_TYPE_QUIET);
2,551,128✔
2489
                                        //setsign(s);  a cfloat encodes a signalling nan with sign = 1, and a quiet nan with sign = 0
2490
                                        return *this;
2,551,128✔
2491
                                }
2492
                                if (rawFraction == 0ull) {
28,713✔
2493
                                        // 1.11111111.0000000.......000000000 -inf
2494
                                        // 0.11111111.0000000.......000000000 +inf
2495
                                        setinf(s);
28,689✔
2496
                                        return *this;
28,689✔
2497
                                }
2498
                        }
2499
                        if (rhs == 0.0) { // IEEE rule: this is valid for + and - 0.0
126,349,852✔
2500
                                setbit(nbits - 1ull, s);
597,336✔
2501
                                return *this;
597,336✔
2502
                        }
2503
        
2504
                        // normal number consists of fbits fraction bits and one hidden bit
2505
                        // subnormal number has no hidden bit
2506
                        int exponent = static_cast<int>(rawExponent) - ieee754_parameter<Real>::bias;  // unbias the exponent
125,752,516✔
2507

2508
                        // check special case of 
2509
                        //  1- saturating to maxpos/maxneg, or 
2510
                        //  2- projecting to +-inf 
2511
                        // if the value is out of range.
2512
                        // 
2513
                        // One problem here is that at the rounding cusps of maxpos <-> inf <-> nan
2514
                        // you need to go through the rounding logic to know which encoding you end up
2515
                        // with. 
2516
                        // For each specific cfloat configuration, you can work out these rounding cusps
2517
                        // but they need to go through the value transformation to map them back to native
2518
                        // IEEE-754. That is a complex computation to do as a static constexpr as you need
2519
                        // to construct the value, then evaluate it, and store it.
2520
                        // 
2521
                        // The algorithm used here is to check for the obvious out of range values by
2522
                        // comparing their scale to the max scale this cfloat encoding can represent.
2523
                        // For the rounding cusps, we go through the rounding logic, and then clean up
2524
                        // after rounding using the observation that no conversion from a value can ever
2525
                        // yield the NaN encoding.
2526
                        //
2527
                        // The rounding logic will correctly sort between maxpos and inf, and we clean
2528
                        // up any NaN encodings by projecting back to the configuration's saturation rule.
2529
                        //
2530
                        // We could improve on this by creating the database of rounding cusps and
2531
                        // referencing them with a direct value comparison with the input. That would be
2532
                        // the most performant implementation.
2533
                        if (exponent > MAX_EXP) {
125,752,516✔
2534
                                if constexpr (isSaturating) {
2535
                                        if (s) this->maxneg(); else this->maxpos(); // saturate to maxpos or maxneg
634,023✔
2536
                                }
2537
                                else {
2538
                                        setinf(s);
666,849✔
2539
                                }
2540
                                return *this;
1,300,872✔
2541
                        }
2542
                        if constexpr (hasSubnormals) {
2543
                                if (exponent < MIN_EXP_SUBNORMAL - 1) { 
88,953,703✔
2544
                                        // map to +-0 any values that have a scale less than (MIN_EXP_SUBMORNAL - 1)
2545
                                        this->setbit(nbits - 1, s);
143,698✔
2546
                                        return *this;
143,698✔
2547
                                }
2548
                        }
2549
                        else {
2550
                                if (exponent < MIN_EXP_NORMAL - 1) {
35,497,941✔
2551
                                        // map to +-0 any values that have a scale less than (MIN_EXP_MORNAL - 1)
2552
                                        this->setbit(nbits - 1, s);
270,875✔
2553
                                        return *this;
270,875✔
2554
                                }
2555
                        }
2556

2557
                        /////////////////  
2558
                        /// end of special case processing, move on to value sampling and rounding
2559

2560
#if TRACE_CONVERSION
2561
                        std::cout << '\n';
2562
                        std::cout << "value             : " << rhs << '\n';
2563
                        std::cout << "segments          : " << to_binary(rhs) << '\n';
2564
                        std::cout << "sign     bit      : " << (s ? '1' : '0') << '\n';
2565
                        std::cout << "exponent bits     : " << to_binary(rawExponent, ieee754_parameter<Real>::ebits, true) << '\n';
2566
                        std::cout << "fraction bits     : " << to_binary(rawFraction, ieee754_parameter<Real>::fbits, true) << '\n';
2567
                        std::cout << "exponent value    : " << exponent << '\n';
2568
#endif
2569

2570
                        // do the following scenarios have different rounding bits?
2571
                        // input is normal, cfloat is normal           <-- rounding can happen with native ieee-754 bits
2572
                        // input is normal, cfloat is subnormal
2573
                        // input is subnormal, cfloat is normal
2574
                        // input is subnormal, cfloat is subnormal
2575

2576
                        // The first condition is the relationship between the number 
2577
                        // of fraction bits from the source and the number of fraction bits 
2578
                        // in the target cfloat: these are constexpressions and guard the shifts
2579
                        // input fbits >= cfloat fbits                 <-- need to round
2580
                        // input fbits < cfloat fbits                  <-- no need to round
2581

2582
                        // quick check if we are truncating to 0 for all subnormal values
2583
                        if constexpr (!hasSubnormals) {
2584
                                if (exponent < MIN_EXP_NORMAL) {
35,227,066✔
2585
                                        setsign(s); // rest of the bits, exponent and fraction, are already set correctly
123,584✔
2586
                                        return *this;
123,584✔
2587
                                }
2588
                        }
2589
                        if constexpr (fbits < ieee754_parameter<Real>::fbits) {
2590
                                // this is the common case for cfloats that are smaller in size compared to single and double precision IEEE-754
2591
                                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
123,654,039✔
2592
                                uint32_t biasedExponent{ 0 };
123,654,039✔
2593
                                int adjustment{ 0 }; // right shift adjustment for subnormal representation
123,654,039✔
2594
                                uint64_t mask;
2595
                                if (rawExponent != 0) {
123,654,039✔
2596
                                        // the source real is a normal number, 
2597
//                                        if (exponent >= (MIN_EXP_SUBNORMAL - 1) && exponent < MIN_EXP_NORMAL) {
2598
                                        if (exponent < MIN_EXP_NORMAL) {
123,654,020✔
2599
//                                                exponent = (exponent < MIN_EXP_SUBNORMAL ? MIN_EXP_SUBNORMAL : exponent); // clip to the smallest subnormal exponent, otherwise the adjustment is off
2600
                                                // the value is a subnormal number in this representation: biasedExponent = 0
2601
                                                // add the hidden bit to the fraction bits so the denormalization has the correct MSB
2602
                                                rawFraction |= ieee754_parameter<Real>::hmask;
41,271,332✔
2603

2604
                                                // fraction processing: we have 1 hidden + 23 explicit fraction bits 
2605
                                                // f = 1.ffff 2^exponent * 2^fbits * 2^-(2-2^(es-1)) = 1.ff...ff >> (23 - (-exponent + fbits - (2 -2^(es-1))))
2606
                                                // -exponent because we are right shifting and exponent in this range is negative
2607
                                                adjustment = -(exponent + subnormal_reciprocal_shift[es]);
41,271,332✔
2608
                                                // this is the right shift adjustment required for subnormal representation due 
2609
                                                // to the scale of the input number, i.e. the exponent of 2^-adjustment
2610
                                        }
2611
                                        else {
2612
                                                // the value is a normal number in this representation: common case
2613
                                                biasedExponent = static_cast<uint32_t>(exponent + EXP_BIAS); // project the exponent into the target 
82,382,688✔
2614
                                                // fraction processing
2615
                                                // float structure is: seee'eeee'efff'ffff'ffff'ffff'ffff'ffff, s = sign, e - exponent bit, f = fraction bit
2616
                                                // target structure is for example cfloat<8,2>: seef'ffff
2617
                                                // since both are normals, we can shift the incoming fraction to the target structure bits, and round
2618
                                                // MSB of source = 23 - 1, MSB of target = fbits - 1: shift = MSB of src - MSB of tgt => 23 - fbits
2619
                                                adjustment = 0;
82,382,688✔
2620
                                        }
2621
                                        if constexpr (rightShift > 0) {
2622
                                                // if true we need to round
2623
                                                // round-to-even logic
2624
                                                //  ... lsb | guard  round sticky   round
2625
                                                //       x     0       x     x       down
2626
                                                //       0     1       0     0       down  round to even
2627
                                                //       1     1       0     0        up   round to even
2628
                                                //       x     1       0     1        up
2629
                                                //       x     1       1     0        up
2630
                                                //       x     1       1     1        up
2631
                                                // collect lsb, guard, round, and sticky bits
2632

2633

2634
#if TRACE_CONVERSION
2635
                                                std::cout << "fraction bits     : " << to_binary(rawFraction, ieee754_parameter<Real>::nbits, true) << '\n';
2636
                                                std::cout << "lsb mask bits     : " << to_binary(mask, ieee754_parameter<Real>::nbits, true) << '\n';
2637
#endif
2638
                                                mask = (1ull << (rightShift + adjustment)); // bit mask for the lsb bit
123,654,020✔
2639
                                                bool lsb = (mask & rawFraction);
123,654,020✔
2640
                                                mask >>= 1;
123,654,020✔
2641
                                                bool guard = (mask & rawFraction);
123,654,020✔
2642
                                                mask >>= 1;
123,654,020✔
2643
                                                bool round = (mask & rawFraction);
123,654,020✔
2644
                                                if ((rightShift + adjustment) > 1) {
123,654,020✔
2645
                                                        mask = (0xFFFF'FFFF'FFFF'FFFFull << (rightShift + adjustment - 2));
123,654,020✔
2646
                                                        mask = ~mask;
123,654,020✔
2647
                                                }
2648
                                                else {
2649
                                                        mask = 0;
×
2650
                                                }
2651
#if TRACE_CONVERSION
2652
                                                std::cout << "right shift       : " << rightShift << '\n';
2653
                                                std::cout << "adjustment        : " << adjustment << '\n';
2654
                                                std::cout << "shift to LSB      : " << (rightShift + adjustment) << '\n';
2655
                                                std::cout << "fraction bits     : " << to_binary(rawFraction, ieee754_parameter<Real>::nbits, true) << '\n';
2656
                                                std::cout << "sticky mask bits  : " << to_binary(mask, ieee754_parameter<Real>::nbits, true) << '\n';
2657
#endif
2658
                                                bool sticky = (mask & rawFraction);
123,654,020✔
2659
                                                rawFraction >>= (static_cast<int64_t>(rightShift) + static_cast<int64_t>(adjustment));
123,654,020✔
2660

2661
                                                // execute rounding operation
2662
                                                if (guard) {
123,654,020✔
2663
                                                        if (lsb && (!round && !sticky)) ++rawFraction; // round to even
24,517,876✔
2664
                                                        if (round || sticky) ++rawFraction;
24,517,876✔
2665
                                                        if (rawFraction == (1ull << fbits)) { // overflow
24,517,876✔
2666
                                                                if (biasedExponent == ALL_ONES_ES) { // overflow to INF == .111..01
443,451✔
2667
                                                                        rawFraction = INF_ENCODING;
398✔
2668
                                                                }
2669
                                                                else {
2670
                                                                        ++biasedExponent;
443,053✔
2671
                                                                        rawFraction = 0;
443,053✔
2672
                                                                }
2673
                                                        }
2674
                                                }
2675
#if TRACE_CONVERSION
2676
                                                std::cout << "lsb               : " << (lsb ? "1\n" : "0\n");
2677
                                                std::cout << "guard             : " << (guard ? "1\n" : "0\n");
2678
                                                std::cout << "round             : " << (round ? "1\n" : "0\n");
2679
                                                std::cout << "sticky            : " << (sticky ? "1\n" : "0\n");
2680
                                                std::cout << "rounding decision : " << (lsb && (!round && !sticky) ? "round to even\n" : "-\n");
2681
                                                std::cout << "rounding direction: " << (round || sticky ? "round up\n" : "round down\n");
2682
#endif
2683
                                        }
2684
                                        else { // all bits of the float go into this representation and need to be shifted up, no rounding necessary
2685
                                                int shiftLeft = fbits - ieee754_parameter<Real>::fbits;
2686
                                                rawFraction <<= shiftLeft;
2687
                                        }
2688
#if TRACE_CONVERSION
2689
                                        std::cout << "biased exponent   : " << biasedExponent << " : 0x" << std::hex << biasedExponent << std::dec << '\n';
2690
                                        std::cout << "right shift       : " << rightShift << '\n';
2691
                                        std::cout << "adjustment shift  : " << adjustment << '\n';
2692
                                        std::cout << "sticky bit mask   : " << to_binary(mask, 32, true) << '\n';
2693
                                        std::cout << "fraction bits     : " << to_binary(rawFraction, 32, true) << '\n';
2694
#endif
2695
                                        // construct the target cfloat
2696
                                        bits = (s ? 1ull : 0ull);
123,654,020✔
2697
                                        bits <<= es;
123,654,020✔
2698
                                        bits |= biasedExponent;
123,654,020✔
2699
                                        bits <<= fbits;
123,654,020✔
2700
                                        bits |= rawFraction;
123,654,020✔
2701
#if TRACE_CONVERSION
2702
                                        std::cout << "sign bit          : " << (s ? '1' : '0') << '\n';
2703
                                        std::cout << "biased exponent   : " << biasedExponent << " : 0x" << std::hex << biasedExponent << std::dec << '\n';
2704
                                        std::cout << "fraction bits     : " << to_binary(rawFraction, 32, true) << '\n';
2705
                                        std::cout << "cfloat bits       : " << to_binary(bits, nbits, true) << '\n';
2706
#endif
2707
                                        setbits(bits);
123,654,020✔
2708
                                }
2709
                                else {
2710
                                        // the source real is a subnormal number                                
2711
                                        mask = 0x00FF'FFFFu >> (fbits + exponent + subnormal_reciprocal_shift[es] + 1); // mask for sticky bit 
19✔
2712

2713
                                        // fraction processing: we have fbits+1 bits = 1 hidden + fbits explicit fraction bits 
2714
                                        // f = 1.ffff  2^exponent * 2^fbits * 2^-(2-2^(es-1)) = 1.ff...ff >> (23 - (-exponent + fbits - (2 -2^(es-1))))
2715
                                        // -exponent because we are right shifting and exponent in this range is negative
2716
                                        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
19✔
2717
#if TRACE_CONVERSION                                        
2718
                                        std::cout << "source is subnormal: TBD\n";
2719
                                        std::cout << "shift to LSB    " << (rightShift + adjustment) << '\n';
2720
                                        std::cout << "adjustment      " << adjustment << '\n';
2721
                                        std::cout << "exponent        " << exponent << '\n';
2722
                                        std::cout << "subnormal shift " << subnormal_reciprocal_shift[es] << '\n';
2723
#endif
2724
                                        if (exponent >= (MIN_EXP_SUBNORMAL - 1) && exponent < MIN_EXP_NORMAL) {
2725
                                                // the value is a subnormal number in this representation
2726
                                        }
2727
                                        else {
2728
                                                // the value is a normal number in this representation
2729
                                        }
2730
                                }
2731
                        }
2732
                        else {
2733
                                // no need to round, but we need to shift left to deliver the bits
2734
                                // cfloat<40,  8> = float
2735
                                // cfloat<48,  9> = float
2736
                                // cfloat<56, 10> = float
2737
                                // cfloat<64, 11> = float
2738
                                // cfloat<64, 10> = double 
2739
                                // can we go from an input subnormal to a cfloat normal? 
2740
                                // yes, for example a cfloat<64,11> assigned to a subnormal float
2741

2742
                                // map exponent into target cfloat encoding
2743
                                uint64_t biasedExponent = static_cast<uint64_t>(static_cast<int64_t>(exponent) + EXP_BIAS);
259,448✔
2744
                                constexpr int upshift = fbits - ieee754_parameter<Real>::fbits;
259,448✔
2745
                                // output processing
2746
                                if constexpr (nbits < 65) {
2747
                                        // we can compose the bits in a native 64-bit unsigned integer
2748
                                        // common case: normal to normal
2749
                                        // reference example: nbits = 40, es = 8, fbits = 31: rhs = float fbits = 23; shift left by (31 - 23) = 8
2750

2751
                                        if (rawExponent != 0) {
199,201✔
2752
                                                // rhs is a normal encoding
2753
                                                uint64_t raw{ s ? 1ull : 0ull };
198,417✔
2754
                                                raw <<= es;
198,417✔
2755
                                                raw |= biasedExponent;
198,417✔
2756
                                                raw <<= fbits;
198,417✔
2757
                                                rawFraction <<= upshift;
198,417✔
2758
                                                raw |= rawFraction;
198,417✔
2759
                                                setbits(raw);
198,417✔
2760
                                        }
2761
                                        else {
2762
                                                // rhs is a subnormal
2763
        //                                        std::cerr << "rhs is a subnormal : " << to_binary(rhs) << " : " << rhs << '\n';
2764
                                                // we need to calculate the effective scale to see 
2765
                                                // if this value becomes a normal, or maps to a subnormal encoding
2766
                                                // in this target format
2767
                                        }
2768
                                }
2769
                                else {
2770
                                        // we need to write and shift bits into place
2771
                                        // use cases are cfloats like cfloat<80, 11, bt>
2772
                                        // even though the bits that come in are single or double precision
2773
                                        // we need to write the fields and then shifting them in place
2774
                                        // 
2775
                                        // common case: normal to normal
2776
                                        if constexpr (bitsInBlock < 64) {
2777
                                                if (rawExponent != 0) {
60,247✔
2778
                                                        // reference example: nbits = 128, es = 15, fbits = 112: rhs = float: shift left by (112 - 23) = 89
2779
                                                        setbits(biasedExponent);
60,139✔
2780
                                                        shiftLeft(fbits);
60,139✔
2781
                                                        bt fractionBlock[nrBlocks]{ 0 };
60,139✔
2782
                                                        // copy fraction bits
2783
                                                        unsigned blocksRequired = (8 * sizeof(rawFraction) + 1) / bitsInBlock;
60,139✔
2784
                                                        unsigned maxBlockNr = (blocksRequired < nrBlocks ? blocksRequired : nrBlocks);
60,139✔
2785
                                                        uint64_t mask = static_cast<uint64_t>(ALL_ONES); // set up the block mask
60,139✔
2786
                                                        unsigned shift = 0;
60,139✔
2787
                                                        for (unsigned i = 0; i < maxBlockNr; ++i) {
340,163✔
2788
                                                                fractionBlock[i] = bt((mask & rawFraction) >> shift);
280,024✔
2789
                                                                mask <<= bitsInBlock;
280,024✔
2790
                                                                shift += bitsInBlock;
280,024✔
2791
                                                        }
2792
                                                        // shift fraction bits
2793
                                                        int bitsToShift = upshift;
60,139✔
2794
                                                        if (bitsToShift >= static_cast<int>(bitsInBlock)) {
60,139✔
2795
                                                                int blockShift = static_cast<int>(bitsToShift / bitsInBlock);
50,118✔
2796
                                                                for (int i = MSU; i >= blockShift; --i) {
270,566✔
2797
                                                                        fractionBlock[i] = fractionBlock[i - blockShift];
220,448✔
2798
                                                                }
2799
                                                                for (int i = blockShift - 1; i >= 0; --i) {
160,384✔
2800
                                                                        fractionBlock[i] = bt(0);
110,266✔
2801
                                                                }
2802
                                                                // adjust the shift
2803
                                                                bitsToShift -= blockShift * bitsInBlock;
50,118✔
2804
                                                        }
2805
                                                        if (bitsToShift > 0) {
60,139✔
2806
                                                                // construct the mask for the upper bits in the block that need to move to the higher word
2807
                                                                bt bitsToMoveMask = bt(ALL_ONES << (bitsInBlock - bitsToShift));
40,109✔
2808
                                                                for (unsigned i = MSU; i > 0; --i) {
210,467✔
2809
                                                                        fractionBlock[i] <<= bitsToShift;
170,358✔
2810
                                                                        // mix in the bits from the right
2811
                                                                        bt fracbits = static_cast<bt>(bitsToMoveMask & fractionBlock[i - 1]); // operator & yields an int
170,358✔
2812
                                                                        fractionBlock[i] |= (fracbits >> (bitsInBlock - bitsToShift));
170,358✔
2813
                                                                }
2814
                                                                fractionBlock[0] <<= bitsToShift;
40,109✔
2815
                                                        }
2816
                                                        // OR the bits in
2817
                                                        for (unsigned i = 0; i <= MSU; ++i) {
420,916✔
2818
                                                                _block[i] |= fractionBlock[i];
360,777✔
2819
                                                        }
2820
                                                        // enforce precondition for fast comparison by properly nulling bits that are outside of nbits
2821
                                                        _block[MSU] &= MSU_MASK;
60,139✔
2822
                                                        // finally, set the sign bit
2823
                                                        setsign(s);
60,139✔
2824
                                                }
2825
                                                else {
2826
                                                        // rhs is a subnormal
2827
                //                                        std::cerr << "rhs is a subnormal : " << to_binary(rhs) << " : " << rhs << '\n';
2828
                                                }
2829
                                        }
2830
                                        else {
2831
                                                // BlockType is incorrect
2832
                                        }
2833
                                }
2834
                        }
2835
                        // post-processing results to implement saturation and projection after rounding logic
2836
                        if constexpr (isSaturating) {
2837
                                if (isinf(INF_TYPE_POSITIVE) || isnan(NAN_TYPE_QUIET)) {
17,112,994✔
2838
                                        maxpos();
587✔
2839
                                }
2840
                                else if (isinf(INF_TYPE_NEGATIVE) || isnan(NAN_TYPE_SIGNALLING)) {
17,112,407✔
2841
                                        maxneg();
587✔
2842
                                }
2843
                        }
2844
                        else {
2845
                                if (isnan(NAN_TYPE_QUIET)) {
106,800,493✔
2846
                                        setinf(false);
301,221✔
2847
                                }
2848
                                else if (isnan(NAN_TYPE_SIGNALLING)) {
106,499,272✔
2849
                                        setinf(true);
300,173✔
2850
                                }
2851
                        }
2852
                        return *this;  // TODO: unreachable in some configurations        
123,913,487✔
2853
                }
2854
        }
2855

2856
        // post-processing results to implement saturation and projection after rounding logic
2857
        // arithmetic bit operations can't produce NaN encodings, so we need to re-interpret
2858
        // these encodings and 'project' them to the proper values.
2859
        void constexpr post_process() noexcept {
2860
                if constexpr (isSaturating) {
2861
                        if (isinf(INF_TYPE_POSITIVE) || isnan(NAN_TYPE_QUIET)) {
2862
                                maxpos();
2863
                        }
2864
                        else if (isinf(INF_TYPE_NEGATIVE) || isnan(NAN_TYPE_SIGNALLING)) {
2865
                                maxneg();
2866
                        }
2867
                }
2868
                else {
2869
                        if (isnan(NAN_TYPE_QUIET)) {
2870
                                setinf(false);
2871
                        }
2872
                        else if (isnan(NAN_TYPE_SIGNALLING)) {
2873
                                setinf(true);
2874
                        }
2875
                }
2876
        }
2877

2878
protected:
2879

2880
        /// <summary>
2881
        /// round a set of source bits to the present representation.
2882
        /// srcbits is the number of bits of significant in the source representation
2883
        /// </summary>
2884
        /// <typeparam name="StorageType"></typeparam>
2885
        /// <param name="raw"></param>
2886
        /// <returns></returns>
2887
        template<unsigned srcbits, typename StorageType>
2888
        constexpr uint64_t round(StorageType raw, int& exponent) noexcept {
33,514✔
2889
                if constexpr (fhbits < srcbits) {
2890
                        // round to even: lsb guard round sticky
2891
                    // collect guard, round, and sticky bits
2892
                    // this same logic will work for the case where
2893
                    // we only have a guard bit and no round and sticky bits
2894
                    // because the mask logic will make round and sticky both 0
2895
                        constexpr uint32_t shift = srcbits - fhbits - 1ull;
30,530✔
2896
                        StorageType mask = (StorageType(1ull) << shift);
30,530✔
2897
                        bool guard = (mask & raw);
30,530✔
2898
                        mask >>= 1;
30,530✔
2899
                        bool round = (mask & raw);
30,530✔
2900
                        if constexpr (shift > 1u) { // protect against a negative shift
2901
                                StorageType allones(StorageType(~0));
30,530✔
2902
                                mask = StorageType(allones << (shift - 2));
30,530✔
2903
                                mask = ~mask;
30,530✔
2904
                        }
2905
                        else {
2906
                                mask = 0;
2907
                        }
2908
                        bool sticky = (mask & raw);
30,530✔
2909

2910
                        raw >>= (shift + 1);  // shift out the bits we are rounding away
30,530✔
2911
                        bool lsb = (raw & 0x1u);
30,530✔
2912
                        //  ... lsb | guard  round sticky   round
2913
                        //       x     0       x     x       down
2914
                        //       0     1       0     0       down  round to even
2915
                        //       1     1       0     0        up   round to even
2916
                        //       x     1       0     1        up
2917
                        //       x     1       1     0        up
2918
                        //       x     1       1     1        up
2919
                        if (guard) {
30,530✔
2920
                                if (lsb && (!round && !sticky)) ++raw; // round to even
3,168✔
2921
                                if (round || sticky) ++raw;
3,168✔
2922
                                if (raw == (1ull << fbits)) { // overflow
3,168✔
2923
                                        ++exponent;
3,168✔
2924
                                        raw >>= 1u;
3,168✔
2925
                                }
2926
                        }
2927
                }
2928
                else {
2929
                        // Target has more precision than source - need to left-shift to align
2930
                        // For large types where fhbits > 64, the fraction bits cannot fit in
2931
                        // a 64-bit raw after shifting. In this case, skip the shift and let
2932
                        // convert_signed/unsigned_integer place bits using setbit().
2933
                        // The caller positions fraction bits at the top of srcbits, and we
2934
                        // need to ensure they don't overflow 64 bits after our shift.
2935
                        constexpr unsigned shift = fhbits - srcbits;
2,984✔
2936
                        if constexpr (fhbits <= 64 && shift < 64) {
2937
                                raw <<= shift;
2,583✔
2938
                        }
2939
                        // else: raw stays as-is; caller will extract bits and place them
2940
                }
2941
                uint64_t significant = raw;
33,514✔
2942
                return significant;
33,514✔
2943
        }
2944

2945
        template<typename ArgumentBlockType>
2946
        constexpr void copyBits(ArgumentBlockType v) {
2947
                unsigned blocksRequired = (8 * sizeof(v) + 1 ) / bitsInBlock;
2948
                unsigned maxBlockNr = (blocksRequired < nrBlocks ? blocksRequired : nrBlocks);
2949
                bt b{ 0ul }; b = bt(~b);
2950
                ArgumentBlockType mask = ArgumentBlockType(b);
2951
                unsigned shift = 0;
2952
                for (unsigned i = 0; i < maxBlockNr; ++i) {
2953
                        _block[i] = bt((mask & v) >> shift);
2954
                        mask <<= bitsInBlock;
2955
                        shift += bitsInBlock;
2956
                }
2957
        }
2958
        void shiftLeft(int leftShift) {
2959
                if (leftShift == 0) return;
2960
                if (leftShift < 0) return shiftRight(-leftShift);
2961
                if (leftShift > long(nbits)) leftShift = nbits; // clip to max
2962
                if (leftShift >= long(bitsInBlock)) {
2963
                        int blockShift = leftShift / static_cast<int>(bitsInBlock);
2964
                        for (signed i = signed(MSU); i >= blockShift; --i) {
2965
                                _block[i] = _block[i - blockShift];
2966
                        }
2967
                        for (signed i = blockShift - 1; i >= 0; --i) {
2968
                                _block[i] = bt(0);
2969
                        }
2970
                        // adjust the shift
2971
                        leftShift -= (long)(blockShift * bitsInBlock);
2972
                        if (leftShift == 0) return;
2973
                }
2974
                // construct the mask for the upper bits in the block that need to move to the higher word
2975
//                bt mask = static_cast<bt>(0xFFFFFFFFFFFFFFFFull << (bitsInBlock - leftShift));
2976
                bt mask = ALL_ONES;
2977
                mask <<= (bitsInBlock - leftShift);
2978
                for (unsigned i = MSU; i > 0; --i) {
2979
                        _block[i] <<= leftShift;
2980
                        // mix in the bits from the right
2981
                        bt bits = static_cast<bt>(mask & _block[i - 1]);
2982
                        _block[i] |= (bits >> (bitsInBlock - leftShift));
2983
                }
2984
                _block[0] <<= leftShift;
2985
        }
2986
        void shiftRight(int rightShift) {
2987
                if (rightShift == 0) return;
2988
                if (rightShift < 0) return shiftLeft(-rightShift);
2989
                if (rightShift >= long(nbits)) {
2990
                        setzero();
2991
                        return;
2992
                }
2993
                bool signext = sign();
2994
                unsigned blockShift = 0;
2995
                if (rightShift >= long(bitsInBlock)) {
2996
                        blockShift = rightShift / bitsInBlock;
2997
                        if (MSU >= blockShift) {
2998
                                // shift by blocks
2999
                                for (unsigned i = 0; i <= MSU - blockShift; ++i) {
3000
                                        _block[i] = _block[i + blockShift];
3001
                                }
3002
                        }
3003
                        // adjust the shift
3004
                        rightShift -= (long)(blockShift * bitsInBlock);
3005
                        if (rightShift == 0) {
3006
                                // fix up the leading zeros if we have a negative number
3007
                                if (signext) {
3008
                                        // rightShift is guaranteed to be less than nbits
3009
                                        rightShift += (long)(blockShift * bitsInBlock);
3010
                                        for (unsigned i = nbits - rightShift; i < nbits; ++i) {
3011
                                                this->setbit(i);
3012
                                        }
3013
                                }
3014
                                else {
3015
                                        // clean up the blocks we have shifted clean
3016
                                        rightShift += (long)(blockShift * bitsInBlock);
3017
                                        for (unsigned i = nbits - rightShift; i < nbits; ++i) {
3018
                                                this->setbit(i, false);
3019
                                        }
3020
                                }
3021
                                return;  // shift was aligned to block boundary, no per-bit shift needed
3022
                        }
3023
                }
3024

3025
                bt mask = ALL_ONES;
3026
                mask >>= (bitsInBlock - rightShift); // this is a mask for the lower bits in the block that need to move to the lower word
3027
                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
3028
                        _block[i] >>= rightShift;
3029
                        // mix in the bits from the left
3030
                        bt bits = static_cast<bt>(mask & _block[i + 1]); // & operator returns an int
3031
                        _block[i] |= (bits << (bitsInBlock - rightShift));
3032
                }
3033
                _block[MSU] >>= rightShift;
3034

3035
                // fix up the leading zeros if we have a negative number
3036
                if (signext) {
3037
                        // bitsToShift is guaranteed to be less than nbits
3038
                        rightShift += (long)(blockShift * bitsInBlock);
3039
                        for (unsigned i = nbits - rightShift; i < nbits; ++i) {
3040
                                this->setbit(i);
3041
                        }
3042
                }
3043
                else {
3044
                        // clean up the blocks we have shifted clean
3045
                        rightShift += (long)(blockShift * bitsInBlock);
3046
                        for (unsigned i = nbits - rightShift; i < nbits; ++i) {
3047
                                this->setbit(i, false);
3048
                        }
3049
                }
3050

3051
                // enforce precondition for fast comparison by properly nulling bits that are outside of nbits
3052
                _block[MSU] &= MSU_MASK;
3053
        }
3054

3055
        // calculate the integer power 2 ^ b using exponentiation by squaring
3056
        double ipow(int exponent) const {
525,230✔
3057
                bool negative = (exponent < 0);
525,230✔
3058
                exponent = negative ? -exponent : exponent;
525,230✔
3059
                double result(1.0);
525,230✔
3060
                double base = 2.0;
525,230✔
3061
                for (;;) {
3062
                        if (exponent % 2) result *= base;
3,847,879✔
3063
                        exponent >>= 1;
3,847,879✔
3064
                        if (exponent == 0) break;
3,847,879✔
3065
                        base *= base;
3,322,649✔
3066
                }
3067
                return (negative ? (1.0 / result) : result);
525,230✔
3068
        }
3069

3070
        template<BlockTripleOperator btop, typename TargetBlockType = bt>
3071
        constexpr void blockcopy(blocktriple<fbits, btop, TargetBlockType>& tgt) const {
1,056,146✔
3072
                // brute force copy of blocks
3073
                if constexpr (1 == fBlocks) {
3074
                        tgt.setblock(0, static_cast<TargetBlockType>(_block[0] & FSU_MASK));
1,050,746✔
3075
                }
3076
                else if constexpr (2 == fBlocks) {
3077
                        tgt.setblock(0, static_cast<TargetBlockType>(_block[0]));
3,044✔
3078
                        tgt.setblock(1, static_cast<TargetBlockType>(_block[1] & FSU_MASK));
3,044✔
3079
                }
3080
                else if constexpr (3 == fBlocks) {
3081
                        tgt.setblock(0, static_cast<TargetBlockType>(_block[0]));
202✔
3082
                        tgt.setblock(1, static_cast<TargetBlockType>(_block[1]));
202✔
3083
                        tgt.setblock(2, static_cast<TargetBlockType>(_block[2] & FSU_MASK));
202✔
3084
                }
3085
                else if constexpr (4 == fBlocks) {
3086
                        tgt.setblock(0, static_cast<TargetBlockType>(_block[0]));
1,438✔
3087
                        tgt.setblock(1, static_cast<TargetBlockType>(_block[1]));
1,438✔
3088
                        tgt.setblock(2, static_cast<TargetBlockType>(_block[2]));
1,438✔
3089
                        tgt.setblock(3, static_cast<TargetBlockType>(_block[3] & FSU_MASK));
1,438✔
3090
                }
3091
                else if constexpr (5 == fBlocks) {
3092
                        tgt.setblock(0, static_cast<TargetBlockType>(_block[0]));
×
3093
                        tgt.setblock(1, static_cast<TargetBlockType>(_block[1]));
×
3094
                        tgt.setblock(2, static_cast<TargetBlockType>(_block[2]));
×
3095
                        tgt.setblock(3, static_cast<TargetBlockType>(_block[3]));
×
3096
                        tgt.setblock(4, static_cast<TargetBlockType>(_block[4] & FSU_MASK));
×
3097
                }
3098
                else if constexpr (6 == fBlocks) {
3099
                        tgt.setblock(0, static_cast<TargetBlockType>(_block[0]));
3100
                        tgt.setblock(1, static_cast<TargetBlockType>(_block[1]));
3101
                        tgt.setblock(2, static_cast<TargetBlockType>(_block[2]));
3102
                        tgt.setblock(3, static_cast<TargetBlockType>(_block[3]));
3103
                        tgt.setblock(4, static_cast<TargetBlockType>(_block[4]));
3104
                        tgt.setblock(5, static_cast<TargetBlockType>(_block[5] & FSU_MASK));
3105
                }
3106
                else if constexpr (7 == fBlocks) {
3107
                        tgt.setblock(0, static_cast<TargetBlockType>(_block[0]));
8✔
3108
                        tgt.setblock(1, static_cast<TargetBlockType>(_block[1]));
8✔
3109
                        tgt.setblock(2, static_cast<TargetBlockType>(_block[2]));
8✔
3110
                        tgt.setblock(3, static_cast<TargetBlockType>(_block[3]));
8✔
3111
                        tgt.setblock(4, static_cast<TargetBlockType>(_block[4]));
8✔
3112
                        tgt.setblock(5, static_cast<TargetBlockType>(_block[5]));
8✔
3113
                        tgt.setblock(6, static_cast<TargetBlockType>(_block[6] & FSU_MASK));
8✔
3114
                }
3115
                else if constexpr (8 == fBlocks) {
3116
                        tgt.setblock(0, static_cast<TargetBlockType>(_block[0]));
356✔
3117
                        tgt.setblock(1, static_cast<TargetBlockType>(_block[1]));
356✔
3118
                        tgt.setblock(2, static_cast<TargetBlockType>(_block[2]));
356✔
3119
                        tgt.setblock(3, static_cast<TargetBlockType>(_block[3]));
356✔
3120
                        tgt.setblock(4, static_cast<TargetBlockType>(_block[4]));
356✔
3121
                        tgt.setblock(5, static_cast<TargetBlockType>(_block[5]));
356✔
3122
                        tgt.setblock(6, static_cast<TargetBlockType>(_block[6]));
356✔
3123
                        tgt.setblock(7, static_cast<TargetBlockType>(_block[7] & FSU_MASK));
356✔
3124
                }
3125
                else {
3126
                        for (unsigned i = 0; i < FSU; ++i) {
3,528✔
3127
                                tgt.setblock(i, static_cast<TargetBlockType>(_block[i]));
3,176✔
3128
                        }
3129
                        tgt.setblock(FSU, static_cast<TargetBlockType>(_block[FSU] & FSU_MASK));
352✔
3130
                }
3131
        }
1,056,146✔
3132

3133
private:
3134
        bt _block[nrBlocks];
3135

3136
        //////////////////////////////////////////////////////////////////////////////
3137
        // friend functions
3138

3139
        // template parameters need names different from class template parameters (for gcc and clang)
3140
        template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3141
        friend std::ostream& operator<< (std::ostream& ostr, const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& r);
3142
        template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3143
        friend std::istream& operator>> (std::istream& istr, cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& r);
3144

3145
        template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3146
        friend bool operator==(const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& lhs, const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& rhs);
3147
        template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3148
        friend bool operator!=(const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& lhs, const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& rhs);
3149
        template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3150
        friend bool operator< (const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& lhs, const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& rhs);
3151
        template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3152
        friend bool operator> (const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& lhs, const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& rhs);
3153
        template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3154
        friend bool operator<=(const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& lhs, const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& rhs);
3155
        template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3156
        friend bool operator>=(const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& lhs, const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& rhs);
3157
};
3158

3159
///////////////////////////// IOSTREAM operators ///////////////////////////////////////////////
3160

3161
// convert cfloat to decimal fixpnt string, i.e. "-1234.5678"
3162
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3163
std::string to_decimal_fixpnt_string(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& value, long long precision) {
86✔
3164
        constexpr unsigned fbits = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>::fbits;
86✔
3165
        constexpr unsigned bias = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>::EXP_BIAS;
86✔
3166
        std::stringstream str;
86✔
3167
        if (value.iszero()) {
86✔
3168
                str << '0';
1✔
3169
                return str.str();
1✔
3170
        }
3171
        if (value.sign()) str << '-';
85✔
3172

3173
        // construct the discretization levels of the fraction part
3174
        support::decimal range, discretizationLevels, step;
85✔
3175
        // create the decimal range we are discretizing
3176
        range.setdigit(1);
85✔
3177
        range.shiftLeft(fbits); // the decimal range of the fraction
85✔
3178
        discretizationLevels.powerOf2(fbits); // calculate the discretization levels of this range
85✔
3179
        step = div(range, discretizationLevels);
85✔
3180
        // now construct the value of this range by adding the fraction samples
3181
        support::decimal partial, multiplier;
85✔
3182
        partial.setzero();  // if you just want the fraction
85✔
3183
        multiplier.setdigit(1);
85✔
3184
        // convert the fraction part
3185
        for (unsigned i = 0; i < fbits; ++i) {
1,130✔
3186
                if (value.at(i)) {
1,045✔
3187
                        support::add(partial, multiplier);
50✔
3188
                }
3189
                support::add(multiplier, multiplier);
1,045✔
3190
        }
3191
        if (value.isdenormal()) {
85✔
3192
                support::mul(partial, step);
10✔
3193
                support::decimal scale;
10✔
3194
                scale.powerOf2(bias - 1ull);
10✔
3195
                partial = support::div(partial, scale);
10✔
3196
        } 
10✔
3197
        else {
3198
                support::add(partial, multiplier); // add the hidden bit
75✔
3199
                support::mul(partial, step);
75✔
3200
                support::decimal scale;
75✔
3201
                int exponent = value.scale();
75✔
3202
                if (exponent < 0) {
75✔
3203
                        scale.powerOf2(static_cast<unsigned>(-exponent));
28✔
3204
                        partial = support::div(partial, scale);
28✔
3205
                }
3206
                else {
3207
                        scale.powerOf2(static_cast<unsigned>(exponent));
47✔
3208
                        support::mul(partial, scale);
47✔
3209
                }
3210
        }
75✔
3211

3212
        // the radix is at fbits
3213
        // The partial represents the parts in the range, so we can deduce
3214
        // the number of leading zeros by comparing to the length of range
3215
        int nrLeadingZeros = static_cast<int>(range.size()) - static_cast<int>(partial.size()) - 1;
85✔
3216
        if (nrLeadingZeros >= 0) str << "0.";
85✔
3217
        for (int i = 0; i < nrLeadingZeros; ++i) str << '0';
189✔
3218
        int digitsWritten = (nrLeadingZeros > 0) ? nrLeadingZeros : 0;
85✔
3219
        int position = static_cast<int>(partial.size()) - 1;
85✔
3220
        for (support::decimal::const_reverse_iterator rit = partial.rbegin(); rit != partial.rend(); ++rit) {
1,131✔
3221
                str << (int)*rit;
1,046✔
3222
                ++digitsWritten;
1,046✔
3223
                if (position == fbits) str << '.';
1,046✔
3224
                --position;
1,046✔
3225
        }
3226
        if (digitsWritten < precision) { // deal with trailing 0s
85✔
3227
                for (unsigned i = static_cast<unsigned>(digitsWritten); i < fbits; ++i) {
×
3228
                        str << '0';
×
3229
                }
3230
        }
3231

3232
        return str.str();
85✔
3233
}
86✔
3234

3235
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3236
std::string to_string(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& value, long long precision) {
3237
        constexpr unsigned fbits = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>::fbits;
3238
        std::stringstream str;
3239
        if (value.iszero()) {
3240
                str << '0';
3241
                return str.str();
3242
        }
3243
        if (value.sign()) str << '-';
3244

3245
        // denormalize the number to gain access to the most sigificant digits
3246
        // 1.ffff^e
3247
        // scale is e
3248
        // lsbScale is e - fbits
3249
        // shift to get lsb to position 2^0 = (e - fbits)
3250
        std::int64_t scale = value.scale();
3251
//        std::int64_t shift = scale + fbits; // we want the lsb at 2^0
3252
        std::int64_t lsbScale = scale - fbits;  // scale of the lsb
3253
        support::decimal partial, multiplier;
3254
        partial.setzero();
3255

3256
        multiplier.powerOf2(lsbScale);
3257

3258
        // convert the fraction bits 
3259
        for (unsigned i = 0; i < fbits; ++i) {
3260
                if (value.at(i)) {
3261
                        support::add(partial, multiplier);
3262
                }
3263
                support::add(multiplier, multiplier);
3264
        }
3265
        if (!value.isdenormal()) {
3266
                support::add(partial, multiplier); // add the hidden bit
3267
        }
3268
        str << partial;
3269
        return str.str();
3270
}
3271

3272
//////////////////////////////////////////////////////////////////////////////////////////////
3273
/// stream operators
3274

3275
// ostream output generates an ASCII format for the floating-point argument
3276
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3277
inline std::ostream& operator<<(std::ostream& ostr, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& v) {
3,154✔
3278
        std::streamsize precision = ostr.precision();
3,154✔
3279
        std::streamsize width = ostr.width();
3,154✔
3280

3281
        std::ios_base::fmtflags ff = ostr.flags();
3,154✔
3282
        // extract the format flags that change the representation
3283
        bool scientific = (ff & std::ios_base::scientific) == std::ios_base::scientific;
3,154✔
3284
        bool fixed      = !scientific && (ff & std::ios_base::fixed);
3,154✔
3285

3286
        std::string representation;
3,154✔
3287
        if (fixed) {
3,154✔
3288
                representation = to_decimal_fixpnt_string(v, precision);
86✔
3289
        }
3290
        else {
3291
                std::stringstream ss;
3,068✔
3292
                ss << std::setprecision(precision) << double(v);  // TODO: make this native
3,068✔
3293
                representation = ss.str();
3,068✔
3294
//                representation = to_string(v, precision);
3295
        }
3,068✔
3296

3297
        // implement setw and left/right operators
3298
        std::streamsize repWidth = static_cast<std::streamsize>(representation.size());
3,154✔
3299
        if (width > repWidth) {
3,154✔
3300
                std::streamsize diff = width - static_cast<std::streamsize>(representation.size());
1,709✔
3301
                char fill = ostr.fill();
1,709✔
3302
                if ((ff & std::ios_base::left) == std::ios_base::left) {
1,709✔
3303
                        representation.append(static_cast<unsigned>(diff), fill);
100✔
3304
                }
3305
                else {
3306
                        representation.insert(0ull, static_cast<unsigned>(diff), fill);
1,609✔
3307
                }
3308
        }
3309

3310
        return ostr << representation;
6,308✔
3311
}
3,154✔
3312

3313
// parse a cfloat from a string in either cfloat hex format (nbits.esxHEXVALUEc)
3314
// or a decimal floating-point representation
3315
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3316
bool parse(const std::string& txt, cfloat<nbits,es,bt,hasSubnormals,hasMaxExpValues,isSaturating>& v) {
18✔
3317
        // check if the txt is of the native cfloat form: nbits.esX[0x]hexvaluec
3318
        std::regex cfloat_regex(R"(^[0-9]+\.[0-9]+[xX](0[xX])?[0-9A-Fa-f]+c?$)");
18✔
3319
        if (std::regex_match(txt, cfloat_regex)) {
18✔
3320
                // found a cfloat representation: parse nbits.esxHEXVALUEc
3321
                std::string nbitsStr, esStr, bitStr;
3✔
3322
                auto it = txt.begin();
3✔
3323
                for (; it != txt.end(); ++it) {
9✔
3324
                        if (*it == '.') break;
9✔
3325
                        nbitsStr.append(1, *it);
6✔
3326
                }
3327
                for (++it; it != txt.end(); ++it) {
6✔
3328
                        if (*it == 'x' || *it == 'X') break;
6✔
3329
                        esStr.append(1, *it);
3✔
3330
                }
3331
                for (++it; it != txt.end(); ++it) {
21✔
3332
                        if (*it == 'c') break;
21✔
3333
                        bitStr.append(1, *it);
18✔
3334
                }
3335
                unsigned nbits_in = 0;
3✔
3336
                unsigned es_in = 0;
3✔
3337
                {
3338
                        std::istringstream ss(nbitsStr);
3✔
3339
                        ss >> nbits_in;
3✔
3340
                        if (ss.fail()) return false;
3✔
3341
                }
3✔
3342
                {
3343
                        std::istringstream ss(esStr);
3✔
3344
                        ss >> es_in;
3✔
3345
                        if (ss.fail()) return false;
3✔
3346
                }
3✔
3347
                // native cfloat form must match target configuration
3348
                if (nbits_in != nbits || es_in != es) return false;
3✔
3349
                uint64_t raw = 0;
2✔
3350
                std::istringstream ss(bitStr);
2✔
3351
                ss >> std::hex >> raw;
2✔
3352
                if (ss.fail()) return false;
2✔
3353
                ss >> std::ws;
2✔
3354
                if (!ss.eof()) return false;
2✔
3355
                v.setbits(raw);
2✔
3356
                return true;
2✔
3357
        }
3✔
3358
        else {
3359
                // assume it is a float/double/long double representation
3360
                std::istringstream ss(txt);
15✔
3361
                double d;
3362
                ss >> d;
15✔
3363
                if (ss.fail()) return false;
15✔
3364
                ss >> std::ws;
15✔
3365
                if (!ss.eof()) return false;
15✔
3366
                v = d;
14✔
3367
                return true;
14✔
3368
        }
15✔
3369
}
18✔
3370

3371
// read an ASCII float or cfloat format: nbits.esxNN...NNc, for example: 16.5x7C00c
3372
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3373
inline std::istream& operator>>(std::istream& istr, cfloat<nbits,es,bt,hasSubnormals,hasMaxExpValues,isSaturating>& v) {
11✔
3374
        std::string txt;
11✔
3375
        istr >> txt;
11✔
3376
        if (!parse(txt, v)) {
11✔
NEW
3377
                std::cerr << "unable to parse -" << txt << "- into a cfloat value\n";
×
3378
        }
3379
        return istr;
11✔
3380
}
11✔
3381

3382
// encoding helpers
3383

3384
// return the Unit in the Last Position
3385
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3386
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> ulp(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& a) {
49✔
3387
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> b(a);
49✔
3388
        return ++b - a;
86✔
3389
}
3390

3391
// transform cfloat to a binary representation
3392
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3393
inline std::string to_binary(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& number, bool nibbleMarker = false) {
1,556✔
3394
        std::stringstream s;
1,556✔
3395
        s << "0b";
1,556✔
3396
        unsigned index = nbits;
1,556✔
3397
        s << (number.at(--index) ? '1' : '0') << '.';
1,556✔
3398

3399
        for (int i = int(es) - 1; i >= 0; --i) {
11,035✔
3400
                s << (number.at(--index) ? '1' : '0');
9,479✔
3401
                if (i > 0 && (i % 4) == 0 && nibbleMarker) s << '\'';
9,479✔
3402
        }
3403

3404
        s << '.';
1,556✔
3405

3406
        constexpr int fbits = nbits - 1ull - es;
1,556✔
3407
        for (int i = fbits - 1; i >= 0; --i) {
34,786✔
3408
                s << (number.at(--index) ? '1' : '0');
33,230✔
3409
                if (i > 0 && (i % 4) == 0 && nibbleMarker) s << '\'';
33,230✔
3410
        }
3411

3412
        return s.str();
3,112✔
3413
}
1,556✔
3414

3415
// transform a cfloat into a triple representation
3416
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3417
inline std::string to_triple(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& number, bool nibbleMarker = true) {
3✔
3418
        std::stringstream s;
3✔
3419
        blocktriple<cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>::fbits, BlockTripleOperator::REP, bt> triple;
3✔
3420
        number.normalize(triple);
3✔
3421
        s << to_triple(triple, nibbleMarker);
3✔
3422
        return s.str();
6✔
3423
}
3✔
3424

3425
// Magnitude of a cfloat (equivalent to turning the sign bit off).
3426
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3427
cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>
3428
abs(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& v) {
16,952✔
3429
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> a(v);
16,952✔
3430
        a.setsign(false);
16,952✔
3431
        return a;
16,952✔
3432
}
3433

3434
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3435
cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>
3436
fabs(cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> v) {
40✔
3437
        return abs(v);
40✔
3438
}
3439

3440
////////////////////// debug helpers
3441

3442
// convenience method to gain access to the values of the constexpr variables that govern the cfloat behavior
3443
template<unsigned nbits, unsigned es, typename bt = uint8_t, bool hasSubnormals = false, bool hasMaxExpValues = false, bool isSaturating = false>
3444
void ReportCfloatClassParameters() {
3445
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> a;
3446
        a.constexprClassParameters();
3447
}
3448

3449
//////////////////////////////////////////////////////
3450
/// cfloat - cfloat binary logic operators
3451

3452
template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3453
inline bool operator==(const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& lhs, const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& rhs) {
120,418,164✔
3454
        if (lhs.isnan() || rhs.isnan()) return false;
120,418,164✔
3455
        for (unsigned i = 0; i < lhs.nrBlocks; ++i) {
362,308,097✔
3456
                if (lhs._block[i] != rhs._block[i]) {
245,771,299✔
3457
                        return false;
2,226,454✔
3458
                }
3459
        }
3460
        return true;
116,536,798✔
3461
}
3462
template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3463
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); }
119,020,026✔
3464
template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3465
inline bool operator< (const cfloat<nnbits, nes, nbt, nsub, nsup, nsat>& lhs, const cfloat<nnbits, nes, nbt, nsub, nsup, nsat>& rhs) {
5,568,897✔
3466
        if (lhs.isnan() || rhs.isnan()) return false;
5,568,897✔
3467
        // need this as arithmetic difference is defined as snan(indeterminate)
3468
        if (lhs.isinf(INF_TYPE_NEGATIVE) && rhs.isinf(INF_TYPE_NEGATIVE)) return false;
5,453,389✔
3469
        if (lhs.isinf(INF_TYPE_POSITIVE) && rhs.isinf(INF_TYPE_POSITIVE)) return false;
5,453,375✔
3470
        if constexpr (nsub) {
3471
                cfloat<nnbits, nes, nbt, nsub, nsup, nsat> diff = (lhs - rhs);
5,384,416✔
3472
                return (!diff.iszero() && diff.sign()) ? true : false;  // got to guard against -0
5,384,416✔
3473
        }
3474
        else {
3475
                if (lhs.iszero() && rhs.iszero()) return false;  // we need to 'collapse' all zero encodings
68,926✔
3476
                if (lhs.sign() && !rhs.sign()) return true;
68,918✔
3477
                if (!lhs.sign() && rhs.sign()) return false;
51,695✔
3478
                bool positive = lhs.ispos();
34,472✔
3479
                if (positive) {
34,472✔
3480
                        if (lhs.scale() < rhs.scale()) return true;
17,252✔
3481
                        if (lhs.scale() > rhs.scale()) return false;
12,786✔
3482
                }
3483
                else {
3484
                        if (lhs.scale() > rhs.scale()) return true;
17,220✔
3485
                        if (lhs.scale() < rhs.scale()) return false;
12,770✔
3486
                }
3487
                // sign and scale are the same
3488
                if (lhs.scale() == rhs.scale()) {
16,642✔
3489
                        // compare fractions: we do not have subnormals, so we can ignore the hidden bit
3490
                        blockbinary<nnbits - 1ull - nes, nbt> l, r;
3491
                        lhs.fraction(l);
16,642✔
3492
                        rhs.fraction(r);
16,642✔
3493
                        blockbinary<nnbits - nes, nbt> ll, rr; // fbits + 1 so we can 0 extend to honor 2's complement encoding of blockbinary
3494
                        ll.assignWithoutSignExtend(l);
16,642✔
3495
                        rr.assignWithoutSignExtend(r);
16,642✔
3496
                        return (positive ? (ll < rr) : (ll > rr));
16,642✔
3497
                }
3498
                return false;
×
3499
        }
3500
}
3501
template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3502
inline bool operator> (const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& lhs, const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& rhs) { 
2,794,589✔
3503
        if (lhs.isnan() || rhs.isnan()) return false;
2,794,589✔
3504
        // need this as arithmetic difference is defined as snan(indeterminate)
3505
        if (lhs.isinf(INF_TYPE_NEGATIVE) && rhs.isinf(INF_TYPE_NEGATIVE)) return false;
2,786,475✔
3506
        if (lhs.isinf(INF_TYPE_POSITIVE) && rhs.isinf(INF_TYPE_POSITIVE)) return false;
2,786,461✔
3507
        return  operator< (rhs, lhs); 
2,786,445✔
3508
}
3509
template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3510
inline bool operator<=(const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& lhs, const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& rhs) { 
1,398,017✔
3511
        if (lhs.isnan() || rhs.isnan()) return false;
1,398,017✔
3512
        return !operator> (lhs, rhs); 
1,389,917✔
3513
}
3514
template<unsigned nnbits, unsigned nes, typename nbt, bool nsub, bool nsup, bool nsat>
3515
inline bool operator>=(const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& lhs, const cfloat<nnbits,nes,nbt,nsub,nsup,nsat>& rhs) {
1,399,400✔
3516
        if (lhs.isnan() || rhs.isnan()) return false;
1,399,400✔
3517
        return !operator< (lhs, rhs); 
1,391,294✔
3518
}
3519

3520
//////////////////////////////////////////////////////
3521
/// cfloat - cfloat binary arithmetic operators
3522

3523
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3524
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✔
3525
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> sum(lhs);
9,974,529✔
3526
        sum += rhs;
9,974,529✔
3527
        return sum;
9,974,529✔
3528
}
3529
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3530
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,446,104✔
3531
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> diff(lhs);
8,446,104✔
3532
        diff -= rhs;
8,446,104✔
3533
        return diff;
8,446,104✔
3534
}
3535
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3536
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,265,958✔
3537
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> mul(lhs);
4,265,958✔
3538
        mul *= rhs;
4,265,958✔
3539
        return mul;
4,265,958✔
3540
}
3541
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3542
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,135,313✔
3543
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> ratio(lhs);
5,135,313✔
3544
        ratio /= rhs;
5,135,313✔
3545
        return ratio;
5,135,312✔
3546
}
3547

3548
/// binary cfloat - literal arithmetic operators
3549

3550
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3551
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator+(float lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3552
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> sum(lhs);
3553
        sum += rhs;
3554
        return sum;
3555
}
3556
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3557
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator-(float lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3558
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> diff(lhs);
3559
        diff -= rhs;
3560
        return diff;
3561
}
3562
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3563
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator*(float lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3564
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> mul(lhs);
3565
        mul *= rhs;
3566
        return mul;
3567
}
3568
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3569
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator/(float lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
2✔
3570
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> ratio(lhs);
2✔
3571
        ratio /= rhs;
2✔
3572
        return ratio;
2✔
3573
}
3574

3575
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3576
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator+(double lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
4✔
3577
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> sum(lhs);
4✔
3578
        sum += rhs;
4✔
3579
        return sum;
4✔
3580
}
3581
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3582
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator-(double lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3583
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> diff(lhs);
3584
        diff -= rhs;
3585
        return diff;
3586
}
3587
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3588
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator*(double lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
16✔
3589
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> mul(lhs);
16✔
3590
        mul *= rhs;
16✔
3591
        return mul;
16✔
3592
}
3593
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3594
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator/(double lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3595
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> ratio(lhs);
3596
        ratio /= rhs;
3597
        return ratio;
3598
}
3599

3600
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3601
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator+(int lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
×
3602
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> sum(lhs);
×
3603
        sum += rhs;
×
3604
        return sum;
×
3605
}
3606
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3607
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator-(int lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3608
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> diff(lhs);
3609
        diff -= rhs;
3610
        return diff;
3611
}
3612
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3613
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator*(int lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
38✔
3614
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> mul(lhs);
38✔
3615
        mul *= rhs;
38✔
3616
        return mul;
38✔
3617
}
3618
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3619
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator/(int lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
374✔
3620
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> ratio(lhs);
374✔
3621
        ratio /= rhs;
374✔
3622
        return ratio;
374✔
3623
}
3624

3625
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3626
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator+(unsigned int lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3627
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> sum(lhs);
3628
        sum += rhs;
3629
        return sum;
3630
}
3631
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3632
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator-(unsigned int lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3633
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> diff(lhs);
3634
        diff -= rhs;
3635
        return diff;
3636
}
3637
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3638
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator*(unsigned int lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3639
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> mul(lhs);
3640
        mul *= rhs;
3641
        return mul;
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/(unsigned int lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3645
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> ratio(lhs);
3646
        ratio /= rhs;
3647
        return ratio;
3648
}
3649

3650
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3651
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator+(long long lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3652
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> sum(lhs);
3653
        sum += rhs;
3654
        return sum;
3655
}
3656
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3657
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator-(long long lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3658
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> diff(lhs);
3659
        diff -= rhs;
3660
        return diff;
3661
}
3662
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3663
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator*(long long lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3664
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> mul(lhs);
3665
        mul *= rhs;
3666
        return mul;
3667
}
3668
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3669
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator/(long long lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3670
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> ratio(lhs);
3671
        ratio /= rhs;
3672
        return ratio;
3673
}
3674

3675
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3676
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator+(unsigned long long lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3677
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> sum(lhs);
3678
        sum += rhs;
3679
        return sum;
3680
}
3681
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3682
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator-(unsigned long long lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3683
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> diff(lhs);
3684
        diff -= rhs;
3685
        return diff;
3686
}
3687
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3688
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator*(unsigned long long lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3689
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> mul(lhs);
3690
        mul *= rhs;
3691
        return mul;
3692
}
3693
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3694
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator/(unsigned long long lhs, const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& rhs) {
3695
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> ratio(lhs);
3696
        ratio /= rhs;
3697
        return ratio;
3698
}
3699

3700
///  binary cfloat - literal arithmetic operators
3701

3702
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3703
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator+(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, float rhs) {
3704
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
3705
        Cfloat sum(lhs);
3706
        sum += Cfloat(rhs);
3707
        return sum;
3708
}
3709
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3710
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator-(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, float rhs) {
3711
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
3712
        Cfloat diff(lhs);
3713
        diff -= rhs;
3714
        return diff;
3715
}
3716
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3717
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator*(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, float rhs) {
3718
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
3719
        Cfloat mul(lhs);
3720
        mul *= Cfloat(rhs);
3721
        return mul;
3722
}
3723
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3724
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator/(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, float rhs) {
3725
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
3726
        Cfloat ratio(lhs);
3727
        ratio /= Cfloat(rhs);
3728
        return ratio;
3729
}
3730

3731
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3732
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator+(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, double rhs) {
3733
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
3734
        Cfloat sum(lhs);
3735
        sum += Cfloat(rhs);
3736
        return sum;
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-(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, double rhs) {
4✔
3740
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
3741
        Cfloat diff(lhs);
4✔
3742
        diff -= rhs;
4✔
3743
        return diff;
4✔
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*(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, double rhs) {
3747
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
3748
        Cfloat mul(lhs);
3749
        mul *= Cfloat(rhs);
3750
        return mul;
3751
}
3752
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3753
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator/(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, double rhs) {
3754
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
3755
        Cfloat ratio(lhs);
3756
        ratio /= Cfloat(rhs);
3757
        return ratio;
3758
}
3759

3760
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3761
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator+(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, int rhs) {
3762
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
3763
        Cfloat sum(lhs);
3764
        sum += Cfloat(rhs);
3765
        return sum;
3766
}
3767
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3768
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator-(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, int rhs) {
4✔
3769
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
3770
        Cfloat diff(lhs);
4✔
3771
        diff -= rhs;
4✔
3772
        return diff;
4✔
3773
}
3774
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3775
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator*(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, int rhs) {
3776
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
3777
        Cfloat mul(lhs);
3778
        mul *= Cfloat(rhs);
3779
        return mul;
3780
}
3781
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3782
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> operator/(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, int rhs) {
3783
        using Cfloat = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>;
3784
        Cfloat ratio(lhs);
3785
        ratio /= Cfloat(rhs);
3786
        return ratio;
3787
}
3788

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

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

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

3876
///////////////////////////////////////////////////////////////////////
3877
///   binary logic literal comparisons
3878

3879
// cfloat - literal float logic operators
3880
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3881
inline bool operator==(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, float rhs) {
1✔
3882
        return float(lhs) == rhs;
1✔
3883
}
3884
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3885
inline bool operator!=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, float rhs) {
1✔
3886
        return float(lhs) != rhs;
1✔
3887
}
3888
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3889
inline bool operator< (const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, float rhs) {
2✔
3890
        return float(lhs) < rhs;
2✔
3891
}
3892
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3893
inline bool operator> (const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, float rhs) {
1✔
3894
        return float(lhs) > rhs;
1✔
3895
}
3896
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3897
inline bool operator<=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, float rhs) {
1✔
3898
        return float(lhs) <= rhs;
1✔
3899
}
3900
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3901
inline bool operator>=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, float rhs) {
1✔
3902
        return float(lhs) >= rhs;
1✔
3903
}
3904
// cfloat - literal double logic operators
3905
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3906
inline bool operator==(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, double rhs) {
1✔
3907
        return double(lhs) == rhs;
1✔
3908
}
3909
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3910
inline bool operator!=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, double rhs) {
1✔
3911
        return double(lhs) != rhs;
1✔
3912
}
3913
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3914
inline bool operator< (const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, double rhs) {
336✔
3915
        return double(lhs) < rhs;
336✔
3916
}
3917
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3918
inline bool operator> (const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, double rhs) {
892✔
3919
        return double(lhs) > rhs;
892✔
3920
}
3921
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3922
inline bool operator<=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, double rhs) {
1✔
3923
        return double(lhs) <= rhs;
1✔
3924
}
3925
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3926
inline bool operator>=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, double rhs) {
1✔
3927
        return double(lhs) >= rhs;
1✔
3928
}
3929

3930
#if LONG_DOUBLE_SUPPORT
3931
// cfloat - literal long double logic operators
3932
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3933
inline bool operator==(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long double rhs) {
1✔
3934
        return (long double)(lhs) == rhs;
1✔
3935
}
3936
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3937
inline bool operator!=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long double rhs) {
1✔
3938
        return (long double)(lhs) != rhs;
1✔
3939
}
3940
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3941
inline bool operator< (const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long double rhs) {
1✔
3942
        return (long double)(lhs) < rhs;
1✔
3943
}
3944
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3945
inline bool operator> (const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long double rhs) {
1✔
3946
        return (long double)(lhs) > rhs;
1✔
3947
}
3948
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3949
inline bool operator<=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long double rhs) {
1✔
3950
        return (long double)(lhs) <= rhs;
1✔
3951
}
3952
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3953
inline bool operator>=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long double rhs) {
1✔
3954
        return (long double)(lhs) >= rhs;
1✔
3955
}
3956
#endif
3957

3958
// cfloat - literal int logic operators
3959
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3960
inline bool operator==(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, int rhs) {
11✔
3961
        return operator==(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
11✔
3962
}
3963
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3964
inline bool operator!=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, int rhs) {
1✔
3965
        return operator!=(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
1✔
3966
}
3967
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3968
inline bool operator< (const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, int rhs) {
107,196✔
3969
        return operator<(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
107,196✔
3970
}
3971
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3972
inline bool operator> (const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, int rhs) {
666✔
3973
        return operator<(cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs), lhs);
666✔
3974
}
3975
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3976
inline bool operator<=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, int rhs) {
1✔
3977
        return operator<(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs)) || operator==(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
1✔
3978
}
3979
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3980
inline bool operator>=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, int rhs) {
1✔
3981
        return !operator<(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
1✔
3982
}
3983

3984
// cfloat - long long logic operators
3985
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3986
inline bool operator==(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long long rhs) {
3987
        return operator==(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
3988
}
3989
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3990
inline bool operator!=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long long rhs) {
3991
        return operator!=(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
3992
}
3993
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3994
inline bool operator< (const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long long rhs) {
3995
        return operator<(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
3996
}
3997
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
3998
inline bool operator> (const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long long rhs) {
3999
        return operator<(cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs), lhs);
4000
}
4001
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4002
inline bool operator<=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long long rhs) {
4003
        return operator<(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs)) || operator==(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
4004
}
4005
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4006
inline bool operator>=(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& lhs, long long rhs) {
4007
        return !operator<(lhs, cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>(rhs));
4008
}
4009

4010
// standard library functions for floating point
4011

4012
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4013
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> frexp(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& x, int* exp) {
57,412✔
4014
        *exp = x.scale();
57,412✔
4015
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> fraction(x);
57,412✔
4016
        fraction.setexponent(0);
57,412✔
4017
        return fraction;
57,412✔
4018
}
4019

4020
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4021
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> ldexp(const cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& x, int exp) {
765✔
4022
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> result(x);
765✔
4023
        int xexp = x.scale();
765✔
4024
        result.setexponent(xexp + exp);  // TODO: this does not work for subnormals
765✔
4025
        return result;
765✔
4026
}
4027

4028
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4029
inline cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> 
4030
fma(cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> x,
3✔
4031
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> y,
4032
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> z) {
4033
        cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> fused{ 0 };
3✔
4034
        constexpr unsigned FBITS = cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>::fbits;
3✔
4035
        constexpr unsigned EXTRA_FBITS = FBITS+2;
3✔
4036
        constexpr unsigned EXTENDED_PRECISION = nbits + EXTRA_FBITS;
3✔
4037
        // the C++ fma spec indicates that the x*y+z is evaluated in 'infinite' precision
4038
        // with only a single rounding event. The minimum finite precision that would behave like this
4039
        // is the precision where the product x*y does not need to be rounded, which will
4040
        // need at least 2*(fbits+1) mantissa bits to capture all bits that can be
4041
        // generated by the product.
4042
        cfloat<EXTENDED_PRECISION, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> preciseX(x), preciseY(y), preciseZ(z);
3✔
4043
//        ReportValue(preciseX, "extended precision x");
4044
//        ReportValue(preciseY, "extended precision y");
4045
//        ReportValue(preciseZ, "extended precision z");
4046
        cfloat<EXTENDED_PRECISION, es, bt, hasSubnormals, hasMaxExpValues, isSaturating> product = preciseX * preciseY;
3✔
4047
//        ReportValue(product, "extended precision p");
4048
        fused = product + preciseZ;
3✔
4049
        return fused;
3✔
4050
}
4051

4052
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4053
cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>&
4054
minpos(cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& c) {
4055
        return c.minpos();
4056
}
4057
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4058
cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>&
4059
maxpos(cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& c) {
4060
        return c.maxpos();
4061
}
4062
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4063
cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>&
4064
minneg(cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& c) {
4065
        return c.minneg();
4066
}
4067
template<unsigned nbits, unsigned es, typename bt, bool hasSubnormals, bool hasMaxExpValues, bool isSaturating>
4068
cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>&
4069
maxneg(cfloat<nbits, es, bt, hasSubnormals, hasMaxExpValues, isSaturating>& c) {
4070
        return c.maxneg();
4071
}
4072

4073
}} // namespace sw::universal
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc