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

stillwater-sc / universal / 30404978394

28 Jul 2026 10:33PM UTC coverage: 85.212% (-0.3%) from 85.499%
30404978394

push

github

web-flow
refactor: archive legacy posit1, posito, valid; migrate consumers to modern posit (#1244)

posit1 and posito are the original posit designs; valid (unum type III interval) is
built on posit1. They forward-declare sw::universal::posit (2-arg) and quire
(3-unsigned) INCOMPATIBLY with the modern posit<nbits,es,bt> and quire<NumberType,
capacity,LimbType>, so they cannot coexist with the modern quire in a translation unit
(posit1 + modern posit already gave 1192 errors). This blocked #1201 (cfloat/lns
aggregators pulling the modern quire via fdp.hpp). Archive the three legacy designs and
migrate consumers to the modern posit.

Archived to ./archive (git mv, preserved, out of the build):
- number/posit1, number/posito, number/valid + their traits + adapters/adapt_integer_and_posit.hpp
- test trees static/tapered/{posit1,posito}, static/range/valid
- the entire posit1-based C API (c_api/)
- posit1-internals-specific consumers: education/number/posit/{components,regimes,exponents,fractions},
  education/{tables/posits,ranges/posit}, static/conversions/{to_posit,to_integer,adapt_integer_and_posit,rounding},
  applications/precision/numeric/{posit_list,posit_properties,traits}, valid + posit benchmarks,
  numeric/{fpbench,utils,functions} valid/adapter users, playground/{efunc_posits,efunc_valids,serialization}

Migrated to modern posit (include swap): number_systems.hpp, tools/float2posit, most
education/number/posit value examples, several applications, playground/{gismo_test,skeleton,type_test}.
takum/conversion/assignment dropped its (archived) posito include.

#1201 landed: cfloat.hpp and lns.hpp now include their fdp.hpp (matching posit.hpp), so
quire_mul/fdp are discoverable from the aggregator header. New regression
static/quire/api/aggregator_fdp.cpp pins it (posit+cfloat+lns coexist, exact dot == 8).

CMake: removed UNIVERSAL_BUILD_NUMBER_POSITOS/VALIDS options + cascades, the posit1/posito/
valid/c_api add_subdirectory lines, the benchmark valid glo... (continued)

41518 of 48723 relevant lines covered (85.21%)

7164664.32 hits per line

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

87.53
/include/sw/universal/internal/value/value.hpp
1
#pragma once
2
// value.hpp: definition of a (sign, scale, significant) representation of an approximation to a real value
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 <cassert>
9
#include <iostream>
10
#include <iomanip>
11
#include <limits>
12
#include <tuple>
13
#include <algorithm> // std::max
14

15
#include <universal/common/exceptions.hpp>
16
#include <universal/number/support/decimal.hpp>
17
#include <universal/utility/find_msb.hpp>
18
#include <universal/native/ieee754.hpp>
19
#include <universal/native/nonconstexpr/extract_fp_components.hpp>
20
#include <universal/internal/bitblock/bitblock.hpp>
21

22
namespace sw { namespace universal { namespace internal {
23

24
struct value_internal_exception : public universal_internal_exception {
25
        value_internal_exception(const std::string& error)
×
26
                : universal_internal_exception(std::string("value internal exception: ") + error) {};
×
27
};
28

29
struct value_shift_too_large : public value_internal_exception {
30
        value_shift_too_large() : value_internal_exception("shift value too large") {}
×
31
};
32

33
using namespace sw::universal;
34

35
// Forward definitions
36
template<unsigned fbits> class value;
37
template<unsigned fbits> value<fbits> abs(const value<fbits>& v);
38

39
#ifdef VALUE_TRACE_CONVERSION
40
constexpr bool _trace_value_conversion = true;
41
#else
42
constexpr bool _trace_value_conversion = false;
43
#endif
44

45
#ifdef VALUE_TRACE_ADD
46
constexpr bool _trace_value_add = true;
47
#else
48
constexpr bool _trace_value_add = false;
49
#endif
50

51
#ifdef VALUE_TRACE_SUB
52
constexpr bool _trace_value_sub = true;
53
#else
54
constexpr bool _trace_value_sub = false;
55
#endif
56

57
#ifdef VALUE_TRACE_MUL
58
constexpr bool _trace_value_mul = true;
59
#else
60
constexpr bool _trace_value_mul = false;
61
#endif
62

63
#ifdef VALUE_TRACE_DIV
64
constexpr bool _trace_value_div = true;
65
#else
66
constexpr bool _trace_value_div = false;
67
#endif
68

69
// template class representing a value in scientific notation, using a template parameter to define the number of fraction bits
70
template<unsigned fbits>
71
class value {
72
public:
73
        static constexpr unsigned fhbits = fbits + 1;    // number of fraction bits including the hidden bit
74
        constexpr value() 
100,045✔
75
          : _sign{false}, _scale{0}, _nrOfBits{fbits}, _fraction{}, _inf{false}, 
100,045✔
76
            _zero{true}, _nan{false} {}
100,045✔
77
        constexpr value(bool sign, int scale, const internal::bitblock<fbits>& fraction_without_hidden_bit, 
1,146,020✔
78
                        bool zero = true, bool inf = false) 
79
          : _sign{sign}, _scale{scale}, _nrOfBits{fbits}, _fraction{fraction_without_hidden_bit}, 
1,146,020✔
80
            _inf{inf}, _zero{zero}, _nan{false} {}
1,146,020✔
81

82

83
        // decorated constructors
84
        constexpr value(const value& initial_value)       { *this = initial_value; }
99,999✔
85
        constexpr value(signed char initial_value)        { *this = initial_value; }
86
        constexpr value(short initial_value)              { *this = initial_value; }
87
        constexpr value(int initial_value)                { *this = initial_value; }
1,599,995✔
88
        constexpr value(long initial_value)               { *this = initial_value; }
89
        constexpr value(long long initial_value)          { *this = initial_value; }
90
        constexpr value(char initial_value)               { *this = initial_value; }
91
        constexpr value(unsigned short initial_value)     { *this = initial_value; }
92
        constexpr value(unsigned int initial_value)       { *this = initial_value; }
93
        constexpr value(unsigned long initial_value)      { *this = initial_value; }
94
        constexpr value(unsigned long long initial_value) { *this = initial_value; }
95
        value(float initial_value)              { *this = initial_value; }
1✔
96
        value(double initial_value) : value{}   { *this = initial_value; }
100,045✔
97
        value(long double initial_value)        { *this = initial_value; }
1✔
98

99
        // assignment operators
100
        constexpr value& operator=(const value& rhs) {
400,001✔
101
                _sign          = rhs._sign;
400,001✔
102
                _scale          = rhs._scale;
400,001✔
103
                _fraction = rhs._fraction;
400,001✔
104
                _nrOfBits = rhs._nrOfBits;
400,001✔
105
                _inf      = rhs._inf;
400,001✔
106
                _zero     = rhs._zero;
400,001✔
107
                _nan      = rhs._nan;
400,001✔
108
                return *this;
400,001✔
109
        }
110
        constexpr value<fbits>& operator=(signed char rhs) {
1✔
111
                *this = static_cast<long long>(rhs);
1✔
112
                return *this;
1✔
113
        }
114
        constexpr value<fbits>& operator=(short rhs) {
1✔
115
                *this = static_cast<long long>(rhs);
1✔
116
                return *this;
1✔
117
        }
118
        constexpr value<fbits>& operator=(int rhs) {
1,800,003✔
119
                *this = static_cast<long long>(rhs);
1,800,003✔
120
                return *this;
1,800,003✔
121
        }
122
        constexpr value<fbits>& operator=(long rhs) {
1✔
123
                *this = static_cast<long long>(rhs);
1✔
124
                return *this;
1✔
125
        }
126
        constexpr value<fbits>& operator=(long long rhs) {
1,800,010✔
127
                if (_trace_value_conversion) std::cout << "---------------------- CONVERT -------------------" << std::endl;
128
                if (rhs == 0) {
1,800,010✔
129
                        setzero();
1,000,002✔
130
                        return *this;
1,000,002✔
131
                }
132
                reset();
800,008✔
133
                _sign = (0x8000000000000000 & rhs);  // 1 is negative, 0 is positive
800,008✔
134
                if (_sign) {
800,008✔
135
                        // process negative number: process 2's complement of the input
136
                        _scale = int(sw::universal::find_msb(-rhs)) - 1;
50,000✔
137
                        uint64_t _fraction_without_hidden_bit = uint64_t(_scale == 0 ? 0 : (-rhs << (64 - _scale)));
50,000✔
138
                        _fraction = copy_integer_fraction<fbits>(_fraction_without_hidden_bit);
50,000✔
139
                        //take_2s_complement();
140
                        _nrOfBits = fbits;
50,000✔
141
                        if (_trace_value_conversion) std::cout << "int64 " << rhs << " sign " << _sign << " scale " << _scale << " fraction b" << _fraction << std::dec << std::endl;
142
                }
143
                else {
144
                        // process positive number
145
                        _scale = int(sw::universal::find_msb(rhs)) - 1;
750,008✔
146
                        uint64_t _fraction_without_hidden_bit = uint64_t(_scale == 0 ? 0 : (rhs << (64 - _scale)));
750,008✔
147
                        _fraction = copy_integer_fraction<fbits>(_fraction_without_hidden_bit);
750,008✔
148
                        _nrOfBits = fbits;
750,008✔
149
                        if (_trace_value_conversion) std::cout << "int64 " << rhs << " sign " << _sign << " scale " << _scale << " fraction b" << _fraction << std::dec << std::endl;
150
                }
151
                return *this;
800,008✔
152
        }
153
        constexpr value<fbits>& operator=(char rhs) {
1✔
154
                *this = (unsigned long long)(rhs);
1✔
155
                return *this;
1✔
156
        }
157
        constexpr value<fbits>& operator=(unsigned short rhs) {
1✔
158
                *this = static_cast<long long>(rhs);
1✔
159
                return *this;
1✔
160
        }
161
        constexpr value<fbits>& operator=(unsigned int rhs) {
1✔
162
                *this = static_cast<long long>(rhs);
1✔
163
                return *this;
1✔
164
        }
165
        constexpr value<fbits>& operator=(unsigned long rhs) {
1✔
166
                *this = static_cast<long long>(rhs);
1✔
167
                return *this;
1✔
168
        }
169
        constexpr value<fbits>& operator=(unsigned long long rhs) {
2✔
170
                if (_trace_value_conversion) std::cout << "---------------------- CONVERT -------------------" << std::endl;
171
                if (rhs == 0) {
2✔
172
                        setzero();
×
173
                }
174
                else {
175
                        reset();
2✔
176
                        _scale = static_cast<int>(sw::universal::find_msb(rhs)) - 1;
2✔
177
                        uint64_t _fraction_without_hidden_bit = _scale == 0 ? 0ull : (rhs << (64 - _scale)); // the scale == -1 case is handled above
2✔
178
                        _fraction = copy_integer_fraction<fbits>(_fraction_without_hidden_bit);
2✔
179
                        _nrOfBits = fbits;
2✔
180
                }
181
                if (_trace_value_conversion) std::cout << "uint64 " << rhs << " sign " << _sign << " scale " << _scale << " fraction b" << _fraction << std::dec << std::endl;
182
                return *this;
2✔
183
        }
184
        value<fbits>& operator=(float rhs) {
9✔
185
                reset();
9✔
186
                if (_trace_value_conversion) std::cout << "---------------------- CONVERT -------------------" << std::endl;
187

188
                switch (std::fpclassify(rhs)) {
9✔
189
                case FP_ZERO:
×
190
                        _nrOfBits = fbits;
×
191
                        _zero = true;
×
192
                        break;
×
193
                case FP_INFINITE:
×
194
                        _inf  = true;
×
195
                        _sign = true;
×
196
                        break;
×
197
                case FP_NAN:
×
198
                        _nan = true;
×
199
                        _sign = true;
×
200
                        break;
×
201
                case FP_SUBNORMAL:
9✔
202
                case FP_NORMAL:
203
                        {
204
                                float _fr{0};
9✔
205
                                unsigned int _23b_fraction_without_hidden_bit{0};
9✔
206
                                int _exponent{0};
9✔
207
                                extract_fp_components(rhs, _sign, _exponent, _fr, _23b_fraction_without_hidden_bit);
9✔
208
                                _scale = _exponent - 1;
9✔
209
                                _fraction = extract_23b_fraction<fbits>(_23b_fraction_without_hidden_bit);
9✔
210
                                _nrOfBits = fbits;
9✔
211
                                if (_trace_value_conversion) std::cout << "float " << rhs << " sign " << _sign << " scale " << _scale << " 23b fraction 0x" << std::hex << _23b_fraction_without_hidden_bit << " _fraction b" << _fraction << std::dec << std::endl;
212
                        }
213
                        break;
9✔
214
                }
215
                return *this;
9✔
216
        }
217
        value<fbits>& operator=(double rhs) {
446,069✔
218
                using std::get;
219
                reset();
446,069✔
220
                if (_trace_value_conversion) std::cout << "---------------------- CONVERT -------------------" << std::endl;
221

222
                switch (std::fpclassify(rhs)) {
446,069✔
223
                case FP_ZERO:
500✔
224
                        _nrOfBits = fbits;
500✔
225
                        _zero = true;
500✔
226
                        break;
500✔
227
                case FP_INFINITE:
1✔
228
                        _inf = true;
1✔
229
                        _sign = true;
1✔
230
                        break;
1✔
231
                case FP_NAN:
1✔
232
                        _nan = true;
1✔
233
                        _sign = true;
1✔
234
                        break;
1✔
235
                case FP_SUBNORMAL:
445,567✔
236
                case FP_NORMAL:
237
                        {
238
#if 1
239
                                double _fr{0};
445,567✔
240
                                unsigned long long _52b_fraction_without_hidden_bit{0};
445,567✔
241
                                int _exponent{0};
445,567✔
242
                                extract_fp_components(rhs, _sign, _exponent, _fr, _52b_fraction_without_hidden_bit);
445,567✔
243
#endif
244
#if 0
245
                                auto components= ieee_components(rhs);
246
                                _sign= get<0>(components);
247
                                int _exponent= get<1>(components);
248
                                unsigned long long _52b_fraction_without_hidden_bit= get<2>(components);
249
#endif
250
                                _scale = _exponent - 1;
445,567✔
251
                                _fraction = extract_52b_fraction<fbits>(_52b_fraction_without_hidden_bit);
445,567✔
252
                                _nrOfBits = fbits;
445,567✔
253
                                if (_trace_value_conversion) std::cout << "double " << rhs << " sign " << _sign << " scale " << _scale << " 52b fraction 0x" << std::hex << _52b_fraction_without_hidden_bit << " _fraction b" << _fraction << std::dec << std::endl;
254
                        }
255
                        break;
445,567✔
256
                }
257
                return *this;
446,069✔
258
        }
259
        value<fbits>& operator=(long double rhs) {
2✔
260
                reset();
2✔
261
                if (_trace_value_conversion) std::cout << "---------------------- CONVERT -------------------" << std::endl;
262

263
                switch (std::fpclassify(rhs)) {
2✔
264
                case FP_ZERO:
×
265
                        _nrOfBits = fbits;
×
266
                        _zero = true;
×
267
                        break;
×
268
                case FP_INFINITE:
×
269
                        _inf = true;
×
270
                        _sign = true;
×
271
                        break;
×
272
                case FP_NAN:
×
273
                        _nan = true;
×
274
                        _sign = true;
×
275
                        break;
×
276
                case FP_SUBNORMAL:
2✔
277
                case FP_NORMAL:
278
                        {
279
                                long double _fr{0};
2✔
280
                                int _exponent{0};
2✔
281
                                // fraction without hidden bit moved into the if
282

283
                                // how to interpret the fraction bits: TODO: this should be a static compile-time code block
284
                                if constexpr (sizeof(long double) == 8) {
285
                                        // we are just a double and thus only have 52bits of fraction
286

287
                                        std::uint64_t _63b_fraction_without_hidden_bit{0};
288
                                        extract_fp_components(rhs, _sign, _exponent, _fr, _63b_fraction_without_hidden_bit);
289
                                        _scale = _exponent - 1;
290

291
                                        _fraction = extract_52b_fraction<fbits>(_63b_fraction_without_hidden_bit);
292
                                        if (_trace_value_conversion) std::cout << "long double " << rhs << " sign " << _sign << " scale " << _scale << " 52b fraction 0x" << std::hex << _63b_fraction_without_hidden_bit << " _fraction b" << _fraction << std::dec << std::endl;
293

294
                                }
295
                                else if constexpr (sizeof(long double) == 16 && std::numeric_limits<long double>::digits <= 64) {
296
                                        // how to differentiate between 80bit and 128bit formats?
297

298
                                        std::uint64_t _63b_fraction_without_hidden_bit{0};
2✔
299
                                        extract_fp_components(rhs, _sign, _exponent, _fr, _63b_fraction_without_hidden_bit);
2✔
300
                                        _scale = _exponent - 1;
2✔
301

302
                                        _fraction = extract_63b_fraction<fbits>(_63b_fraction_without_hidden_bit);
2✔
303
                                        if (_trace_value_conversion) std::cout << "long double " << rhs << " sign " << _sign << " scale " << _scale << " 63b fraction 0x" << std::hex << _63b_fraction_without_hidden_bit << " _fraction b" << _fraction << std::dec << std::endl;
304

305
                                } else if constexpr (sizeof(long double) == 16  && std::numeric_limits<long double>::digits <= 128) {
306
                                        internal::uint128 _112b_fraction_without_hidden_bit{0};
307
                                        extract_fp_components(rhs, _sign, _exponent, _fr, _112b_fraction_without_hidden_bit);
308
                                        _scale = _exponent - 1;
309

310
                                        _fraction = extract_long_double_fraction<fbits>(_112b_fraction_without_hidden_bit);
311
                                        if (_trace_value_conversion) std::cout << "long double " << rhs << " sign " << _sign << " scale " << _scale << " 112b fraction upper 0x" << std::hex << _112b_fraction_without_hidden_bit.upper << " lower 0x" << std::hex << _112b_fraction_without_hidden_bit.lower << " _fraction b" << _fraction << std::dec << std::endl;
312
                                } else {
313
                                        assert(false);
314
                                }
315
                                _nrOfBits = fbits;
2✔
316
                        }
317
                        break;
2✔
318
                }
319
                return *this;
2✔
320
        }
321

322
        // operators
323
        constexpr value<fbits> operator-() const {                                
99,999✔
324
                return value<fbits>(!_sign, _scale, _fraction, _zero, _inf);
99,999✔
325
        }
326

327
        value<fbits>& operator++() {
199,998✔
328
                *this = *this + value<fbits>(1);
199,998✔
329
                return *this;
199,998✔
330
        }
331
        value<fbits> operator++(int) {
99,999✔
332
                value<fbits> tmp{ *this };
99,999✔
333
                operator++();
99,999✔
334
                return tmp;
99,999✔
335
        }
336

337
        // modifiers
338
        constexpr void reset() & {
1,246,090✔
339
                _sign  = false;
1,246,090✔
340
                _scale = 0;
1,246,090✔
341
                _nrOfBits = 0;
1,246,090✔
342
                _inf = false;
1,246,090✔
343
                _zero = false;
1,246,090✔
344
                _nan = false;
1,246,090✔
345
                _fraction.reset(); // not constexpr
1,246,090✔
346
                // _fraction= bitblock<fbits>{}; // work around
347
        }
1,246,090✔
348
        void set(bool sign, int scale, bitblock<fbits> fraction_without_hidden_bit, bool zero = false, bool inf = false, bool nan = false) {
1,092,533✔
349
                _sign     = sign;
1,092,533✔
350
                _scale    = scale;
1,092,533✔
351
                _fraction = fraction_without_hidden_bit;
1,092,533✔
352
                _zero     = zero;
1,092,533✔
353
                _inf      = inf;
1,092,533✔
354
                _nan      = nan;
1,092,533✔
355
        }
1,092,533✔
356
        void setzero() {
1,000,004✔
357
                _zero     = true;
1,000,004✔
358
                _sign     = false;
1,000,004✔
359
                _inf      = false;
1,000,004✔
360
                _nan      = false;
1,000,004✔
361
                _scale    = 0;
1,000,004✔
362
                _nrOfBits = fbits;
1,000,004✔
363
                _fraction.reset();
1,000,004✔
364
        }
1,000,004✔
365
        void setinf() {      // this maps to NaR on the posit side, and that has a sign = 1
3✔
366
                _inf      = true;
3✔
367
                _sign     = true;
3✔
368
                _zero     = false;
3✔
369
                _nan      = false;
3✔
370
                _scale    = 0;
3✔
371
                _nrOfBits = fbits;
3✔
372
                _fraction.reset();
3✔
373
        }
3✔
374
        void setnan() {                // this will also map to NaR
3✔
375
                _nan      = true;
3✔
376
                _sign     = true;
3✔
377
                _zero     = false;
3✔
378
                _inf      = false;
3✔
379
                _scale    = 0;
3✔
380
                _nrOfBits = fbits;        
3✔
381
                _fraction.reset();
3✔
382
        }
3✔
383
        inline void setsign(bool sign = true) { _sign = sign; }
1✔
384
        inline void setscale(int e) { _scale = e; }
10✔
385
        inline void setfraction(const bitblock<fbits>& fraction_without_hidden_bit) { _fraction = fraction_without_hidden_bit; }
386
        inline bool isneg() const { return _sign; }
387
        inline bool ispos() const { return !_sign; }
388
        inline constexpr bool iszero() const { return _zero; }
846,124✔
389
        inline constexpr bool isinf() const { return _inf; }
1,692,193✔
390
        inline constexpr bool isnan() const { return _nan; }
92✔
391
        inline bool sign() const { return _sign; }
1,692,099✔
392
        inline int scale() const { return _scale; }
2,138,109✔
393
        bitblock<fbits> fraction() const { return _fraction; }
448,266✔
394

395
        // Get the mantissa with hidden bit for Grisu-style decimal conversion
396
        // Returns: bitblock with hidden bit at position fbits, fraction bits below
397
        // The actual value is: mantissa * 2^(scale - fbits)
398
        bitblock<fhbits> mantissa() const {
399
                bitblock<fhbits> m;
400
                if (_zero || _inf || _nan) return m;
401

402
                // Set hidden bit at position fbits
403
                m.set(fbits, true);
404

405
                // Copy fraction bits
406
                for (unsigned i = 0; i < fbits; ++i) {
407
                        m[i] = _fraction[i];
408
                }
409
                return m;
410
        }
411

412
        // Get the effective binary exponent for the mantissa representation
413
        // The value = mantissa * 2^mantissa_exponent()
414
        int mantissa_exponent() const {
415
                return _scale - static_cast<int>(fbits);
416
        }
417

418
        // Normalized shift (e.g., for addition).
419
        template <unsigned Size>
420
        bitblock<Size> nshift(int shift) const {
1,292,034✔
421
        bitblock<Size> number;
1,292,034✔
422

423
#if VALUE_THROW_ARITHMETIC_EXCEPTION
424
                // Check range
425
                if (int(fbits) + shift >= int(Size))
492,032✔
426
                        throw value_shift_too_large{};
×
427
#else
428
                // Check range
429
                if (int(fbits) + shift >= int(Size)) {
800,002✔
430
                        std::cerr << "nshift: shift is too large\n";
×
431
                        number.reset();
×
432
                        return number;
×
433
                }
434
#endif // VALUE_THROW_ARITHMETIC_EXCEPTIONS
435

436
                int hpos = int(fbits) + shift;       // position of hidden bit
1,292,034✔
437
                if (hpos <= 0) {   // If hidden bit is LSB or beyond just set uncertainty bit and call it a day
1,292,034✔
438
                        number[0] = true;
350,016✔
439
                        return number;
350,016✔
440
                }
441
                number[unsigned(hpos)] = true; // hidden bit now safely set
942,018✔
442

443
                // Copy fraction bits into certain part
444
                for (int npos = hpos - 1, fpos = int(fbits) - 1; npos > 0 && fpos >= 0; --npos, --fpos)
7,853,428✔
445
                        number[unsigned(npos)] = _fraction[unsigned(fpos)];
6,911,410✔
446

447
                // Set uncertainty bit
448
                bool uncertainty = false;
942,018✔
449
                for (int fpos = std::min(int(fbits) - 1, -shift); fpos >= 0 && !uncertainty; --fpos)
1,147,858✔
450
                        uncertainty |= _fraction[unsigned(fpos)];
205,840✔
451
                number[0] = uncertainty;
942,018✔
452
                return number;
942,018✔
453
        }
454
        // get a fixed point number by making the hidden bit explicit: useful for multiply units
455
        bitblock<fhbits> get_fixed_point() const {
400,002✔
456
                bitblock<fbits + 1> fixed_point_number;
400,002✔
457
                fixed_point_number.set(fbits, true); // make hidden bit explicit
400,002✔
458
                for (unsigned i = 0; i < fbits; i++) {
4,400,100✔
459
                        fixed_point_number[i] = _fraction[i];
4,000,098✔
460
                }
461
                return fixed_point_number;
400,002✔
462
        }
463
        // get the fraction value including the implicit hidden bit (this is at an exponent level 1 smaller)
464
        template<typename Ty = double>
465
        Ty get_implicit_fraction_value() const {
466
                if (_zero) return (long double)0.0;
467
                Ty v = 1.0;
468
                Ty scale = 0.5;
469
                for (int i = int(fbits) - 1; i >= 0; i--) {
470
                        if (_fraction.test(unsigned(i))) v += scale;
471
                        scale *= 0.5;
472
                        if (scale == 0.0) break;
473
                }
474
                return v;
475
        }
476
        template<typename Ty = double>
477
        Ty sign_value() const { 
346,093✔
478
                return (_sign ? Ty{ -1 } : Ty{ 1 });
346,093✔
479
        }
480
        template<typename Ty = double>
481
        Ty scale_value() const {
346,093✔
482
                if (_zero) return Ty(0.0);
346,093✔
483
                return std::pow(Ty(2.0), Ty(_scale));
345,594✔
484
        }
485
        template<typename Ty = double>
486
        Ty fraction_value() const {
346,093✔
487
                if (_zero) return (long double)0.0;
346,093✔
488
                Ty v = 1.0;
345,594✔
489
                Ty scale = 0.5;
345,594✔
490
                for (int i = int(fbits) - 1; i >= 0; i--) {
3,311,531✔
491
                        if (_fraction.test(unsigned(i))) v += scale;
2,965,937✔
492
                        scale *= 0.5;
2,965,937✔
493
                        if (scale == 0.0) break;
2,965,937✔
494
                }
495
                return v;
345,594✔
496
        }
497

498
        // conversion helpers
499
        int to_int()                 const noexcept { return int(to_float()); }
500
        long to_long()               const noexcept { return long(to_float()); }
501
        long long to_long_long()     const noexcept { return (long long)(to_double()); }
502
        float to_float()             const noexcept { return sign_value<float>() * scale_value<float>() * fraction_value<float>(); }
17✔
503
        double to_double()           const noexcept { return sign_value<double>() * scale_value<double>() * fraction_value<double>(); }
346,076✔
504
        long double to_long_double() const noexcept { return sign_value<long double>() * scale_value<long double>() * fraction_value<long double>(); }
505

506
        // explicit conversion operators to native types
507
        explicit operator long double() const { return to_long_double(); }
508
        explicit operator double() const { return to_double(); }
100,047✔
509
        explicit operator float() const { return to_float(); }
3✔
510

511
        // TODO: this does not implement a 'real' right extend. tgtbits need to be shorter than fbits
512
        template<unsigned srcbits, unsigned tgtbits>
513
        void right_extend(const value<srcbits>& src) {
514
                _sign = src.sign();
515
                _scale = src.scale();
516
                _nrOfBits = tgtbits;
517
                _inf = src.isinf();
518
                _zero = src.iszero();
519
                _nan = src.isnan();
520
                bitblock<srcbits> src_fraction = src.fraction();
521
                if (!_inf && !_zero && !_nan) {
522
                        for (int s = srcbits - 1, t = tgtbits - 1; s >= 0 && t >= 0; --s, --t)
523
                                _fraction[static_cast<unsigned>(t)] = src_fraction[static_cast<unsigned>(s)];
524
                }
525
        }
526
        // round to a target size number of bits using round-to-nearest round-to-even-on-tie
527
        template<unsigned tgt_size>
528
        value<tgt_size> round_to() {
600,003✔
529
                bitblock<tgt_size> rounded_fraction;
600,003✔
530
                if constexpr (tgt_size == 0) {
531
                        bool round_up = false;
532
                        if constexpr (fbits >= 2) {
533
                                bool blast = _fraction[fbits - 1ull];
534
                                bool sb = anyAfter(_fraction, static_cast<int>(fbits) - 2);
535
                                if (blast && sb) round_up = true;
536
                        }
537
                        else if constexpr (fbits == 1) {
538
                                round_up = _fraction[0];
539
                        }
540
                        return value<tgt_size>(_sign, (round_up ? _scale + 1 : _scale), rounded_fraction, _zero, _inf);
541
                }
542
                else {
543
                        if (!_zero && !_inf) {
600,003✔
544
                                if constexpr (tgt_size < fbits) {
545
                                        int rb = int(tgt_size) - 1;
599,999✔
546
                                        int lb = int(fbits) - int(tgt_size) - 1;
599,999✔
547
                                        for (int i = int(fbits) - 1; i > lb; i--, rb--) {
6,600,054✔
548
                                                rounded_fraction[static_cast<unsigned>(rb)] = _fraction[static_cast<unsigned>(i)];
6,000,055✔
549
                                        }
550
                                        bool blast = _fraction[static_cast<unsigned>(lb)];
599,999✔
551
                                        bool sb = false;
599,999✔
552
                                        if (lb > 0) sb = anyAfter(_fraction, lb-1);
599,999✔
553
                                        if (blast || sb) rounded_fraction[0ull] = true;
599,999✔
554
                                }
555
                                else {
556
                                        int rb = int(tgt_size) - 1;
557
                                        for (int i = int(fbits) - 1; i >= 0; i--, rb--) {
558
                                                rounded_fraction[static_cast<unsigned>(rb)] = _fraction[static_cast<unsigned>(i)];
559
                                        }
560
                                }
561
                        }
562
                }
563
                return value<tgt_size>(_sign, _scale, rounded_fraction, _zero, _inf);
1,200,006✔
564
        }
565

566
private:
567
        bool                _sign;
568
        int                 _scale;
569
        int                 _nrOfBits;  // in case the fraction is smaller than the full fbits
570
        bitblock<fbits>            _fraction;
571
        bool                _inf;
572
        bool                _zero;
573
        bool                _nan;
574

575
        // template parameters need names different from class template parameters (for gcc and clang)
576
        template<unsigned nfbits>
577
        friend std::ostream& operator<< (std::ostream& ostr, const value<nfbits>& r);
578
        template<unsigned nfbits>
579
        friend std::istream& operator>> (std::istream& istr, value<nfbits>& r);
580

581
        template<unsigned nfbits>
582
        friend bool operator==(const value<nfbits>& lhs, const value<nfbits>& rhs);
583
        template<unsigned nfbits>
584
        friend bool operator!=(const value<nfbits>& lhs, const value<nfbits>& rhs);
585
        template<unsigned nfbits>
586
        friend bool operator< (const value<nfbits>& lhs, const value<nfbits>& rhs);
587
        template<unsigned nfbits>
588
        friend bool operator> (const value<nfbits>& lhs, const value<nfbits>& rhs);
589
        template<unsigned nfbits>
590
        friend bool operator<=(const value<nfbits>& lhs, const value<nfbits>& rhs);
591
        template<unsigned nfbits>
592
        friend bool operator>=(const value<nfbits>& lhs, const value<nfbits>& rhs);
593
};
594

595
////////////////////// VALUE operators
596

597
// ETLO 7/19/2022
598
// OLD compiler guard
599
// we are trying to get value<> to use a native string conversion so that we can support arbitrary large values
600
// but this is turning out to be a complicated implementation with deep history and named algorithms, such as Dragon4, etc.
601
// For the moment, we still take the easy way out.
602
#define USE_CONVERSION_TO_DOUBLE 
603
#ifdef USE_CONVERSION_TO_DOUBLE
604
template<unsigned nfbits>
605
inline std::string convert_to_string(std::ios_base::fmtflags flags, const value<nfbits>& v, std::streamsize precision = 0) {
51✔
606
        std::stringstream s;
51✔
607
        if (v.isinf()) {
51✔
608
                if (v.sign()) {
4✔
609
                        s << "-inf";
2✔
610
                }
611
                else {
612
                        s << ((flags & std::ios_base::showpos) ? "+inf" : "inf");
2✔
613
                }
614
        }
615
        else {
616
                if (precision) {
47✔
617
                        s << std::setprecision(precision) << double(v);
47✔
618
                }
619
                else {
620
                        s << double(v);
×
621
                }
622
        }
623
        return s.str();
102✔
624
}
51✔
625
#else
626

627
// Helper function: integer power of value<> (needed for decimal normalization)
628
template<unsigned fbits>
629
inline value<fbits> pown(const value<fbits>& a, int n) {
630
        if (a.iszero()) {
631
                if (n == 0) {
632
                        value<fbits> one(1);
633
                        return one;
634
                }
635
                return a;
636
        }
637

638
        int N = (n < 0) ? -n : n;
639
        value<fbits> result(1);
640
        value<fbits> base = a;
641

642
        // Binary exponentiation
643
        while (N > 0) {
644
                if (N % 2 == 1) {
645
                        result = result * base;
646
                }
647
                N /= 2;
648
                if (N > 0) base = base * base;
649
        }
650

651
        if (n < 0) {
652
                value<fbits> one(1);
653
                result = one / result;
654
        }
655

656
        return result;
657
}
658

659
// Helper function: extract decimal digits using integer arithmetic on value<>
660
template<unsigned nfbits>
661
inline void to_digits(std::vector<char>& s, int& exponent, int precision, const value<nfbits>& v) {
662
        constexpr double log10_of_2 = 0.301029995663981;  // log10(2)
663

664
        if (v.iszero()) {
665
                exponent = 0;
666
                for (int i = 0; i < precision; ++i) s[static_cast<unsigned>(i)] = '0';
667
                return;
668
        }
669

670
        // Estimate decimal exponent from binary scale
671
        exponent = static_cast<int>(log10_of_2 * v.scale());
672

673
        // Work with normalized value in [1, 10) range
674
        // Use value<> arithmetic throughout - it uses exact integer operations internally
675
        value<nfbits> r = abs(v);
676
        value<nfbits> ten(10);
677
        value<nfbits> one(1);
678

679
        // Normalize to [1, 10) range by scaling with powers of 10
680
        if (exponent > 0) {
681
                r = r / pown(ten, exponent);
682
        } else if (exponent < 0) {
683
                r = r * pown(ten, -exponent);
684
        }
685

686
        // Fine-tune to ensure in [1, 10) range
687
        // Compare using value<> comparisons which are exact
688
        while (r >= ten) {
689
                r = r / ten;
690
                ++exponent;
691
        }
692
        while (r < one && !r.iszero()) {
693
                r = r * ten;
694
                --exponent;
695
        }
696

697
        // Extract digits: repeatedly extract integer part and multiply by 10
698
        int nrDigits = precision + 1;
699
        for (int i = 0; i < nrDigits; ++i) {
700
                // Extract integer part by comparing against integer values
701
                int digit = 0;
702
                value<nfbits> digit_val(0);
703

704
                // Find the digit by successive comparison (0-9)
705
                for (int d = 9; d >= 0; --d) {
706
                        value<nfbits> test_val(d);
707
                        if (r >= test_val) {
708
                                digit = d;
709
                                digit_val = test_val;
710
                                break;
711
                        }
712
                }
713

714
                s[static_cast<unsigned>(i)] = static_cast<char>(digit + '0');
715

716
                // Subtract digit and multiply by 10 for next iteration
717
                // This uses exact value<> arithmetic
718
                r = (r - digit_val) * ten;
719
        }
720

721
        // Round the last digit
722
        int lastDigit = nrDigits - 1;
723
        if (s[static_cast<unsigned>(lastDigit)] >= '5') {
724
                int i = nrDigits - 2;
725
                s[static_cast<unsigned>(i)]++;
726
                while (i > 0 && s[static_cast<unsigned>(i)] > '9') {
727
                        s[static_cast<unsigned>(i)] -= 10;
728
                        s[static_cast<unsigned>(--i)]++;
729
                }
730
        }
731

732
        // If first digit overflowed to 10, shift and adjust exponent
733
        if (s[0] > '9') {
734
                ++exponent;
735
                for (int i = precision; i >= 2; --i) {
736
                        s[static_cast<unsigned>(i)] = s[static_cast<unsigned>(i - 1)];
737
                }
738
                s[0] = '1';
739
                s[1] = '0';
740
        }
741

742
        s[static_cast<unsigned>(precision)] = 0;  // null terminator
743
}
744

745
template<unsigned nfbits>
746
inline std::string convert_to_string(std::ios_base::fmtflags flags, const value<nfbits>& v, unsigned precision) {
747
        std::string result;
748

749
        // Handle special cases
750
        if (v.isnan()) return std::string("nan");
751
        if (v.isinf()) {
752
                if (v.sign()) {
753
                        result = "-inf";
754
                }
755
                else {
756
                        result = ((flags & std::ios_base::showpos) ? "+inf" : "inf");
757
                }
758
                return result;
759
        }
760

761
        if (v.iszero()) {
762
                result = "0";
763
                if (precision > 0) {
764
                        result += '.';
765
                        result.append(precision, '0');
766
                }
767
                if (v.sign()) result.insert(0, 1, '-');
768
                else if (flags & std::ios_base::showpos) result.insert(0, 1, '+');
769
                return result;
770
        }
771

772
        bool scientific = (flags & std::ios_base::scientific) == std::ios_base::scientific;
773
        bool fixed = (flags & std::ios_base::fixed) == std::ios_base::fixed;
774
        if (fixed && scientific) fixed = false;  // scientific takes precedence
775

776
        int nrDigits = static_cast<int>(precision);
777
        if (nrDigits == 0) nrDigits = static_cast<int>(nfbits) / 3;
778

779
        // Estimate scale for fixed format
780
        constexpr double log10_of_2 = 0.301029995663981;
781
        int scale10 = static_cast<int>(v.scale() * log10_of_2);
782

783
        if (fixed) {
784
                nrDigits = std::max(1, static_cast<int>(precision) + scale10 + 1);
785
        }
786

787
        // Extract digits
788
        std::vector<char> digits(static_cast<size_t>(nrDigits + 1));
789
        int exponent;
790
        to_digits(digits, exponent, nrDigits, v);
791

792
        // Build the result string
793
        if (v.sign()) result = "-";
794
        else if (flags & std::ios_base::showpos) result = "+";
795

796
        if (fixed) {
797
                int integerDigits = exponent + 1;
798
                if (integerDigits > 0) {
799
                        // Normal fixed point
800
                        for (int i = 0; i < integerDigits && i < nrDigits; ++i) {
801
                                result += digits[static_cast<unsigned>(i)];
802
                        }
803
                        if (precision > 0) {
804
                                result += '.';
805
                                for (int i = integerDigits; i < integerDigits + static_cast<int>(precision) && i < nrDigits; ++i) {
806
                                        result += digits[static_cast<unsigned>(i)];
807
                                }
808
                        }
809
                }
810
                else {
811
                        // Small number (0.00...)
812
                        result += "0.";
813
                        for (int i = 0; i < -integerDigits; ++i) result += '0';
814
                        for (int i = 0; i < static_cast<int>(precision) + integerDigits && i < nrDigits; ++i) {
815
                                result += digits[static_cast<unsigned>(i)];
816
                        }
817
                }
818
        }
819
        else {
820
                // Scientific notation
821
                result += digits[0];
822
                if (precision > 0) {
823
                        result += '.';
824
                        for (unsigned i = 1; i <= precision && i < digits.size() - 1; ++i) {
825
                                result += digits[i];
826
                        }
827
                }
828
                result += 'e';
829
                result += (exponent >= 0) ? '+' : '-';
830
                int abs_exp = (exponent >= 0) ? exponent : -exponent;
831
                if (abs_exp >= 100) result += char('0' + abs_exp / 100);
832
                result += char('0' + (abs_exp / 10) % 10);
833
                result += char('0' + abs_exp % 10);
834
        }
835

836
        return result;
837
}
838
#endif
839

840

841
template<unsigned nfbits>
842
inline std::ostream& operator<<(std::ostream& ostr, const value<nfbits>& v) {
51✔
843
        std::streamsize nrDigits = ostr.precision();
51✔
844
        std::string s = convert_to_string(ostr.flags(), v, nrDigits);
51✔
845
        std::streamsize width = ostr.width();
51✔
846
        if (static_cast<unsigned>(width) > s.size()) {
51✔
847
                char fill = ostr.fill();
5✔
848
                if ((ostr.flags() & std::ios_base::left) == std::ios_base::left)
5✔
849
                        s.append(static_cast<std::string::size_type>(width - s.size()), fill);
1✔
850
                else
851
                        s.insert(static_cast<std::string::size_type>(0), static_cast<std::string::size_type>(width - s.size()), fill);
4✔
852
        }
853
        return ostr << s;
102✔
854
}
51✔
855

856
template<unsigned nfbits>
857
inline std::istream& operator>> (std::istream& istr, const value<nfbits>& v) {
858
        istr >> v._fraction;
859
        return istr;
860
}
861

862
template<unsigned nfbits>
863
inline bool operator==(const value<nfbits>& lhs, const value<nfbits>& rhs) { return lhs._sign == rhs._sign && lhs._scale == rhs._scale && lhs._fraction == rhs._fraction && lhs._nrOfBits == rhs._nrOfBits && lhs._zero == rhs._zero && lhs._inf == rhs._inf; }
246,016✔
864
template<unsigned nfbits>
865
inline bool operator!=(const value<nfbits>& lhs, const value<nfbits>& rhs) { return !operator==(lhs, rhs); }
246,016✔
866
template<unsigned nfbits>
867
inline bool operator< (const value<nfbits>& lhs, const value<nfbits>& rhs) {
1,223,005✔
868
        if (lhs._inf) {
1,223,005✔
869
                if (rhs._inf) return false; else return true; // everything is less than -infinity
×
870
        }
871
        else {
872
                if (rhs._inf) return false;
1,223,005✔
873
        }
874

875
        if (lhs._zero) {
1,223,005✔
876
                if (rhs._zero) return false; // they are both 0
6✔
877
                if (rhs._sign) return false; else return true;
×
878
        }
879
        if (rhs._zero) {
1,222,999✔
880
                if (lhs._sign) return true; else return false;
999,991✔
881
        }
882
        if (lhs._sign) {
223,008✔
883
                if (rhs._sign) {        // both operands are negative
×
884
                        if (lhs._scale > rhs._scale) {
×
885
                                return true;        // lhs is more negative
×
886
                        }
887
                        else {
888
                                if (lhs._scale == rhs._scale) {
×
889
                                        // compare the fraction, which is an unsigned value
890
                                        if (lhs._fraction == rhs._fraction) return false; // they are the same value
×
891
                                        if (lhs._fraction > rhs._fraction) {
×
892
                                                return true; // lhs is more negative
×
893
                                        }
894
                                        else {
895
                                                return false; // lhs is less negative
×
896
                                        }
897
                                }
898
                                else {
899
                                        return false; // lhs is less negative
×
900
                                }
901
                        }
902
                }
903
                else {
904
                        return true; // lhs is negative, rhs is positive
×
905
                }
906
        }
907
        else {
908
                if (rhs._sign) {        
223,008✔
909
                        return false; // lhs is positive, rhs is negative
×
910
                }
911
                else {
912
                        if (lhs._scale > rhs._scale) {
223,008✔
913
                                return false; // lhs is more positive
59,521✔
914
                        }
915
                        else {
916
                                if (lhs._scale == rhs._scale) {
163,487✔
917
                                        // compare the fractions
918
                                        if (lhs._fraction == rhs._fraction) return false; // they are the same value
3,969✔
919
                                        if (lhs._fraction > rhs._fraction) {
3,472✔
920
                                                return false; // lhs is more positive than rhs
1,736✔
921
                                        }
922
                                        else {
923
                                                return true; // lhs is less positive than rhs
1,736✔
924
                                        }
925
                                }
926
                                else {
927
                                        return true; // lhs is less positive
159,518✔
928
                                }
929
                        }
930
                }
931
        }
932
//        return false; // all paths are taken care of
933
}
934
template<unsigned nfbits>
935
inline bool operator> (const value<nfbits>& lhs, const value<nfbits>& rhs) { return  operator< (rhs, lhs); }
936
template<unsigned nfbits>
937
inline bool operator<=(const value<nfbits>& lhs, const value<nfbits>& rhs) { return !operator> (lhs, rhs); }
938
template<unsigned nfbits>
939
inline bool operator>=(const value<nfbits>& lhs, const value<nfbits>& rhs) { return !operator< (lhs, rhs); }
999,996✔
940

941
template<unsigned nbits>
942
inline std::string to_binary(const bitblock<nbits>& a, bool nibbleMarker = true) {
12✔
943
        if constexpr (nbits > 1) {
944
                std::stringstream s;
12✔
945
                s << "0b";
12✔
946
                for (int i = int(nbits - 1); i >= 0; --i) {
314✔
947
                        s << (a[static_cast<unsigned>(i)] ? '1' : '0');
302✔
948
                        if (i > 0 && (i % 4) == 0 && nibbleMarker) s << '\'';
302✔
949
                }
950
                return s.str();
24✔
951
        }
12✔
952
        else {
953
                return std::string("-");
954
        }
955
}
956
template<unsigned fbits>
957
inline std::string to_triple(const value<fbits>& v, bool nibbleMarker = true) {
12✔
958
        std::stringstream s;
12✔
959
        if (v.iszero()) {
12✔
960
                s << "(+,0," << std::setw(fbits) << v.fraction() << ')';
×
961
                return s.str();
×
962
        }
963
        else if (v.isinf()) {
12✔
964
                s << "(inf," << std::setw(fbits) << v.fraction() << ')';
×
965
                return s.str();
×
966
        }
967
        s << (v.sign() ? "(-," : "(+,");
12✔
968
        s << v.scale() << ',';
12✔
969
        s << to_binary(v.fraction(), nibbleMarker) << ')';
12✔
970
        return s.str();
12✔
971
}
12✔
972

973
/// Magnitude of a scientific notation value (equivalent to turning the sign bit off).
974
template<unsigned nfbits>
975
value<nfbits> abs(const value<nfbits>& v) {
446,018✔
976
        return value<nfbits>(false, v.scale(), v.fraction(), v.iszero());
446,018✔
977
}
978

979
// add two values with fbits fraction bits, round them to abits, and return the abits+1 result value
980
template<unsigned fbits, unsigned abits>
981
void module_add(const value<fbits>& lhs, const value<fbits>& rhs, value<abits + 1>& result) {
546,016✔
982
        // with sign/magnitude adders it is customary to organize the computation 
983
        // along the four quadrants of sign combinations
984
        //  + + = +
985
        //  + - =   lhs > rhs ? + : -
986
        //  - + =   lhs > rhs ? - : +
987
        //  - - = 
988
        // to simplify the result processing assign the biggest 
989
        // absolute value to R1, then the sign of the result will be sign of the value in R1.
990

991
        if (lhs.isinf() || rhs.isinf()) {
546,016✔
992
                result.setinf();
×
993
                return;
496✔
994
        }
995
        int lhs_scale = lhs.scale(), rhs_scale = rhs.scale(), scale_of_result = std::max(lhs_scale, rhs_scale);
546,016✔
996

997
        // align the fractions
998
        bitblock<abits> r1 = lhs.template nshift<abits>(lhs_scale - scale_of_result + 3);
546,016✔
999
        bitblock<abits> r2 = rhs.template nshift<abits>(rhs_scale - scale_of_result + 3);
546,016✔
1000
        bool r1_sign = lhs.sign(), r2_sign = rhs.sign();
546,016✔
1001
        bool signs_are_different = r1_sign != r2_sign;
546,016✔
1002

1003
        if (signs_are_different && abs(lhs) < abs(rhs)) {
546,016✔
1004
                std::swap(r1, r2);
61,256✔
1005
                std::swap(r1_sign, r2_sign);
61,256✔
1006
        }
1007

1008
        if (signs_are_different) r2 = twos_complement(r2);
546,016✔
1009

1010
        if (_trace_value_add) {
1011
                std::cout << (r1_sign ? "sign -1" : "sign  1") << " scale " << std::setw(3) << scale_of_result << " r1       " << r1 << std::endl;
1012
                if (signs_are_different) {
1013
                        std::cout << (r2_sign ? "sign -1" : "sign  1") << " scale " << std::setw(3) << scale_of_result << " r2 orig  " << twos_complement(r2) << std::endl;
1014
                }
1015
                std::cout << (r2_sign ? "sign -1" : "sign  1") << " scale " << std::setw(3) << scale_of_result << " r2       " << r2 << std::endl;
1016
        }
1017

1018
        bitblock<abits + 1> sum;
546,016✔
1019
        const bool carry = add_unsigned(r1, r2, sum);
546,016✔
1020

1021
        if (_trace_value_add) std::cout << (r1_sign ? "sign -1" : "sign  1") << " carry " << std::setw(3) << (carry ? 1 : 0) << " sum     " << sum << std::endl;
1022

1023
        int shift = 0;
546,016✔
1024
        if (carry) {
546,016✔
1025
                if (r1_sign == r2_sign) {  // the carry && signs== implies that we have a number bigger than r1
135,473✔
1026
                        shift = -1;
12,465✔
1027
                } 
1028
                else {
1029
                        // the carry && signs!= implies ||result|| < ||r1||, must find MSB (in the complement)
1030
                        for (int i = int(abits) - 1; i >= 0 && !sum[unsigned(i)]; --i) {
158,124✔
1031
                                ++shift;
35,116✔
1032
                        }
1033
                }
1034
        }
1035
        assert(shift >= -1);
546,016✔
1036

1037
        if (shift >= long(abits)) {            // we have actual 0                            
546,016✔
1038
                sum.reset();
496✔
1039
                result.set(false, 0, sum, true, false, false);
496✔
1040
                return;
496✔
1041
        }
1042

1043
        scale_of_result -= shift;
545,520✔
1044
        const int hpos = int(abits) - 1 - shift;         // position of the hidden bit 
545,520✔
1045
        sum <<= static_cast<size_t>(1 + int(abits) - hpos);
545,520✔
1046
        if (_trace_value_add) std::cout << (r1_sign ? "sign -1" : "sign  1") << " scale " << std::setw(3) << scale_of_result << " sum     " << sum << std::endl;
1047
        result.set(r1_sign, scale_of_result, sum, false, false, false);
545,520✔
1048
}
1049

1050
// subtract module: use ADDER
1051
template<unsigned fbits, unsigned abits>
1052
void module_subtract(const value<fbits>& lhs, const value<fbits>& rhs, value<abits + 1>& result) {
100,001✔
1053
        if (lhs.isinf() || rhs.isinf()) {
100,001✔
1054
                result.setinf();
×
1055
                return;
2✔
1056
        }
1057
        int lhs_scale = lhs.scale(), rhs_scale = rhs.scale(), scale_of_result = std::max(lhs_scale, rhs_scale);
100,001✔
1058

1059
        // align the fractions
1060
        bitblock<abits> r1 = lhs.template nshift<abits>(lhs_scale - scale_of_result + 3);
100,001✔
1061
        bitblock<abits> r2 = rhs.template nshift<abits>(rhs_scale - scale_of_result + 3);
100,001✔
1062
        bool r1_sign = lhs.sign(), r2_sign = !rhs.sign();
100,001✔
1063
        bool signs_are_different = r1_sign != r2_sign;
100,001✔
1064

1065
        if (abs(lhs) < abs(rhs)) {
100,001✔
1066
                std::swap(r1, r2);
99,998✔
1067
                std::swap(r1_sign, r2_sign);
99,998✔
1068
        }
1069

1070
        if (signs_are_different) r2 = twos_complement(r2);
100,001✔
1071

1072
        if (_trace_value_sub) {
1073
                std::cout << (r1_sign ? "sign -1" : "sign  1") << " scale " << std::setw(3) << scale_of_result << " r1       " << r1 << std::endl;
1074
                std::cout << (r2_sign ? "sign -1" : "sign  1") << " scale " << std::setw(3) << scale_of_result << " r2       " << r2 << std::endl;
1075
        }
1076

1077
        bitblock<abits + 1> sum;
100,001✔
1078
        const bool carry = add_unsigned(r1, r2, sum);
100,001✔
1079

1080
        if (_trace_value_sub) std::cout << (r1_sign ? "sign -1" : "sign  1") << " carry " << std::setw(3) << (carry ? 1 : 0) << " sum     " << sum << std::endl;
1081

1082
        int shift = 0;
100,001✔
1083
        if (carry) {
100,001✔
1084
                if (r1_sign == r2_sign) {  // the carry && signs== implies that we have a number bigger than r1
100,001✔
1085
                        shift = -1;
×
1086
                }
1087
                else {
1088
                        // the carry && signs!= implies r2 is complement, result < r1, must find hidden bit (in the complement)
1089
                        for (int i = static_cast<int>(abits) - 1; i >= 0 && !sum[static_cast<unsigned>(i)]; --i) {
200,167✔
1090
                                shift++;
100,166✔
1091
                        }
1092
                }
1093
        }
1094
        assert(shift >= -1);
100,001✔
1095

1096
        if (shift >= static_cast<int>(abits)) {            // we have actual 0                            
100,001✔
1097
                sum.reset();
2✔
1098
                result.set(false, 0, sum, true, false, false);
2✔
1099
                return;
2✔
1100
        }
1101

1102
        scale_of_result -= shift;
99,999✔
1103
        const int hpos = static_cast<int>(abits) - 1 - shift;         // position of the hidden bit 
99,999✔
1104
        sum <<= (1ll + static_cast<uint64_t>(abits) - hpos);
99,999✔
1105
        if (_trace_value_sub) std::cout << (r1_sign ? "sign -1" : "sign  1") << " scale " << std::setw(3) << scale_of_result << " sum     " << sum << std::endl;
1106
        result.set(r1_sign, scale_of_result, sum, false, false, false);
99,999✔
1107
}
1108

1109
// subtract module using SUBTRACTOR: CURRENTLY BROKEN FOR UNKNOWN REASON
1110
template<unsigned fbits, unsigned abits>
1111
void module_subtract_BROKEN(const value<fbits>& lhs, const value<fbits>& rhs, value<abits + 1>& result) {
1112

1113
        if (lhs.isinf() || rhs.isinf()) {
1114
                result.setinf();
1115
                return;
1116
        }
1117
        int lhs_scale = lhs.scale(), rhs_scale = rhs.scale(), scale_of_result = std::max(lhs_scale, rhs_scale);
1118

1119
        // align the fractions
1120
        bitblock<abits> r1 = lhs.template nshift<abits>(lhs_scale - scale_of_result + 3);
1121
        bitblock<abits> r2 = rhs.template nshift<abits>(rhs_scale - scale_of_result + 3);
1122
        bool r1_sign = lhs.sign(), r2_sign = rhs.sign();
1123
        //bool signs_are_equal = r1_sign == r2_sign;
1124

1125
        if (r1_sign) r1 = twos_complement(r1);
1126
        if (r1_sign) r2 = twos_complement(r2);
1127

1128
        if (_trace_value_sub) {
1129
                std::cout << (r1_sign ? "sign -1" : "sign  1") << " scale " << std::setw(3) << scale_of_result << " r1       " << r1 << std::endl;
1130
                std::cout << (r2_sign ? "sign -1" : "sign  1") << " scale " << std::setw(3) << scale_of_result << " r2       " << r2 << std::endl;
1131
        }
1132

1133
        bitblock<abits + 1> difference;
1134
        const bool borrow = subtract_unsigned(r1, r2, difference);
1135

1136
        if (_trace_value_sub) std::cout << (r1_sign ? "sign -1" : "sign  1") << " borrow" << std::setw(3) << (borrow ? 1 : 0) << " diff    " << difference << std::endl;
1137

1138
        long shift = 0;
1139
        if (borrow) {   // we have a negative value result
1140
                difference = twos_complement(difference);
1141
        }
1142
        // find hidden bit
1143
        for (int i = abits - 1; i >= 0 && difference[i]; i--) {
1144
                shift++;
1145
        }
1146
        assert(shift >= -1);
1147

1148
        if (shift >= long(abits)) {            // we have actual 0                            
1149
                difference.reset();
1150
                result.set(false, 0, difference, true, false, false);
1151
                return;
1152
        }
1153

1154
        scale_of_result -= shift;
1155
        const int hpos = abits - 1 - shift;         // position of the hidden bit 
1156
        difference <<= abits - hpos + 1;
1157
        if (_trace_value_sub) std::cout << (borrow ? "sign -1" : "sign  1") << " scale " << std::setw(3) << scale_of_result << " result  " << difference << std::endl;
1158
        result.set(borrow, scale_of_result, difference, false, false, false);
1159
}
1160

1161
// multiply module
1162
template<unsigned fbits, unsigned mbits>
1163
void module_multiply(const value<fbits>& lhs, const value<fbits>& rhs, value<mbits>& result) {
100,002✔
1164
        static constexpr unsigned fhbits = fbits + 1;  // fraction + hidden bit
1165
        if (_trace_value_mul) std::cout << "lhs  " << to_triple(lhs) << std::endl << "rhs  " << to_triple(rhs) << std::endl;
1166

1167
        if (lhs.isinf() || rhs.isinf()) {
100,002✔
1168
                result.setinf();
×
1169
                return;
1✔
1170
        }
1171
        if (lhs.iszero() || rhs.iszero()) {
100,002✔
1172
                result.setzero();
1✔
1173
                return;
1✔
1174
        }
1175

1176
        bool new_sign = lhs.sign() ^ rhs.sign();
100,001✔
1177
        int new_scale = lhs.scale() + rhs.scale();
100,001✔
1178
        bitblock<mbits> result_fraction;
100,001✔
1179

1180
        if constexpr (fbits > 0) {
1181
                // fractions are without hidden bit, get_fixed_point adds the hidden bit back in
1182
                bitblock<fhbits> r1 = lhs.get_fixed_point();
100,001✔
1183
                bitblock<fhbits> r2 = rhs.get_fixed_point();
100,001✔
1184
                multiply_unsigned(r1, r2, result_fraction);
100,001✔
1185

1186
                if (_trace_value_mul) std::cout << "r1  " << r1 << std::endl << "r2  " << r2 << std::endl << "res " << result_fraction << std::endl;
1187
                // check if the radix point needs to shift
1188
                int shift = 2;
100,001✔
1189
                if (result_fraction.test(mbits - 1)) {
100,001✔
1190
                        shift = 1;
×
1191
                        if (_trace_value_mul) std::cout << " shift " << shift << std::endl;
1192
                        new_scale += 1;
×
1193
                }
1194
                result_fraction <<= static_cast<unsigned>(shift);    // shift hidden bit out        
100,001✔
1195
        }
1196
        else {   // posit<3,0>, <4,1>, <5,2>, <6,3>, <7,4> etc are pure sign and scale
1197
                // multiply the hidden bits together, i.e. 1*1: we know the answer a priori
1198
        }
1199
        if (_trace_value_mul) std::cout << "sign " << (new_sign ? "-1 " : " 1 ") << "scale " << new_scale << " fraction " << result_fraction << std::endl;
1200

1201
        result.set(new_sign, new_scale, result_fraction, false, false, false);
100,001✔
1202
}
1203

1204
// divide module
1205
template<unsigned fbits, unsigned divbits>
1206
void module_divide(const value<fbits>& lhs, const value<fbits>& rhs, value<divbits>& result) {
100,001✔
1207
        static constexpr unsigned fhbits = fbits + 1;  // fraction + hidden bit
1208
        if (_trace_value_div) std::cout << "lhs  " << to_triple(lhs) << std::endl << "rhs  " << to_triple(rhs) << std::endl;
1209

1210
        if (lhs.isinf() || rhs.isinf()) {
100,001✔
1211
                result.setinf();
×
1212
                return;
1✔
1213
        }
1214
        if (lhs.iszero() || rhs.iszero()) {
100,001✔
1215
                result.setzero();
1✔
1216
                return;
1✔
1217
        }
1218

1219
        bool new_sign = lhs.sign() ^ rhs.sign();
100,000✔
1220
        int new_scale = lhs.scale() - rhs.scale();
100,000✔
1221
        bitblock<divbits> result_fraction;
100,000✔
1222

1223
        if constexpr (fbits > 0) {
1224
                // fractions are without hidden bit, get_fixed_point adds the hidden bit back in
1225
                bitblock<fhbits> r1 = lhs.get_fixed_point();
100,000✔
1226
                bitblock<fhbits> r2 = rhs.get_fixed_point();
100,000✔
1227
                divide_with_fraction(r1, r2, result_fraction);
100,000✔
1228
                if (_trace_value_div) std::cout << "r1     " << r1 << std::endl << "r2     " << r2 << std::endl << "result " << result_fraction << std::endl << "scale  " << new_scale << std::endl;
1229
                // check if the radix point needs to shift
1230
                // radix point is at divbits - fhbits
1231
                int msb = static_cast<int>(divbits - fhbits);
100,000✔
1232
                int shift = fhbits;
100,000✔
1233
                if (!result_fraction.test(static_cast<unsigned>(msb))) {
100,000✔
1234
                        msb--; shift++;
99,863✔
1235
                        while (!result_fraction.test(static_cast<unsigned>(msb))) { // search for the first 1
99,863✔
1236
                                msb--; shift++;
×
1237
                        }
1238
                }
1239
                result_fraction <<= static_cast<unsigned>(shift);    // shift hidden bit out
100,000✔
1240
                new_scale -= (shift - static_cast<int>(fhbits));
100,000✔
1241
                if (_trace_value_div) std::cout << "shift  " << shift << std::endl << "result " << result_fraction << std::endl << "scale  " << new_scale << std::endl;;
1242
        }
1243
        else {   // posit<3,0>, <4,1>, <5,2>, <6,3>, <7,4> etc are pure sign and scale
1244
                         // no need to multiply the hidden bits together, i.e. 1*1: we know the answer a priori
1245
        }
1246
        if (_trace_value_div) std::cout << "sign " << (new_sign ? "-1 " : " 1 ") << "scale " << new_scale << " fraction " << result_fraction << std::endl;
1247

1248
        result.set(new_sign, new_scale, result_fraction, false, false, false);
100,000✔
1249
}
1250

1251
template<unsigned fbits>
1252
value<fbits> operator+(const value<fbits>& lhs, const value<fbits>& rhs) {
299,999✔
1253
        constexpr unsigned abits = fbits + 5;
299,999✔
1254
        value<abits+1> result;
299,999✔
1255
        module_add<fbits,abits>(lhs, rhs, result);
299,999✔
1256
        return result.template round_to<fbits>();
599,998✔
1257
}
1258
template<unsigned fbits>
1259
value<fbits> operator-(const value<fbits>& lhs, const value<fbits>& rhs) {
100,001✔
1260
        constexpr unsigned abits = fbits + 5;
100,001✔
1261
        value<abits+1> result;
100,001✔
1262
        module_subtract<fbits,abits>(lhs, rhs, result);
100,001✔
1263
        return result.template round_to<fbits>();
200,002✔
1264
}
1265
template<unsigned fbits>
1266
value<fbits> operator*(const value<fbits>& lhs, const value<fbits>& rhs) {
100,002✔
1267
        constexpr unsigned mbits = 2*fbits + 2;
100,002✔
1268
        value<mbits> result;
100,002✔
1269
        module_multiply(lhs, rhs, result);
100,002✔
1270
        return result.template round_to<fbits>();
200,004✔
1271
}
1272
template<unsigned fbits>
1273
value<fbits> operator/(const value<fbits>& lhs, const value<fbits>& rhs) {
100,001✔
1274
        constexpr unsigned divbits = 2 * fbits + 5;
100,001✔
1275
        value<divbits> result;
100,001✔
1276
        module_divide(lhs, rhs, result);
100,001✔
1277
        return result.template round_to<fbits>();
200,002✔
1278
}
1279
template<unsigned fbits>
1280
value<fbits> sqrt(const value<fbits>& a) {
100,000✔
1281
        return std::sqrt(double(a));
100,000✔
1282
}
1283

1284
}}} // namespace sw::universal::internal
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