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

stillwater-sc / universal / 22006915043

13 Feb 2026 11:54PM UTC coverage: 84.79% (-0.1%) from 84.885%
22006915043

push

github

web-flow
V3.98: posit v1 to posit v2 transition and adding arm64, PPC, and MINGW as cross-platform targets (#508)

* Incrementing SEMVER v3.98

* Rename posit → posit1, posit2 → posit: make BlockType-based posit the default

Move the old bitset-based posit (2-param template) to posit1/ for backward
compatibility, and promote the new BlockType-based posit2 (3-param template)
to posit/ so that #include <universal/number/posit/posit.hpp> gives consumers
the modern implementation.

Key changes:
- include/sw/universal/number/posit/ now contains the new 3-param posit
- include/sw/universal/number/posit1/ preserves the old 2-param posit
- Math library copied from posit1 into new posit with 3-param signatures
- posit_traits.hpp updated for 3-param, new posit1_traits.hpp for 2-param
- Test directories: static/posit/ (new), static/posit1/ (old)
- sqrt.hpp fixed for blockbinary::bits() → unsigned cast in table lookups
- Verification header decoupled from direct mathlib include

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix quire tests to include posit1 (quire requires old posit)

The quire super-accumulator is only implemented for the old 2-param
posit (now posit1). Update the two quire test files to include
posit1/posit1.hpp instead of posit/posit.hpp.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix posito to use posit1 (depends on old posit internal classes)

posito uses positRegime, positExponent, and positFraction from the old
posit implementation. Update posito.hpp and posito_impl.hpp to include
from posit1/ instead of posit/. Also fix number_system.cpp which
explicitly included posit/posit.hpp alongside posito.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix remaining posit→posit1 include paths for CI

- valid_impl.hpp: use posit1/posit_impl.hpp (valid depends on bitblock)
- exceptions.hpp: add shared include guard to prevent redefinition when
  both posit/ and posit1/ exceptions are included in the same TU
- takum, ... (continued)

2525 of 2732 new or added lines in 44 files covered. (92.42%)

36 existing lines in 8 files now uncovered.

36972 of 43604 relevant lines covered (84.79%)

7199787.74 hits per line

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

97.96
/include/sw/universal/internal/blockbinary/blockbinary.hpp
1
#pragma once
2
// blockbinary.hpp: parameterized blocked binary number system representing a 2's complement binary number
3
//
4
// Copyright (C) 2017 Stillwater Supercomputing, Inc.
5
// SPDX-License-Identifier: MIT
6
//
7
// This file is part of the universal numbers project, which is released under an MIT Open Source license.
8
#include <cstdint>
9
#include <iostream>
10
#include <iomanip>
11
#include <string>
12
#include <sstream>
13
#include <universal/number/shared/specific_value_encoding.hpp>
14

15
namespace sw { namespace universal {
16

17
enum class BinaryNumberType {
18
        Signed   = 0, // { ...,-3,-2,-1,0,1,2,3,... }    // 2's complement encoding
19
        Unsigned = 1  // {              0,1,2,3,... }    // binary encoding
20
};
21

22
// forward references
23
template<unsigned nbits, typename BlockType, BinaryNumberType NumberType> class blockbinary;
24
template<unsigned nbits, typename BlockType, BinaryNumberType NumberType> blockbinary<nbits, BlockType, NumberType> twosComplement(const blockbinary<nbits, BlockType, NumberType>&);
25
template<unsigned nbits, typename BlockType, BinaryNumberType NumberType> struct quorem;
26
template<unsigned nbits, typename BlockType, BinaryNumberType NumberType> quorem<nbits, BlockType, NumberType> longdivision(const blockbinary<nbits, BlockType, NumberType>&, const blockbinary<nbits, BlockType, NumberType>&);
27

28
// idiv_t for blockbinary<nbits> to capture quotient and remainder during long division
29
template<unsigned nbits, typename BlockType, BinaryNumberType NumberType>
30
struct quorem {
31
        int exceptionId;
32
        blockbinary<nbits, BlockType, NumberType> quo; // quotient
33
        blockbinary<nbits, BlockType, NumberType> rem;  // remainder
34
};
35

36
// maximum positive 2's complement number: b01111...1111
37
template<unsigned nbits, typename BlockType = uint8_t, BinaryNumberType NumberType>
38
constexpr blockbinary<nbits, BlockType, NumberType>& maxpos(blockbinary<nbits, BlockType, NumberType>& a) {
39
        a.clear();
40
        a.flip();
41
        if constexpr (NumberType == BinaryNumberType::Signed) {
42
                a.setbit(nbits - 1, false);
43
        }
44
        return a;
45
}
46

47
// maximum negative 2's complement number: b1000...0000
48
template<unsigned nbits, typename BlockType = uint8_t, BinaryNumberType NumberType>
49
constexpr blockbinary<nbits, BlockType, NumberType>& maxneg(blockbinary<nbits, BlockType, NumberType>& a) {
17,195,575✔
50
        a.clear();
17,195,575✔
51
        if constexpr (NumberType == BinaryNumberType::Signed) {
52
                a.setbit(nbits - 1);
17,195,575✔
53
        }
54
        return a;
17,195,575✔
55
}
56

57
// generate the 2's complement of the block binary number
58
template<unsigned nbits, typename BlockType, BinaryNumberType NumberType>
59
blockbinary<nbits, BlockType, NumberType> twosComplement(const blockbinary<nbits, BlockType, NumberType>& orig) {
40,897,029✔
60
        blockbinary<nbits, BlockType, NumberType> twosC(orig);
40,897,029✔
61
        blockbinary<nbits, BlockType, NumberType> plusOne(1);
40,897,029✔
62
        twosC.flip();
40,897,029✔
63
        twosC += plusOne;
40,897,029✔
64
        return twosC;
40,897,029✔
65
}
66

67
// Truncate a bigger posit to fit in a smaller
68
template<unsigned srcbits, unsigned tgtbits, typename bt, BinaryNumberType nt>
69
void truncate(const blockbinary<srcbits, bt, nt>& src, blockbinary<tgtbits, bt, nt>& tgt) {
13,348,432✔
70
        static_assert(tgtbits < srcbits, "truncate requires source posit to be bigger than target posit");
71
        constexpr unsigned diff = srcbits - tgtbits;
13,348,432✔
72
        for (unsigned i = 0; i < tgtbits; ++i) { // TODO: optimize for limbs
276,633,909✔
73
                tgt.setbit(i, src.test(i + diff));
263,285,361✔
74
        }
75
}
13,348,548✔
76

77
/*
78
NOTES
79

80
for block arithmetic, we need to manage a carry bit.
81
This disqualifies using uint64_t as a block type as we can't catch the overflow condition
82
in the same way as the other native types, uint8_t, uint16_t, uint32_t.
83

84
We could use a sint64_t and then convert to uint64_t and observe the MSB. Very different 
85
logic though.
86
*/
87

88
// a block-based binary number configurable to be signed or unsigned. When signed it uses 2's complement encoding
89
template<unsigned _nbits, typename bt = uint8_t, BinaryNumberType _NumberType = BinaryNumberType::Signed>
90
class blockbinary {
91
public:
92
        static constexpr unsigned nbits = _nbits;
93
        typedef bt BlockType;
94
        static constexpr BinaryNumberType NumberType = _NumberType;
95

96
        static constexpr unsigned bitsInByte = 8;
97
        static constexpr unsigned bitsInBlock = sizeof(bt) * bitsInByte;
98
        static constexpr unsigned nrBlocks = (0 == nbits ? 1 : (1ull + ((nbits - 1ull) / bitsInBlock)));
99
        static constexpr uint64_t storageMask = (0xFFFFFFFFFFFFFFFFull >> (64 - bitsInBlock));
100
        static constexpr bt       maxBlockValue = bt(-1);
101

102
        static constexpr unsigned MSU = nrBlocks - 1; // MSU == Most Significant Unit
103
        static constexpr bt       ALL_ONES = bt(~0);
104
        static constexpr unsigned maxShift = (0 == nbits ? 0 : (nrBlocks* bitsInBlock - nbits)); // protect the shift that is >= sizeof(bt)
105
        static constexpr bt       MSU_MASK = (0 == nbits ? bt(0) : (ALL_ONES >> maxShift));      // the other side of this protection
106
        static constexpr bt       SIGN_BIT_MASK = (0 == nbits ? bt(0) : (bt(bt(1) << ((nbits - 1ull) % bitsInBlock))));
107

108
        static constexpr bool     uniblock64 = (bitsInBlock == 64) && (nrBlocks == 1);
109
        static_assert(bitsInBlock < 64 || uniblock64, "storage unit for multi-block arithmetic needs to be one of [uint8_t | uint16_t | uint32_t]");
110

111
        /// trivial constructor
112
        blockbinary() = default;
113

114
        /// construct a blockbinary from another: bt must be the same
115
        template<unsigned nnbits>
116
        blockbinary(const blockbinary<nnbits, BlockType, NumberType>& rhs) : _block{} { this->assign(rhs); }
174,184,930✔
117

118
        // initializer for long long
119
        constexpr blockbinary(long long initial_value) noexcept : _block{} { *this = initial_value; }
1,599,424✔
120

121
        // specific value constructors
122
        constexpr blockbinary(const std::string& s) noexcept : _block{} {  }  // TODO
123
        constexpr blockbinary(const SpecificValue code) : _block{} {
124
                switch (code) {
125
                case SpecificValue::infpos:
126
                case SpecificValue::maxpos:
127
                        maxpos();
128
                        break;
129
                case SpecificValue::minpos:
130
                        minpos();
131
                        break;
132
                case SpecificValue::qnan:
133
                case SpecificValue::snan:
134
                case SpecificValue::nar:
135
                case SpecificValue::zero:
136
                default:
137
                        zero();
138
                        break;
139
                case SpecificValue::minneg:
140
                        minneg();
141
                        break;
142
                case SpecificValue::infneg:
143
                case SpecificValue::maxneg:
144
                        maxneg();
145
                        break;
146
                }
147
        }
148

149
        constexpr blockbinary& operator=(long long rhs) noexcept {
15,084,890✔
150
                if constexpr (1 < nrBlocks) {
151
                        for (unsigned i = 0; i < nrBlocks; ++i) {
63,544,119✔
152
                                _block[i] = rhs & storageMask;
49,394,710✔
153
                                rhs >>= bitsInBlock;
49,394,710✔
154
                        }
155
                        // enforce precondition for fast comparison by properly nulling bits that are outside of nbits
156
                        _block[MSU] &= MSU_MASK;
14,149,409✔
157
                } 
158
                else if constexpr (1 == nrBlocks) {
159
                        _block[0] = rhs & storageMask;
935,481✔
160
                        // enforce precondition for fast comparison by properly nulling bits that are outside of nbits
161
                        _block[MSU] &= MSU_MASK;
935,481✔
162
                }
163
                return *this;
15,084,890✔
164
        }
165

166
        // conversion operators
167
        explicit operator int() const                { return int(to_sll()); }
168
        explicit operator long() const               { return long(to_sll()); }
6✔
169
        explicit operator long long() const          { return to_sll(); }
395,846✔
170
        explicit operator unsigned int() const       { return unsigned(to_ull()); }
109,858,925✔
171
        explicit operator unsigned long() const      { return (unsigned long)to_ull(); }
UNCOV
172
        explicit operator unsigned long long() const { return to_ull(); }
×
173
        // TODO: these need proper implementations that can convert very large integers to the proper scale afforded by the floating-point formats
174
        explicit operator float() const              { return to_native<float>(); }
36✔
175
        explicit operator double() const             { return to_native<double>(); }
132,145✔
176

177
#if LONG_DOUBLE_SUPPORT
178
        explicit operator long double() const        { return to_native<long double>(); }
179
#endif
180

181
        // limb access operators
182
//        constexpr BlockType& operator[](unsigned index) { return _block[index]; }
183
        constexpr BlockType operator[](unsigned index) const { return _block[index]; }
169,812,758✔
184

185
        // prefix operators
186
        blockbinary operator-() const {
432,785✔
187
                blockbinary negated(*this);
432,785✔
188
                blockbinary plusOne(1);
432,785✔
189
                negated.flip();
432,785✔
190
                negated += plusOne;
432,785✔
191
                return negated;
432,785✔
192
        }
193
        // one's complement
194
        blockbinary operator~() const {
195
                blockbinary complement(*this);
196
                complement.flip();
197
                return complement;
198
        }
199
        // increment/decrement
200
        blockbinary operator++(int) {
201
                blockbinary tmp(*this);
202
                operator++();
203
                return tmp;
204
        }
205
        blockbinary& operator++() {
3,018,755✔
206
                blockbinary increment;
207
                increment.setbits(0x1);
3,018,755✔
208
                *this += increment;
3,018,755✔
209
                return *this;
3,018,755✔
210
        }
211
        blockbinary operator--(int) {
212
                blockbinary tmp(*this);
213
                operator--();
214
                return tmp;
215
        }
216
        blockbinary& operator--() {
16✔
217
                blockbinary decrement;
218
                decrement.setbits(0x1);
16✔
219
                return *this -= decrement;
32✔
220
        }
221
        // logic operators
222
        blockbinary  operator~() {
223
                blockbinary<nbits, bt> complement(*this);
224
                complement.flip();
225
                return complement;
226
        }
227
        // arithmetic operators
228
        blockbinary& operator+=(const blockbinary& rhs) {
231,003,113✔
229
                if constexpr (nrBlocks == 1) {
230
                        _block[0] = static_cast<bt>(_block[0] + rhs.block(0));
117,457,645✔
231
                        // null any leading bits that fall outside of nbits
232
                        _block[MSU] = static_cast<bt>(MSU_MASK & _block[MSU]);
117,457,645✔
233
                }
234
                else {
235
                        blockbinary sum;
236
                        BlockType* pA = _block;
113,545,468✔
237
                        BlockType const* pB = rhs._block;
113,545,468✔
238
                        BlockType* pC = sum._block;
113,545,468✔
239
                        BlockType* pEnd = pC + nrBlocks;
113,545,468✔
240
                        std::uint64_t carry = 0;
113,545,468✔
241
                        while (pC != pEnd) {
364,943,354✔
242
                                carry += static_cast<std::uint64_t>(*pA) + static_cast<std::uint64_t>(*pB);
251,397,886✔
243
                                *pC = static_cast<bt>(carry);
251,397,886✔
244
                                carry >>= bitsInBlock;
251,397,886✔
245
                                ++pA; ++pB; ++pC;
251,397,886✔
246
                        }
247
                        // enforce precondition for fast comparison by properly nulling bits that are outside of nbits
248
                        BlockType* pLast = pEnd - 1;
113,545,468✔
249
                        *pLast = static_cast<bt>(MSU_MASK & *pLast);
113,545,468✔
250
                        *this = sum;
113,545,468✔
251
                }
252
                return *this;
231,003,113✔
253
        }
254
        blockbinary& operator-=(const blockbinary& rhs) {
26,906,984✔
255
                return operator+=(sw::universal::twosComplement(rhs));
26,906,984✔
256
        }
257
#define BLOCKBINARY_FAST_MUL
258
#ifdef BLOCKBINARY_FAST_MUL
259
        blockbinary& operator*=(const blockbinary& rhs) {
72,289,089✔
260
                if constexpr (NumberType == BinaryNumberType::Signed) {
261
                        if constexpr (nrBlocks == 1) {
262
                                _block[0] = static_cast<bt>(static_cast<std::uint64_t>(_block[0]) * static_cast<std::uint64_t>(rhs.block(0)));
47,319,856✔
263
                        }
264
                        else {
265
                                // is there a better way than upconverting to deal with maxneg in a 2's complement encoding?
266
                                blockbinary<nbits + 1, BlockType, NumberType> base(*this);
24,969,233✔
267
                                blockbinary<nbits + 1, BlockType, NumberType> multiplicant(rhs);
24,969,233✔
268
                                bool resultIsNeg = (base.isneg() ^ multiplicant.isneg());
24,969,233✔
269
                                if (base.isneg()) {
24,969,233✔
270
                                        base.twosComplement();
12,484,608✔
271
                                }
272
                                if (multiplicant.isneg()) {
24,969,233✔
273
                                        multiplicant.twosComplement();
12,484,609✔
274
                                }
275
                                clear();
24,969,233✔
276
                                for (unsigned i = 0; i < static_cast<unsigned>(nrBlocks); ++i) {
76,218,521✔
277
                                        std::uint64_t segment(0);
51,249,288✔
278
                                        for (unsigned j = 0; j < static_cast<unsigned>(nrBlocks); ++j) {
157,680,840✔
279
                                                segment += static_cast<std::uint64_t>(base.block(i)) * static_cast<std::uint64_t>(multiplicant.block(j));
106,431,552✔
280

281
                                                if (i + j < static_cast<unsigned>(nrBlocks)) {
106,431,552✔
282
                                                        segment += _block[i + j];
78,840,420✔
283
                                                        _block[i + j] = static_cast<bt>(segment);
78,840,420✔
284
                                                        segment >>= bitsInBlock;
78,840,420✔
285
                                                }
286
                                        }
287
                                }
288
                                if (resultIsNeg) twosComplement();
24,969,233✔
289
                        }
290
                }
291
                else {  // unsigned
292
                        if constexpr (nrBlocks == 1) {
293
                                _block[0] = static_cast<bt>(static_cast<std::uint64_t>(_block[0]) * static_cast<std::uint64_t>(rhs.block(0)));
294
                        }
295
                        else {
296
                                blockbinary base(*this);
297
                                blockbinary multiplicant(rhs);
298
                                clear();
299
                                for (unsigned i = 0; i < static_cast<unsigned>(nrBlocks); ++i) {
300
                                        std::uint64_t segment(0);
301
                                        for (unsigned j = 0; j < static_cast<unsigned>(nrBlocks); ++j) {
302
                                                segment += static_cast<std::uint64_t>(base.block(i)) * static_cast<std::uint64_t>(multiplicant.block(j));
303

304
                                                if (i + j < static_cast<unsigned>(nrBlocks)) {
305
                                                        segment += _block[i + j];
306
                                                        _block[i + j] = static_cast<bt>(segment);
307
                                                        segment >>= bitsInBlock;
308
                                                }
309
                                        }
310
                                }
311
                        }
312
                }
313
                // null any leading bits that fall outside of nbits
314
                _block[MSU] = static_cast<bt>(MSU_MASK & _block[MSU]);
72,289,089✔
315
                return *this;
72,289,089✔
316
        }
317
#else
318
        blockbinary& operator*=(const blockbinary& rhs) { // modulo in-place
319
                blockbinary base(*this);
320
                blockbinary multiplicant(rhs);
321
                clear();
322
                for (unsigned i = 0; i < nbits; ++i) {
323
                        if (base.at(i)) {
324
                                operator+=(multiplicant);
325
                        }
326
                        multiplicant <<= 1;
327
                }
328
                // since we used operator+=, which enforces the nulling of leading bits
329
                // we don't need to null here
330
                return *this;
331
        }
332
#endif
333
        blockbinary& operator/=(const blockbinary& rhs) {
199,209✔
334
                if constexpr (nbits == (sizeof(BlockType) * 8)) {
335
                        if (rhs.iszero()) {
197,495✔
336
                                *this = 0;
265✔
337
                                return *this;
265✔
338
                        }
339
                        if constexpr (sizeof(BlockType) == 1) {
340
                                _block[0] = static_cast<bt>(std::int8_t(_block[0]) / std::int8_t(rhs._block[0]));
66,010✔
341
                        }
342
                        else if constexpr (sizeof(BlockType) == 2) {
343
                                _block[0] = static_cast<bt>(std::int16_t(_block[0]) / std::int16_t(rhs._block[0]));
131,208✔
344
                        }
345
                        else if constexpr (sizeof(BlockType) == 4) {
346
                                _block[0] = static_cast<bt>(std::int32_t(_block[0]) / std::int32_t(rhs._block[0]));
6✔
347
                        }
348
                        else if constexpr (sizeof(BlockType) == 8) {
349
                                _block[0] = static_cast<bt>(std::int64_t(_block[0]) / std::int64_t(rhs._block[0]));
6✔
350
                        }
351
                        _block[0] = static_cast<bt>(MSU_MASK & _block[0]);
197,230✔
352
                }
353
                else {
354
                        quorem<nbits, BlockType, NumberType> result = longdivision(*this, rhs);
1,714✔
355
                        *this = result.quo;
1,714✔
356
                }
357
                return *this;
198,944✔
358
        }
359
        blockbinary& operator%=(const blockbinary& rhs) {
1,464,257✔
360
                if constexpr (nbits == (sizeof(BlockType) * 8)) {
361
                        if (rhs.iszero()) {
131,741✔
362
                                *this = 0;
260✔
363
                                return *this;
260✔
364
                        }
365
                        if constexpr (sizeof(BlockType) == 1) {
366
                                _block[0] = static_cast<bt>(std::int8_t(_block[0]) % std::int8_t(rhs._block[0]));
65,647✔
367
                        }
368
                        else if constexpr (sizeof(BlockType) == 2) {
369
                                _block[0] = static_cast<bt>(std::int16_t(_block[0]) % std::int16_t(rhs._block[0]));
65,806✔
370
                        }
371
                        else if constexpr (sizeof(BlockType) == 4) {
372
                                _block[0] = static_cast<bt>(std::int32_t(_block[0]) % std::int32_t(rhs._block[0]));
13✔
373
                        }
374
                        else if constexpr (sizeof(BlockType) == 8) {
375
                                _block[0] = static_cast<bt>(std::int64_t(_block[0]) % std::int64_t(rhs._block[0]));
15✔
376
                        }
377
                        _block[0] = static_cast<bt>(MSU_MASK & _block[0]);
131,481✔
378
                }
379
                else {
380
                        quorem<nbits, BlockType, NumberType> result = longdivision(*this, rhs);
1,332,516✔
381
                        *this = result.rem;
1,332,516✔
382
                }
383
                return *this;
1,463,997✔
384
        }
385
        
386
        ///////////////////////////////////////////////////////////////////
387
        ///              logic operators
388

389
        blockbinary& operator|=(const blockbinary& rhs) noexcept {
53,393,620✔
390
                for (unsigned i = 0; i < nrBlocks; ++i) {
239,024,188✔
391
                        _block[i] |= rhs._block[i];
185,630,568✔
392
                }
393
                _block[MSU] &= MSU_MASK; // assert precondition of properly nulled leading non-bits
53,393,620✔
394
                return *this;
53,393,620✔
395
        }
396
        blockbinary& operator&=(const blockbinary& rhs) noexcept {
397
                for (unsigned i = 0; i < nrBlocks; ++i) {
398
                        _block[i] &= rhs._block[i];
399
                }
400
                _block[MSU] &= MSU_MASK; // assert precondition of properly nulled leading non-bits
401
                return *this;
402
        }
403
        blockbinary& operator^=(const blockbinary& rhs) noexcept {
404
                for (unsigned i = 0; i < nrBlocks; ++i) {
405
                        _block[i] ^= rhs._block[i];
406
                }
407
                _block[MSU] &= MSU_MASK; // assert precondition of properly nulled leading non-bits
408
                return *this;
409
        }
410

411
        // shift left operator
412
        blockbinary& operator<<=(int bitsToShift) {
59,182,292✔
413
                if (bitsToShift == 0) return *this;
59,182,292✔
414
                if (bitsToShift < 0) return operator>>=(-bitsToShift);
58,958,334✔
415
                if (bitsToShift > static_cast<int>(nbits)) {
58,958,333✔
416
                        setzero();
2,097,152✔
417
                        return *this;
2,097,152✔
418
                }
419
                if (bitsToShift >= static_cast<int>(bitsInBlock)) {
56,861,181✔
420
                        int blockShift = bitsToShift / static_cast<int>(bitsInBlock);
14,966,066✔
421
                        for (int i = static_cast<int>(MSU); i >= blockShift; --i) {
74,956,581✔
422
                                _block[i] = _block[i - blockShift];
59,990,515✔
423
                        }
424
                        for (int i = blockShift - 1; i >= 0; --i) {
61,837,221✔
425
                                _block[i] = bt(0);
46,871,155✔
426
                        }
427
                        // adjust the shift
428
                        bitsToShift -= static_cast<int>(blockShift * bitsInBlock);
14,966,066✔
429
                        if (bitsToShift == 0) return *this;
14,966,066✔
430
                }
431
                if constexpr (MSU > 0) {
432
                        // construct the mask for the upper bits in the block that needs to move to the higher word
433
                        bt mask = 0xFFFFFFFFFFFFFFFF << (bitsInBlock - bitsToShift);
56,026,938✔
434
                        for (unsigned i = MSU; i > 0; --i) {
228,890,518✔
435
                                _block[i] <<= bitsToShift;
172,863,580✔
436
                                // mix in the bits from the right
437
                                bt bits = bt(mask & _block[i - 1]);
172,863,580✔
438
                                _block[i] |= (bits >> (bitsInBlock - bitsToShift));
172,863,580✔
439
                        }
440
                }
441
                _block[0] <<= bitsToShift;
56,626,406✔
442
                return *this;
56,626,406✔
443
        }
444
        // arithmetic shift right operator
445
        blockbinary& operator>>=(int bitsToShift) {
5,811,507✔
446
                if (bitsToShift == 0) return *this;
5,811,507✔
447
                if (bitsToShift < 0) return operator<<=(-bitsToShift);
5,802,787✔
448
                if (bitsToShift >= static_cast<int>(nbits)) {
5,802,787✔
449
                        setzero();
16✔
450
                        return *this;
16✔
451
                }
452
                bool signext = sign();
5,802,771✔
453
                unsigned blockShift = 0;
5,802,771✔
454
                if (bitsToShift >= static_cast<int>(bitsInBlock)) {
5,802,771✔
455
                        blockShift = bitsToShift / bitsInBlock;
4,128,959✔
456
                        if (MSU >= blockShift) {
4,128,959✔
457
                                // shift by blocks
458
                                for (unsigned i = 0; i <= MSU - blockShift; ++i) {
48,240,729✔
459
                                        _block[i] = _block[i + blockShift];
44,111,770✔
460
                                }
461
                        }
462
                        // adjust the shift
463
                        bitsToShift -= static_cast<int>(blockShift * bitsInBlock);
4,128,959✔
464
                        if (bitsToShift == 0) {
4,128,959✔
465
                                // fix up the leading zeros if we have a negative number
466
                                if (signext) {
60✔
467
                                        // bitsToShift is guaranteed to be less than nbits
468
                                        bitsToShift += static_cast<int>(blockShift * bitsInBlock);
10✔
469
                                        for (unsigned i = nbits - bitsToShift; i < nbits; ++i) {
98✔
470
                                                this->setbit(i);
88✔
471
                                        }
472
                                }
473
                                else {
474
                                        // clean up the blocks we have shifted clean
475
                                        bitsToShift += static_cast<int>(blockShift * bitsInBlock);
50✔
476
                                        for (unsigned i = nbits - bitsToShift; i < nbits; ++i) {
2,930✔
477
                                                this->setbit(i, false);
2,880✔
478
                                        }
479
                                }
480
                                return *this;
60✔
481
                        }
482
                }
483
                if constexpr (MSU > 0) {
484
                        bt mask = ALL_ONES;
5,777,054✔
485
                        mask >>= (bitsInBlock - bitsToShift); // this is a mask for the lower bits in the block that need to move to the lower word
5,777,054✔
486
                        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
53,247,815✔
487
                                _block[i] >>= bitsToShift;
47,470,761✔
488
                                // mix in the bits from the left
489
                                bt bits = bt(mask & _block[i + 1]);
47,470,761✔
490
                                _block[i] |= (bits << (bitsInBlock - bitsToShift));
47,470,761✔
491
                        }
492
                }
493
                _block[MSU] >>= bitsToShift;
5,802,711✔
494

495
                // fix up the leading zeros if we have a negative number
496
                if (signext) {
5,802,711✔
497
                        // bitsToShift is guaranteed to be less than nbits
498
                        bitsToShift += static_cast<int>(blockShift * bitsInBlock);
57,419✔
499
                        for (unsigned i = nbits - bitsToShift; i < nbits; ++i) {
274,244✔
500
                                this->setbit(i);
216,825✔
501
                        }
502
                }
503
                else {
504
                        // clean up the blocks we have shifted clean
505
                        bitsToShift += static_cast<int>(blockShift * bitsInBlock);
5,745,292✔
506
                        for (unsigned i = nbits - bitsToShift; i < nbits; ++i) {
61,211,779✔
507
                                this->setbit(i, false);
55,466,487✔
508
                        }
509
                }
510

511
                // enforce precondition for fast comparison by properly nulling bits that are outside of nbits
512
                _block[MSU] &= MSU_MASK;
5,802,711✔
513
                return *this;
5,802,711✔
514
        }
515

516

517
        ///////////////////////////////////////////////////////////////////
518
        ///                  modifiers
519

520
        constexpr void clear() noexcept {
1,282,884,237✔
521
                for (unsigned i = 0; i < nrBlocks; ++i) {
2,147,483,647✔
522
                        _block[i] = bt(0ull);
1,469,790,563✔
523
                }
524
        }
1,282,884,237✔
525
        constexpr void setzero() noexcept { clear(); }
2,097,168✔
526
        constexpr void set() noexcept { // set all bits to 1
10,812,530✔
527
                if constexpr (nrBlocks > 1) {
528
                        for (unsigned i = 0; i < nrBlocks - 1; ++i) {
11,721,963✔
529
                                _block[i] = ALL_ONES;
8,900,798✔
530
                        }
531
                }
532
                _block[MSU] = ALL_ONES & MSU_MASK;
10,812,530✔
533
        }
10,812,530✔
534
        constexpr void reset() noexcept { clear(); } // set all bits to 0
535
        constexpr void set(unsigned i) noexcept {        setbit(i, true); }
32✔
536
        constexpr void reset(unsigned i) noexcept { setbit(i, false); }
11,596,711✔
537
        constexpr void setbit(unsigned i, bool v = true) noexcept {
2,147,483,647✔
538
                unsigned blockIndex = i / bitsInBlock;
2,147,483,647✔
539
                if (blockIndex < nrBlocks) {
2,147,483,647✔
540
                        bt blockBits = _block[blockIndex];
2,147,483,647✔
541
                        bt null = ~(1ull << (i % bitsInBlock));
2,147,483,647✔
542
                        bt bit = bt(v ? 1 : 0);
2,147,483,647✔
543
                        bt mask = bt(bit << (i % bitsInBlock));
2,147,483,647✔
544
                        _block[blockIndex] = bt((blockBits & null) | mask);
2,147,483,647✔
545
                }
546
                // nop if blockIndex is out of range
547
        }
2,147,483,647✔
548
        constexpr void setbits(uint64_t value) noexcept {
930,535,958✔
549
                if constexpr (1 == nrBlocks) {
550
                        _block[0] = value & storageMask;
816,302,562✔
551
                }
552
                else if constexpr (1 < nrBlocks) {
553
                        for (unsigned i = 0; i < nrBlocks; ++i) {
345,321,524✔
554
                                _block[i] = value & storageMask;
231,088,128✔
555
                                value >>= bitsInBlock;
231,088,128✔
556
                        }
557
                }
558
                _block[MSU] &= MSU_MASK; // enforce precondition for fast comparison by properly nulling bits that are outside of nbits
930,535,958✔
559
        }
930,535,958✔
560
        constexpr void setblock(unsigned b, const bt& blockBits) noexcept {
78,729,795✔
561
                if (b < nrBlocks) _block[b] = blockBits; // nop if b is out of range
78,729,795✔
562
        }        
78,729,795✔
563
        constexpr blockbinary& flip() noexcept { // in-place one's complement
95,991,095✔
564
                for (unsigned i = 0; i < nrBlocks; ++i) {
271,856,203✔
565
                        _block[i] = bt(~_block[i]);
175,865,108✔
566
                }                
567
                _block[MSU] &= MSU_MASK; // assert precondition of properly nulled leading non-bits
95,991,095✔
568
                return *this;
95,991,095✔
569
        }
570
        /// <summary>
571
        /// in-place 2's complement
572
        /// </summary>
573
        /// <returns>2's complement of original</returns>
574
        constexpr blockbinary& twosComplement() noexcept {
53,526,074✔
575
                blockbinary plusOne(1);
53,526,074✔
576
                if constexpr (NumberType == BinaryNumberType::Signed) {
577
                        flip();
53,526,074✔
578
                }
579
                else {
580
                        static_assert(NumberType == BinaryNumberType::Signed, "calling in-place 2's complement on an unsigned blockbinary"); // should this be allowed?
581
                }
582
                return *this += plusOne;
53,526,074✔
583
        }
584

585
        // minimum positive value of the blockbinary configuration
586
        constexpr blockbinary& minpos() noexcept {
587
                // minpos = 0000....00001
588
                clear();
589
                setbit(0, true);
590
                return *this;
591
        }
592
        // maximum positive value of the blockbinary configuration
593
        constexpr blockbinary& maxpos() noexcept {
41✔
594
                if constexpr (NumberType == BinaryNumberType::Signed) {
595
                        // maxpos = 01111....1111
596
                        clear();
41✔
597
                        flip();
41✔
598
                        setbit(nbits - 1, false);
41✔
599
                }
600
                else {
601
                        // maxpos = 11111....1111
602
                        clear();
603
                        flip();
604
                }
605
                return *this;
41✔
606
        }
607
        // zero
608
        constexpr blockbinary& zero() noexcept {
609
                clear();
610
                return *this;
611
        }
612
        // minimum negative value of the blockbinary configuration
613
        constexpr blockbinary& minneg() noexcept {
614
                if constexpr (NumberType == BinaryNumberType::Signed) {
615
                        // minneg = 11111....11111
616
                        clear();
617
                        flip();
618
                }
619
                else {
620
                        // minneg = 00000....00000
621
                        clear();
622
                }
623
                return *this;
624
        }
625
        // maximum negative value of the blockbinary configuration
626
        constexpr blockbinary& maxneg() noexcept {
12✔
627
                if constexpr (NumberType == BinaryNumberType::Signed) {
628
                        // maxneg = 10000....0000
629
                        clear();
12✔
630
                        setbit(nbits - 1);
12✔
631
                }
632
                else {
633
                        // maxneg = 00000....00000
634
                        clear();
635
                }
636
                                
637
                return *this;
12✔
638
        }
639

640
        // selectors
641
        constexpr bool sign() const noexcept { return _block[MSU] & SIGN_BIT_MASK; }
501,353,056✔
642
        constexpr bool ispos() const noexcept { return !sign(); }
44,271,097✔
643
        constexpr bool isneg() const noexcept { return sign(); }
158,574,610✔
644
        constexpr bool iszero() const noexcept {
623,875,737✔
645
                for (unsigned i = 0; i < nrBlocks; ++i) if (_block[i] != 0) return false;
680,031,398✔
646
                return true;
43,070,660✔
647
        }
648
        constexpr bool isodd() const noexcept { return _block[0] & 0x1;        }
649
        constexpr bool iseven() const noexcept { return !isodd(); }
650

651
        constexpr bool all() const noexcept {
493,041,024✔
652
                if constexpr (nrBlocks > 1) for (unsigned i = 0; i < nrBlocks - 1; ++i) if (_block[i] != ALL_ONES) return false;
1,223✔
653
                if (_block[MSU] != MSU_MASK) return false;
493,040,339✔
654
                return true;
33,933,809✔
655
        }
656
        constexpr bool any() const noexcept {
657
                if constexpr (nrBlocks > 1) for (unsigned i = 0; i < nrBlocks - 1; ++i) if (_block[i] || ALL_ONES) return true;
658
                if (_block[MSU] || MSU_MASK) return true;
659
                return false;
660
        }
661
        constexpr bool anyAfter(unsigned bitIndex) const noexcept {  // TODO: optimize for limbs
13,348,469✔
662
                unsigned limit = (bitIndex < nbits) ? bitIndex : nbits;
13,348,469✔
663
                for (unsigned i = 0; i < limit; ++i) if (test(i)) return true;
21,477,083✔
664
                return false;
7,656,216✔
665
        }
666

667
        constexpr bool none() const noexcept {
774,146,642✔
668
                if constexpr (nrBlocks > 1) for (unsigned i = 0; i < nrBlocks - 1; ++i) if (_block[i] != 0) return false;
126,265,413✔
669
                if (_block[MSU] & MSU_MASK) return false;
743,655,353✔
670
                return true;
14,065,487✔
671
        }
672
        constexpr unsigned count() const noexcept { // TODO: optimize for limbs
673
                unsigned nrOnes = 0;
674
                for (unsigned i = 0; i < nbits; ++i) {
675
                        if (test(i)) ++nrOnes;
676
                }
677
                return nrOnes;
678
        }
679
        constexpr bool test(unsigned bitIndex) const noexcept { return at(bitIndex); }
2,147,483,647✔
680
        constexpr bool at(unsigned bitIndex) const noexcept {
2,147,483,647✔
681
                if (bitIndex >= nbits) return false; // fail silently as no-op
2,147,483,647✔
682
                unsigned blockIndex = bitIndex / bitsInBlock;
2,147,483,647✔
683
                bt limb = _block[blockIndex];
2,147,483,647✔
684
                bt mask = bt(1ull << (bitIndex % bitsInBlock));
2,147,483,647✔
685
                return (limb & mask);
2,147,483,647✔
686
        }
687
        constexpr uint8_t nibble(unsigned n) const noexcept {
357✔
688
                uint8_t retval{ 0 };
357✔
689
                if (n < (1 + ((nbits - 1) >> 2))) {
357✔
690
                        bt word = _block[(n * 4) / bitsInBlock];
357✔
691
                        unsigned nibbleIndexInWord = n % (bitsInBlock >> 2);
357✔
692
                        bt mask = static_cast<bt>(0x0Fu << (nibbleIndexInWord*4));
357✔
693
                        bt nibblebits = static_cast<bt>(mask & word);
357✔
694
                        retval = static_cast<uint8_t>(nibblebits >> static_cast<bt>(nibbleIndexInWord*4));
357✔
695
                }
696
                else { // nop when nibble index out of bounds
697
                        retval = 0;
×
698
                }
699
                return retval;
357✔
700
        }
701
        constexpr bt block(unsigned b) const noexcept {
638,203,440✔
702
                if (b < nrBlocks) return _block[b]; 
638,203,440✔
703
                return bt(0); // return 0 when block index out of bounds
×
704
        }
705

706
        // copy a value over from one blockbinary to this blockbinary
707
        // blockbinary is a 2's complement encoding, so we sign-extend by default
708
        template<unsigned srcbits>
709
        blockbinary<nbits, bt, NumberType>& assign(const blockbinary<srcbits, bt, NumberType>& rhs) {
185,704,500✔
710
                clear();
185,704,500✔
711
                // since bt is the same, we can simply copy the blocks in
712
                unsigned minNrBlocks = (this->nrBlocks < rhs.nrBlocks) ? this->nrBlocks : rhs.nrBlocks;
185,704,500✔
713
                for (unsigned i = 0; i < minNrBlocks; ++i) {
446,234,051✔
714
                        _block[i] = rhs.block(i);
260,529,551✔
715
                }
716
                if constexpr (nbits > srcbits) { // check if we need to sign extend
717
                        if (rhs.sign()) {
129,058,057✔
718
                                for (unsigned i = srcbits; i < nbits; ++i) { // TODO: replace bit-oriented sequence with block
157,885,465✔
719
                                        setbit(i);
85,015,902✔
720
                                }
721
                        }
722
                }
723
                // enforce precondition for fast comparison by properly nulling bits that are outside of nbits
724
                _block[MSU] &= MSU_MASK;
185,704,500✔
725
                return *this;
185,704,500✔
726
        }
727

728
        // copy a value over from one blockbinary to this without sign-extending the value
729
        // blockbinary is a 2's complement encoding, so we sign-extend by default
730
        // for fraction/significent encodings, we need to turn off sign-extending.
731
        template<unsigned srcbits>
732
        blockbinary<nbits, bt, NumberType>& assignWithoutSignExtend(const blockbinary<srcbits, bt, NumberType>& rhs) {
33,284✔
733
                clear();
33,284✔
734
                // since bt is the same, we can simply copy the blocks in
735
                unsigned minNrBlocks = (this->nrBlocks < rhs.nrBlocks) ? this->nrBlocks : rhs.nrBlocks;
33,284✔
736
                for (unsigned i = 0; i < minNrBlocks; ++i) {
66,568✔
737
                        _block[i] = rhs.block(i);
33,284✔
738
                }
739
                // enforce precondition for fast comparison by properly nulling bits that are outside of nbits
740
                _block[MSU] &= MSU_MASK;
33,284✔
741
                return *this;
33,284✔
742
        }
743

744
        // return the position of the most significant bit, -1 if v == 0
745
        int msb() const noexcept {
1,335,392✔
746
                for (int i = int(MSU); i >= 0; --i) {
2,128,805✔
747
                        if (_block[i] != 0) {
2,128,805✔
748
                                bt mask = (bt(1u) << (bitsInBlock-1));
1,335,392✔
749
                                for (int j = bitsInBlock - 1; j >= 0; --j) {
5,859,341✔
750
                                        if (_block[i] & mask) {
5,859,341✔
751
                                                return i * static_cast<int>(bitsInBlock) + j;
1,335,392✔
752
                                        }
753
                                        mask >>= 1;
4,523,949✔
754
                                }
755
                        }
756
                }
757
                return -1; // no significant bit found, all bits are zero
×
758
        }
759
        // conversion to native types
760
        int64_t to_sll() const {
138,754,708✔
761
                constexpr unsigned sizeoflonglong = 8 * sizeof(long long);
138,754,708✔
762
                int64_t ll{ 0 };
138,754,708✔
763
                int64_t mask{ 1 };
138,754,708✔
764
                unsigned upper = (nbits < sizeoflonglong ? nbits : sizeoflonglong);
138,754,708✔
765
                for (unsigned i = 0; i < upper; ++i) {
1,775,094,864✔
766
                        ll |= at(i) ? mask : 0;
1,636,340,156✔
767
                        mask <<= 1;
1,636,340,156✔
768
                }
769
                if (sign() && upper < sizeoflonglong) { // sign extend
138,754,708✔
770
                        for (unsigned i = upper; i < sizeoflonglong; ++i) {
2,147,483,647✔
771
                                ll |= mask;
2,147,483,647✔
772
                                mask <<= 1;
2,147,483,647✔
773
                        }
774
                }
775
                return ll;
138,754,708✔
776
        }
777
        uint64_t to_ull() const {
127,499,512✔
778
                uint64_t ull{ 0 };
127,499,512✔
779
                uint64_t mask{ 1 };
127,499,512✔
780
                uint32_t msb = nbits < 64 ? nbits : 64;
127,499,512✔
781
                for (uint32_t i = 0; i < msb; ++i) {
730,612,919✔
782
                        ull |= at(i) ? mask : 0;
603,113,407✔
783
                        mask <<= 1;
603,113,407✔
784
                }
785
                return ull;
127,499,512✔
786
        }
787
        template<typename Real,
788
                typename = typename std::enable_if< std::is_floating_point<Real>::value, Real >::type>
789
        Real to_native() const {
132,181✔
790
                blockbinary tmp(*this);
132,181✔
791
                if (isneg()) tmp.twosComplement();
132,181✔
792
                Real v{ 0.0 }, base{ 1.0 };
132,181✔
793
                for (unsigned i = 0; i < nbits; ++i) {
2,251,637✔
794
                        if (tmp.test(i)) v += base;
2,119,456✔
795
                        base *= 2.0;
2,119,456✔
796
                }
797
                return (isneg() ? -v : v);
132,181✔
798
        }
799
        // determine the rounding mode: result needs to be rounded up if true
800
        bool roundingMode(unsigned targetLsb) const {
126,930✔
801
                bool lsb = at(targetLsb);
126,930✔
802
                bool guard = (targetLsb == 0 ? false : at(targetLsb - 1));
126,930✔
803
                bool round = (targetLsb > 1 ? at(targetLsb - 2) : false);
126,930✔
804
                bool sticky =(targetLsb < 3 ? false : any(targetLsb - 3));
126,930✔
805
                bool tie = guard && !round && !sticky;
126,930✔
806
                return (lsb && tie) || (guard && !tie);
126,930✔
807
        }
808
        bool any(unsigned msb) const {
100,809✔
809
                msb = (msb > nbits - 1 ? nbits - 1 : msb);
100,809✔
810
                unsigned topBlock = msb / bitsInBlock;
100,809✔
811
                bt mask = bt(ALL_ONES >> (bitsInBlock - 1 - (msb % bitsInBlock)));
100,809✔
812
                for (unsigned i = 0; i < topBlock; ++i) {
101,122✔
813
                        if (_block[i] > 0) return true;
413✔
814
                }
815
                // process the partial block
816
                if (_block[topBlock] & mask) return true;
100,709✔
817
                return false;
48,672✔
818
        }
819

820
protected:
821
        // HELPER methods
822
        // none
823

824
private:
825
        bt _block[nrBlocks];
826

827
        //////////////////////////////////////////////////////////////////////////////
828
        // friend functions
829

830
        // integer - integer logic comparisons
831
        template<unsigned N, typename B, BinaryNumberType T>
832
        friend bool operator==(const blockbinary<N, B, T>& lhs, const blockbinary<N, B, T>& rhs);
833
        template<unsigned N, typename B, BinaryNumberType T>
834
        friend bool operator!=(const blockbinary<N, B, T>& lhs, const blockbinary<N, B, T>& rhs);
835
        // the other logic operators are defined in terms of arithmetic terms
836

837
        template<unsigned N, typename B, BinaryNumberType T>
838
        friend std::ostream& operator<<(std::ostream& ostr, const blockbinary<N, B, T>& v);
839
};
840

841
// Generate a type tag for blockbinary
842
template<unsigned N, typename B, BinaryNumberType T>
843
std::string type_tag(const blockbinary<N, B, T>& = {}) {
×
844
        std::stringstream str;
×
845
        str << "blockbinary<"
846
                << std::setw(4) << N << ", "
×
847
                << typeid(B).name() << ", "
848
                << typeid(T).name() << '>';
×
849
        return str.str();
×
850
}
×
851

852
//////////////////////////////////////////////////////////////////////////////////
853
// logic operators
854

855
template<unsigned N, typename B, BinaryNumberType T>
856
bool operator==(const blockbinary<N, B, T>& lhs, const blockbinary<N, B, T>& rhs) {
231,529,261✔
857
        for (unsigned i = 0; i < lhs.nrBlocks; ++i) {
489,171,792✔
858
                if (lhs._block[i] != rhs._block[i]) {
307,676,981✔
859
                        return false;
50,034,450✔
860
                }
861
        }
862
        return true;
181,494,811✔
863
}
864
template<unsigned N, typename B, BinaryNumberType T>
865
bool operator!=(const blockbinary<N, B, T>& lhs, const blockbinary<N, B, T>& rhs) {
174,678,805✔
866
        return !operator==(lhs, rhs);
174,678,805✔
867
}
868
template<unsigned N, typename B, BinaryNumberType T>
869
bool operator<(const blockbinary<N, B, T>& lhs, const blockbinary<N, B, T>& rhs) {
30,696,840✔
870
        if (lhs.ispos() && rhs.isneg()) return false; // need to filter out possible overflow conditions
30,696,840✔
871
        if (lhs.isneg() && rhs.ispos()) return true;  // need to filter out possible underflow conditions
24,115,179✔
872
        if (lhs == rhs) return false; // so the maxneg logic works
17,328,965✔
873
        blockbinary<N, B, T> mneg; maxneg<N, B>(mneg);
17,195,559✔
874
        if (rhs == mneg) return false; // special case: nothing is smaller than maximum negative
17,195,559✔
875
        blockbinary<N, B, T> diff = lhs - rhs;
17,187,356✔
876
        return diff.isneg();
17,187,356✔
877
}
878
template<unsigned N, typename B, BinaryNumberType T>
879
bool operator<=(const blockbinary<N, B, T>& lhs, const blockbinary<N, B, T>& rhs) {
13,514,353✔
880
        return (lhs < rhs || lhs == rhs);
13,514,353✔
881
}
882
template<unsigned N, typename B, BinaryNumberType T>
883
bool operator>(const blockbinary<N, B, T>& lhs, const blockbinary<N, B, T>& rhs) {
90,263✔
884
        return !(lhs <= rhs);
90,263✔
885
}
886
template<unsigned N, typename B, BinaryNumberType T>
887
bool operator>=(const blockbinary<N, B, T>& lhs, const blockbinary<N, B, T>& rhs) {
12,087,200✔
888
        return !(lhs < rhs);
12,087,200✔
889
}
890
///////////////////////////////////////////////////////////////////////////////
891
// binary operators
892

893
template<unsigned N, typename B, BinaryNumberType T>
894
blockbinary<N, B, T> operator+(const blockbinary<N, B, T>& a, const blockbinary<N, B, T>& b) {
100,664,071✔
895
        blockbinary<N, B, T> c(a);
100,664,071✔
896
        return c += b;
100,664,071✔
897
}
898
template<unsigned N, typename B, BinaryNumberType T >
899
blockbinary<N, B, T> operator-(const blockbinary<N, B, T>& a, const blockbinary<N, B, T>& b) {
17,385,762✔
900
        blockbinary<N, B, T> c(a);
17,385,762✔
901
        return c -= b;
33,990,581✔
902
}
903
template<unsigned N, typename B, BinaryNumberType T>
904
blockbinary<N, B, T> operator*(const blockbinary<N, B, T>& a, const blockbinary<N, B, T>& b) {
72,289,039✔
905
        blockbinary<N, B, T> c(a);
72,289,039✔
906
        return c *= b;
97,258,264✔
907
}
908
template<unsigned N, typename B, BinaryNumberType T>
909
blockbinary<N, B, T> operator/(const blockbinary<N, B, T>& a, const blockbinary<N, B, T>& b) {
67,381✔
910
        blockbinary<N, B, T> c(a);
67,381✔
911
        return c /= b;
69,061✔
912
}
913
template<unsigned N, typename B, BinaryNumberType T>
914
blockbinary<N, B, T> operator%(const blockbinary<N, B, T>& a, const blockbinary<N, B, T>& b) {
1,464,257✔
915
        blockbinary<N, B, T> c(a);
1,464,257✔
916
        return c %= b;
2,796,773✔
917
}
918

919
template<unsigned N, typename B, BinaryNumberType T>
920
blockbinary<N, B, T> operator<<(const blockbinary<N, B, T>& a, long b) {
752✔
921
        blockbinary<N, B, T> c(a);
752✔
922
        return c <<= b;
1,504✔
923
}
924
template<unsigned N, typename B, BinaryNumberType T>
925
blockbinary<N, B, T> operator>>(const blockbinary<N, B, T>& a, long b) {
168✔
926
        blockbinary<N, B, T> c(a);
168✔
927
        return c >>= b;
336✔
928
}
929

930
// divide a by b and return both quotient and remainder
931
template<unsigned N, typename B, BinaryNumberType T>
932
quorem<N, B, T> longdivision(const blockbinary<N, B, T>& dividend, const blockbinary<N, B, T>& divisor) {
1,334,230✔
933
        static_assert(T == BinaryNumberType::Signed, "longdivision requires signed blockbinary types");
934
        using BlockBinary = blockbinary<N + 1, B, T>;
935
        quorem<N, B, T> result = { 0, 0, 0 };
1,334,230✔
936
        if (divisor.iszero()) {
1,334,230✔
937
                result.exceptionId = 1; // division by zero
1,808✔
938
                return result;
1,808✔
939
        }
940
        // generate the absolute values to do long division 
941
        // 2's complement special case -max requires an signed int that is 1 bit bigger to represent abs()
942
        bool a_sign = dividend.sign();
1,332,422✔
943
        bool b_sign = divisor.sign();
1,332,422✔
944
        bool result_negative = (a_sign ^ b_sign);
1,332,422✔
945
        // normalize both arguments to positive, which requires expansion by 1-bit to deal with maxneg
946
        BlockBinary a(dividend);
1,332,422✔
947
        BlockBinary b(divisor);
1,332,422✔
948
        if (a_sign) a.twosComplement();
1,332,422✔
949
        if (b_sign) b.twosComplement();
1,332,422✔
950

951
        if (a < b) { // optimization for integer numbers
1,332,422✔
952
                result.rem = dividend; // a % b = a when a / b = 0
664,730✔
953
                return result;         // a / b = 0 when b > a
664,730✔
954
        }
955
        // initialize the long division
956
        BlockBinary accumulator = a;
667,692✔
957
        // prepare the subtractand
958
        BlockBinary subtractand = b;
667,692✔
959
        int msb_b = b.msb();
667,692✔
960
        int msb_a = a.msb();
667,692✔
961
        int shift = msb_a - msb_b;
667,692✔
962
        subtractand <<= shift;
667,692✔
963
        // long division
964
        for (int i = shift; i >= 0; --i) {
2,223,030✔
965
                if (subtractand <= accumulator) {
1,555,338✔
966
                        accumulator -= subtractand;
929,649✔
967
                        result.quo.setbit(static_cast<unsigned>(i));
929,649✔
968
                }
969
                else {
970
                        result.quo.setbit(static_cast<unsigned>(i), false);
625,689✔
971
                }
972
                subtractand >>= 1;
1,555,338✔
973
        }
974
        if (result_negative) {  // take 2's complement
667,692✔
975
                result.quo.flip();
333,194✔
976
                result.quo += 1;
333,194✔
977
        }
978
        if (a_sign) {
667,692✔
979
                result.rem = -accumulator;
334,075✔
980
        }
981
        else {
982
                result.rem = accumulator;
333,617✔
983
        }
984
        return result;
667,692✔
985
}
986

987
///////////////////////////////////////////////////////////////////////////////
988
// specialty binary operators
989

990
// unrounded addition, returns a blockbinary that is of size nbits+1
991
template<unsigned N, typename B, BinaryNumberType T>
992
blockbinary<N + 1, B, T> uradd(const blockbinary<N, B, T>& a, const blockbinary<N, B, T>& b) {
3,400,060✔
993
        blockbinary<N + 1, B, T> result(a);
3,400,060✔
994
        return result += blockbinary<N + 1, B, T>(b);
3,400,060✔
995
}
996

997
// unrounded subtraction, returns a blockbinary that is of size nbits+1
998
template<unsigned N, typename B, BinaryNumberType T>
999
blockbinary<N + 1, B, T> ursub(const blockbinary<N, B, T>& a, const blockbinary<N, B, T>& b) {
8,591,552✔
1000
        blockbinary<N + 1, B, T> result(a);
8,591,552✔
1001
        return result -= blockbinary<N + 1, B, T>(b);
8,591,552✔
1002
}
1003

1004
#define TRACE_URMUL 0
1005
// unrounded multiplication, returns a blockbinary that is of size 2*nbits
1006
// using brute-force sign-extending of operands to yield correct sign-extended result for 2*nbits 2's complement.
1007
template<unsigned N, typename B, BinaryNumberType T>
1008
blockbinary<2*N, B, T> urmul(const blockbinary<N, B, T>& a, const blockbinary<N, B, T>& b) {
6✔
1009
        using BlockBinary = blockbinary<2 * N, B, T>;
1010
        BlockBinary result(0);
6✔
1011
        if (a.iszero() || b.iszero()) return result;
6✔
1012

1013
        // compute the result
1014
        BlockBinary signextended_a(a);
6✔
1015
        BlockBinary multiplicant(b);
6✔
1016
#if TRACE_URMUL
1017
        std::cout << "    " << to_binary(a) << " * " << to_binary(b) << std::endl;
1018
        std::cout << std::setw(3) << 0 << ' ' << to_binary(multiplicant) << ' ' << to_binary(result) << std::endl;
1019
#endif
1020
        for (unsigned i = 0; i < 2*N; ++i) {
102✔
1021
                if (signextended_a.at(i)) {
96✔
1022
                        result += multiplicant;
9✔
1023
                }
1024
                multiplicant <<= 1;
96✔
1025
#if TRACE_URMUL
1026
                std::cout << std::setw(3) << i << ' ' << to_binary(multiplicant) << ' ' << to_binary(result) << std::endl;
1027
#endif
1028

1029
        }
1030
#if TRACE_URMUL
1031
        std::cout << "fnl " << to_binary(result) << std::endl;
1032
#endif
1033
        //blockbinary<2 * nbits, bt> clipped(result);
1034
        // since we used operator+=, which enforces the nulling of leading bits
1035
        // we don't need to null here
1036
        return result;
6✔
1037
}
1038

1039
// unrounded multiplication, returns a blockbinary that is of size 2*nbits
1040
// using nbits modulo arithmetic with final sign
1041
template<unsigned N, typename B, BinaryNumberType T>
1042
blockbinary<2 * N, B, T> urmul2(const blockbinary<N, B, T>& a, const blockbinary<N, B, T>& b) {
125,583✔
1043
        blockbinary<2 * N, B, T> result(0);
125,583✔
1044
        if (a.iszero() || b.iszero()) return result;
125,583✔
1045

1046
        // compute the result
1047
        bool result_sign = a.sign() ^ b.sign();
122,984✔
1048
        // normalize both arguments to positive in new size
1049
        blockbinary<N + 1, B, T> a_new(a); // TODO optimize: now create a, create _a.bb, copy, destroy _a.bb_copy
122,984✔
1050
        blockbinary<N + 1, B, T> b_new(b);
122,984✔
1051
        if (a.sign()) a_new.twosComplement();
122,984✔
1052
        if (b.sign()) b_new.twosComplement();
122,984✔
1053
        blockbinary<2*N, B, T> multiplicant(b_new);
122,984✔
1054

1055
#if TRACE_URMUL
1056
        std::cout << "    " << a_new << " * " << b_new << std::endl;
1057
        std::cout << std::setw(3) << 0 << ' ' << multiplicant << ' ' << result << std::endl;
1058
#endif
1059
        for (unsigned i = 0; i < (N+1); ++i) {
1,111,523✔
1060
                if (a_new.at(i)) {
988,539✔
1061
                        result += multiplicant;  // if multiplicant is not the same size as result, the assignment will get sign-extended if the MSB is true, this is not correct because we are assuming unsigned binaries in this loop
374,670✔
1062
                }
1063
                multiplicant <<= 1;
988,539✔
1064
#if TRACE_URMUL
1065
                std::cout << std::setw(3) << i << ' ' << multiplicant << ' ' << result << std::endl;
1066
#endif
1067
        }
1068
        if (result_sign) result.twosComplement();
122,984✔
1069
#if TRACE_URMUL
1070
        std::cout << "fnl " << result << std::endl;
1071
#endif
1072
        return result;
122,984✔
1073
}
1074

1075
#define TRACE_DIV 0
1076
// unrounded division, returns a blockbinary that is of size 2*nbits
1077
template<unsigned nbits, unsigned roundingBits, typename B, BinaryNumberType T>
1078
blockbinary<2 * nbits + roundingBits, B, T> urdiv(const blockbinary<nbits, B, T>& a, const blockbinary<nbits, B, T>& b) {
1079
        if (b.iszero()) {
1080
                // division by zero
1081
                throw "urdiv divide by zero";
1082
        }
1083
        // generate the absolute values to do long division 
1084
        // 2's complement special case -max requires an signed int that is 1 bit bigger to represent abs()
1085
        bool a_sign = a.sign();
1086
        bool b_sign = b.sign();
1087
        bool result_negative = (a_sign ^ b_sign);
1088

1089
        // normalize both arguments to positive, which requires expansion by 1-bit to deal with maxneg
1090
        blockbinary<nbits + 1, B, T> a_new(a); // TODO optimize: now create a, create _a.bb, copy, destroy _a.bb_copy
1091
        blockbinary<nbits + 1, B, T> b_new(b);
1092
#if TRACE_DIV
1093
        std::cout << "a " << to_binary(a_new) << '\n';
1094
        std::cout << "b " << to_binary(b_new) << '\n';
1095
#endif
1096
        if (a_sign) a_new.twosComplement();
1097
        if (b_sign) b_new.twosComplement();
1098
#if TRACE_DIV
1099
        std::cout << "a " << to_binary(a_new) << '\n';
1100
        std::cout << "b " << to_binary(b_new) << '\n';
1101
#endif
1102

1103
        // initialize the long division
1104
        blockbinary<2 * nbits + roundingBits + 1, B, T> decimator(a_new);
1105
        blockbinary<2 * nbits + roundingBits + 1, B, T> subtractand(b_new); // prepare the subtractand
1106
        blockbinary<2 * nbits + roundingBits + 1, B, T> result;
1107

1108
        constexpr unsigned msp = nbits + roundingBits; // msp = most significant position
1109
        decimator <<= msp; // scale the decimator to the largest possible positive value
1110

1111
        int msb_b = subtractand.msb();
1112
        int msb_a = decimator.msb();
1113
        int shift = msb_a - msb_b;
1114
        subtractand <<= shift;
1115
        int offset = msb_a - static_cast<int>(msp);  // msb of the result
1116
        int scale  = shift - static_cast<int>(msp);  // scale of the result quotient
1117

1118
#if TRACE_DIV
1119
        std::cout << "  " << to_binary(decimator, true)   << " msp  : " << msp << '\n';
1120
        std::cout << "- " << to_binary(subtractand, true) << " shift: " << shift << '\n';
1121
#endif
1122
        // long division
1123
        for (int i = msb_a; i >= 0; --i) {
1124

1125
                if (subtractand <= decimator) {
1126
                        decimator -= subtractand;
1127
                        result.setbit(static_cast<unsigned>(i));
1128
                }
1129
                else {
1130
                        result.setbit(static_cast<unsigned>(i), false);
1131
                }
1132
                subtractand >>= 1;
1133

1134
#if TRACE_DIV
1135
                std::cout << "  " << to_binary(decimator, true) << "  current quotient: " << to_binary(result, true) << '\n';
1136
                std::cout << "- " << to_binary(subtractand, true) << '\n';
1137
#endif
1138
        }
1139
        result <<= (scale - offset);
1140
#if TRACE_DIV
1141
        std::cout << "  " << "scaled result: " << to_binary(result, true) << " scale : " << scale << " offset : " << offset << '\n';
1142
#endif
1143
        if (result_negative) result.twosComplement();
1144
        return result;
1145
}
1146

1147
//////////////////////////////////////////////////////////////////////////////
1148
// conversions to string representations
1149

1150
// create a binary representation of the storage
1151
template<unsigned nbits, typename BlockType, BinaryNumberType NumberType>
1152
std::string to_binary(const blockbinary<nbits, BlockType, NumberType>& number, bool nibbleMarker = false) {
1,104✔
1153
        std::stringstream s;
1,104✔
1154
        s << "0b";
1,104✔
1155
        for (unsigned i = 0; i < nbits; ++i) {
16,424✔
1156
                unsigned bitIndex = nbits - 1ull - i;
15,320✔
1157
                s << (number.at(bitIndex) ? '1' : '0');
15,320✔
1158
                if (bitIndex > 0 && (bitIndex % 4) == 0 && nibbleMarker) s << '\'';
15,320✔
1159
        }
1160
        return s.str();
2,208✔
1161
}
1,104✔
1162

1163
// local helper to display the contents of a byte array
1164
template<unsigned nbits, typename BlockType, BinaryNumberType NumberType>
1165
std::string to_hex(const blockbinary<nbits, BlockType, NumberType>& number, bool nibbleMarker = true) {
52✔
1166
        static constexpr unsigned bitsInByte = 8;
1167
        static constexpr unsigned bitsInBlock = sizeof(BlockType) * bitsInByte;
1168
        char hexChar[16] = {
52✔
1169
                '0', '1', '2', '3', '4', '5', '6', '7',
1170
                '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
1171
        };
1172
        std::stringstream ss;
52✔
1173
        ss << "0x" << std::hex;
52✔
1174
        int nrNibbles = int(1 + ((nbits - 1) >> 2));
52✔
1175
        for (int n = nrNibbles - 1; n >= 0; --n) {
409✔
1176
                uint8_t nibble = number.nibble(static_cast<unsigned>(n));
357✔
1177
                ss << hexChar[nibble];
357✔
1178
                if (nibbleMarker && n > 0 && ((n * 4ll) % bitsInBlock) == 0) ss << '\'';
357✔
1179
        }
1180
        return ss.str();
104✔
1181
}
52✔
1182

1183
// decimal string conversion
1184
template<unsigned nbits, typename BlockType, BinaryNumberType NumberType>
1185
std::string to_decimal(const blockbinary<nbits, BlockType, NumberType>& number) {
1,727✔
1186
        if (number.iszero()) return "0";
2,033✔
1187

1188
        std::string result;
1,574✔
1189
        blockbinary<nbits, BlockType, NumberType> dividend(number);
1,574✔
1190
        bool isNegative = false;
1,574✔
1191

1192
        // Handle negative numbers for signed types
1193
        if constexpr (NumberType == BinaryNumberType::Signed) {
1194
                if (dividend.isneg()) {
1,572✔
1195
                        isNegative = true;
476✔
1196
                        dividend.twosComplement(); // Convert to positive
476✔
1197
                }
1198
        }
1199

1200
        // Repeatedly divide by 10 and collect remainders
1201
        blockbinary<nbits, BlockType, NumberType> ten(10);
1,574✔
1202
        while (!dividend.iszero()) {
4,313✔
1203
                if constexpr (nbits <= 64) {
1204
                        // For smaller sizes, use native division to avoid complexity
1205
                        uint64_t temp = dividend.to_ull();
2,721✔
1206
                        uint64_t remainder = temp % 10;
2,721✔
1207
                        result = char('0' + remainder) + result;
2,721✔
1208
                        dividend = temp / 10;
2,721✔
1209
                } else {
1210
                        // For larger sizes, use blockbinary division operators
1211
                        blockbinary<nbits, BlockType, NumberType> remainder = dividend % ten;
18✔
1212
                        uint64_t digit = remainder.to_ull();
18✔
1213
                        result = char('0' + digit) + result;
18✔
1214
                        dividend /= ten;
18✔
1215
                }
1216
        }
1217

1218
        if (isNegative) {
1,574✔
1219
                result = "-" + result;
476✔
1220
        }
1221

1222
        return result;
1,574✔
1223
}
1,574✔
1224

1225
// ostream operator
1226
template<unsigned nbits, typename BlockType, BinaryNumberType NumberType>
1227
std::ostream& operator<<(std::ostream& ostr, const blockbinary<nbits, BlockType, NumberType>& number) {
1,727✔
1228
        ostr << to_decimal(number);
1,727✔
1229
        return ostr;
1,727✔
1230
}
1231

1232

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

© 2026 Coveralls, Inc