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

stillwater-sc / universal / 21902983909

11 Feb 2026 11:18AM UTC coverage: 84.829%. First build
21902983909

Pull #482

github

web-flow
Merge 703924479 into 2fc0bfdf5
Pull Request #482: Implementation of Unum 2.0

392 of 463 new or added lines in 4 files covered. (84.67%)

36133 of 42595 relevant lines covered (84.83%)

6162846.93 hits per line

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

83.86
/include/sw/universal/number/unum2/unum2_impl.hpp
1
// Universal Number 2.0 implementation including SORN (Sets of Real Numbers).
2
//
3
// Copyright (C) 2017-2026 Stillwater Supercomputing, Inc.
4
// SPDX-License-Identifier: MIT
5

6
#pragma once
7

8

9
#ifndef UNUM2_USE_OP_MATRIX
10
#define UNUM2_USE_OP_MATRIX     1  // Use op matrix
11
#endif  // UNUM2_USE_OP_MATRIX
12

13

14
#include <universal/number/unum2/common.hpp>
15
#include <universal/number/unum2/unum2_fwd.hpp>
16

17
#include <iostream>
18
#include <sstream>
19
#include <cstdint>
20
#include <bitset>
21
#include <algorithm>
22
#include <cmath>
23

24
namespace sw { namespace universal {
25

26
template <size_t N>
27
static inline int _find_first(const std::bitset<N>& b)
2✔
28
{
29
    for (size_t i = 0; i < N; ++i) {
42✔
30
        if (b.test(i))
42✔
31
            return static_cast<int>(i);
2✔
32
    }
33

NEW
34
    return -1;
×
35
}
36

37
template <int... exacts>
38
class unum2<lattice<exacts...>> final {
39
private:
40
    using T = lattice<exacts...>;
41
    const T& _lattice;
42

43
    // SORN
44
    constexpr static uint64_t sorn_length = sizeof... (exacts) << 3;
45
    std::bitset<sorn_length> _sorn;  // empty
46

47
public:
48
    unum2(uint64_t index) : _lattice(T::instance()) {
138✔
49
        //uint64_tDo bitwise or with with half the lattice size. SORN index starts from 2's compliment. That is
50
        // the first bit in SORN represents 'inf' instead of '0'. Next SORN bit represents (inf, -en) and
51
        // so on so forth.
52
        _sorn = std::bitset<sorn_length>().set((index ^ _lattice._N_half) & _lattice._MASK);
138✔
53
    }
138✔
54

55
    unum2() : _lattice(T::instance()) {
2,116✔
56
        _sorn.reset();
2,116✔
57
    }
2,116✔
58

59
    unum2(const unum2<T>& num) : 
82✔
60
        _lattice(num._lattice),
82✔
61
        _sorn(num._sorn)
82✔
62
    {}
82✔
63

64
    unum2(unum2<T>&& num) noexcept :
109✔
65
        _lattice(num._lattice),
109✔
66
        _sorn(std::move(num._sorn))
109✔
67
    {}
109✔
68

69
    unum2<T>& operator = (const unum2<T>& num) {
68✔
70
        if (this != &num)
68✔
71
            _sorn = num._sorn;
68✔
72
        return *this;
68✔
73
    }
74

75
    unum2<T>& operator = (unum2<T>&& num) noexcept {
69✔
76
        if (this != &num)
69✔
77
            _sorn = std::move(num._sorn);
69✔
78
        return *this;
69✔
79
    }
80

81
    friend std::ostream& operator << (std::ostream& os, const unum2<T>& u) {
1✔
82
        std::ostringstream oss;
1✔
83

84
        int64_t left_bound = -1;
1✔
85
        bool bound = false;  // Series of continuous 1s in the SORN bitset
1✔
86
        bool written = false;  // Has something been written to sstream?
1✔
87
        for(uint64_t i = 0; i < u.sorn_length; i++) {
33✔
88
            if(u._sorn[i] == 1) {
32✔
89
                // If already bound, continue
90
                if(bound) continue;
14✔
91

92
                // Set left bound
93
                bound = true;
2✔
94
                left_bound = i;
2✔
95

96
                // If something has been written, that means there were other bounds. Add Union sign.
97
                if(written)
2✔
98
                    oss << " U ";
1✔
99
            } else {
100
                // Single bit bound. Can be exact or inexact.
101
                if(bound) {
18✔
102
                    // End bound
103
                    bound = false;
1✔
104
                    written = true;
1✔
105

106
                    if(left_bound == i - 1) {
1✔
107
                        // If inexact
NEW
108
                        if(left_bound & 0x01) {  // Check ubit
×
NEW
109
                            oss << "(" << u._lattice.get_exact(u._conv_idx(left_bound - 1))
×
NEW
110
                                << ", " << u._lattice.get_exact(u._conv_idx(left_bound + 1))
×
NEW
111
                                << ")";
×
112
                        } else {
NEW
113
                            if(left_bound == 0) { 
×
NEW
114
                                if(u._sorn[u._lattice._N - 1] != 1) 
×
NEW
115
                                    oss << "inf";
×
NEW
116
                                else written = false;
×
NEW
117
                            } else oss << u._lattice.get_exact(u._conv_idx(left_bound));
×
118
                        }
119
                    } else {  // Multiple bit bound
120
                              // Check if left_bound is inexact. If so, get previous exact.
121
                        if(left_bound & 0x01) {
1✔
NEW
122
                            left_bound--;
×
NEW
123
                            oss << "(";
×
124
                        } else oss << "[";
1✔
125

126
                        int64_t right_bound;
127
                        char brace = ']';
1✔
128
                        if((i - 1) & 0x01) {
1✔
129
                            right_bound = i;
1✔
130
                            brace = ')';
1✔
NEW
131
                        } else right_bound = i - 1;
×
132

133
                        oss << u._lattice.get_exact(u._conv_idx(left_bound)) << ", "
1✔
134
                            << u._lattice.get_exact(u._conv_idx(right_bound)) << brace;
1✔
135
                    }
136
                }
137
            }
138
        }
139

140
        // No bounds.
141
        if(left_bound == -1) 
1✔
NEW
142
            oss << "[EMPTY]";
×
143
        else if(bound == true && left_bound == 0)
1✔
NEW
144
            oss << "[EVERYTHING]";
×
145

146
        // Bit equal 0 code over again.
147
        else if(bound) {
1✔
148
            // Final bit should be 1 if there is a bound.
149
            int64_t i = u.sorn_length - 1;
1✔
150

151
            if(left_bound == i) {
1✔
152
                // Final bit index in SORN is always inexact
NEW
153
                oss << "(" << u._lattice.get_exact(u._conv_idx(i - 1));
×
154

155
                // If only the first SORN bit is set, that infers infinity is included.
NEW
156
                if(u._sorn[0] == 1)
×
NEW
157
                    oss << ", inf]";
×
NEW
158
                else oss << ", " << u._lattice.get_exact(u._conv_idx(i + 1)) << ")";
×
159
            } else {  // Multiple bit bound
160
                if(left_bound & 0x01) {
1✔
NEW
161
                    left_bound--;
×
NEW
162
                    oss << "(";
×
163
                } else oss << "[";
1✔
164

165
                int64_t right_bound;
166
                char brace = ')';
1✔
167
                if(u._sorn[0] == 1) { 
1✔
168
                    right_bound = 0;
1✔
169
                    brace = ']';
1✔
NEW
170
                } else right_bound = i + 1;
×
171

172
                oss << u._lattice.get_exact(u._conv_idx(left_bound)) << ", "
1✔
173
                    << u._lattice.get_exact(u._conv_idx(right_bound)) << brace;
1✔
174
            }
175
        }
176

177
        return (os << oss.str());
2✔
178
    }
1✔
179

180
    static unum2<T> empty() {
14✔
181
        unum2<T> res(0);
14✔
182
        res._sorn.reset();
14✔
183

184
        return res;
14✔
185
    }
186

NEW
187
    static unum2<T> everything() {
×
NEW
188
        unum2<T> res(0);
×
NEW
189
        res._sorn.set();
×
190

NEW
191
        return res;
×
192
    }
193

194
    template<typename TT>
195
    static unum2<T> from(TT value) {
11✔
196
        return unum2<T>(_from_index(value));
11✔
197
    }
198

199
    template<typename TT>
200
    static unum2<T> interval(TT a, TT b) {
15✔
201
        if(a == b) 
15✔
NEW
202
            return unum2<T>::from(a);
×
203
        
204
        uint64_t ai = unum2<T>::_from_index(a);
15✔
205
        uint64_t bi = unum2<T>::_from_index(b);
15✔
206
        
207
        if(a < b) return unum2<T>::_bound(ai, bi);
15✔
208
        return unum2<T>::_bound_inverse(ai, bi);
2✔
209
    }
210

211
    // Addition
212
    unum2<T> operator + (const unum2<T>& other) const {
2✔
213
        auto res = unum2<T>::empty();
2✔
214
        for(uint64_t i = 0; i < sorn_length; i++) {
66✔
215
            for(uint64_t j = 0; j < sorn_length; j++) {
2,112✔
216
                if(_sorn[i] == 1 && other._sorn[j] == 1)
2,048✔
217
                    res._sorn |= unum2<T>::_sumpoint(_conv_idx(i), _conv_idx(j), _lattice)._sorn;
30✔
218
            }
219
        }
220

221
        return res;
2✔
222
    }
223

224
    // SORN Concatenation or Union
225
    unum2<T> operator | (const unum2<T>& other) const {
4✔
226
        auto res = unum2<T>::empty();
4✔
227
        res._sorn = this->_sorn | other._sorn;
4✔
228
        return res;
4✔
229
    }
230

231
    // Multiplication
232
    unum2<T> operator * (const unum2<T>& other) const {
2✔
233
        auto res = unum2<T>::empty();
2✔
234
        for(uint64_t i = 0; i < sorn_length; i++) {
66✔
235
            for(uint64_t j = 0; j < sorn_length; j++) {
2,112✔
236
                if(_sorn[i] == 1 && other._sorn[j] == 1)
2,048✔
237
                    res._sorn |= unum2<T>::_mulpoint(_conv_idx(i), _conv_idx(j), _lattice)._sorn;
30✔
238
            }
239
        }
240

241
        return res;
2✔
242
    }
243

244
    // Negation
245
    unum2<T> operator - () const {
2✔
246
        auto res = unum2<T>::empty();
2✔
247
        for(uint64_t i = 0; i < sorn_length; i++) {
66✔
248
            if(_sorn[i] == 1) {
64✔
249
                // Horizontal invert
250
                uint64_t neg_idx = _horizontal_invert(_conv_idx(i), _lattice._MASK);
32✔
251
                res._sorn.set(_conv_idx(neg_idx));
32✔
252
            }
253
        }
254

255
        return res;
2✔
256
    }
257

258
    // Subtraction
259
    unum2<T> operator - (const unum2<T>& other) const {
1✔
260
        auto neg = -other;
1✔
261
        return this->unum2<T>::operator + (neg);
2✔
262
    }
263

264
    // Invert
265
    unum2<T> operator ~ () const {
2✔
266
        uint64_t msb_mask = _lattice._N >> 1;
2✔
267
        auto res = unum2<T>::empty();
2✔
268
        for(uint64_t i = 0; i < sorn_length; i++) {
66✔
269
            if(_sorn[i] == 1) {
64✔
270
                // Vertical invert
271
                uint64_t ix = _conv_idx(i);
32✔
272
                uint64_t msb_set = ix & msb_mask;
32✔
273
                uint64_t inverted_idx = ~ix & _lattice._MASK;
32✔
274

275
                if(!msb_set) inverted_idx &= (msb_mask - 1);
32✔
276
                else inverted_idx |= msb_set;
20✔
277
                inverted_idx = (inverted_idx + 1) & _lattice._MASK;
32✔
278

279
                res._sorn.set(_conv_idx(inverted_idx));
32✔
280
            }
281
        }
282

283
        return res;
2✔
284
    }
285

286
    unum2<T> operator / (const unum2<T>& other) const {
1✔
287
        auto inv = ~other;
1✔
288
        return this->unum2<T>::operator * (inv);
2✔
289
    }
290

291
    bool operator == (const unum2<T>& other) const {
10✔
292
        return this->_sorn == other._sorn;
10✔
293
    }
294

295
    // Raise to a power
296
    unum2<T> operator ^ (double n) const {
1✔
297
        auto res = unum2<T>::empty();
1✔
298
        for(uint64_t i = 0; i < sorn_length; i++) {
33✔
299
            if(_sorn[i] == 1)
32✔
300
                res._sorn |= unum2<T>::_powpoint(_conv_idx(i), n, _lattice)._sorn;
17✔
301
        }
302

303
        return res;
1✔
304
    }
305

306
    // Absolute
307
    unum2<T> abs() const {
1✔
308
        auto res = unum2<T>::empty();
1✔
309
        for(int i = 0; i < sorn_length; i++) {
33✔
310
            if(_sorn[i] == 1) {
32✔
311
                uint64_t idx = _conv_idx(i);
17✔
312

313
                // Negative point
314
                if(idx > _lattice._N_half)
17✔
315
                    // Horizontal invert
316
                    res._sorn.set(_conv_idx(_horizontal_invert(idx, _lattice._MASK)));
12✔
317
                else res._sorn.set(i);
5✔
318
            }
319
        }
320

321
        return res;
1✔
322
    }
323

324
    uint64_t _conv_idx(uint64_t idx) const {
298✔
325
        // In Unum2 SORN, 0 starts from _N_half, instead of 0 for compatibility
326
        // reasons. This function converts adjusted index (e.g. 15 -> 0) to
327
        // absolute index (e.g. 0 -> 0) and vice versa.
328
        return (idx ^ _lattice._N_half) & _lattice._MASK;
298✔
329
    }
330

331
private:
332
    template<typename TT>
333
    static uint64_t _from_index(TT value) {
145✔
334
        T lattice = T();
145✔
335
    
336
        if(!std::isfinite(value))
145✔
337
            return lattice._N >> 1;
6✔
338
        else if(value == 0)
139✔
339
            return 0;
8✔
340
        else if(value == 1)
131✔
341
            return lattice._N_quarter;
5✔
342
        else if(value == -1)
126✔
343
            return lattice._N_quarter * 3;
8✔
344

345
        TT absolute_value = std::abs(value);
118✔
346
        size_t exact_size = lattice._exacts.size();
118✔
347

348
        // Try exact values.
349
        int64_t index = -1;
118✔
350
        for(int64_t i = 1; i < lattice._exacts.size(); i++) {
357✔
351
            int e = lattice._exacts[i];
299✔
352
            
353
            if(absolute_value == static_cast<TT>(e)) {
299✔
354
                // Vertical flip
355
                index = (i << 1) + lattice._N_quarter;
21✔
356
                break;
21✔
357
            }
358
            else if(absolute_value == (1 / static_cast<TT>(e))) {
278✔
359
                index = lattice._N_quarter - (i << 1);
4✔
360
                break;
4✔
361
            }
362

363
            // No exacts :(
364
            // Compare bounds.
365
            if(absolute_value > static_cast<TT>(lattice._exacts[i - 1]) && 
472✔
366
            absolute_value < static_cast<TT>(e))
198✔
367
            {
368
                // Vertical flip.
369
                index = (i << 1) + lattice._N_quarter - 1;
31✔
370
                break;
31✔
371
            } else {
372
                TT reciprocal_right = 1 / static_cast<TT>(lattice._exacts[exact_size - i - 1]);
243✔
373
                TT reciprocal_left = 1 / static_cast<TT>(lattice._exacts[exact_size - i]);
243✔
374

375
                if(absolute_value > reciprocal_left && absolute_value < reciprocal_right) {
243✔
376
                    index = (i << 1) + 1;
4✔
377
                    break;
4✔
378
                }
379
            }
380
        }
381

382
        if(index == -1) {
118✔
383
            // Check (0, e1) or (en, inf) bounds.
384
            if(absolute_value > 0 && absolute_value < (1 / static_cast<TT>(lattice._exacts[exact_size - 1])))
58✔
385
                index = 1;
17✔
386
            else if(absolute_value > static_cast<TT>(lattice._exacts[exact_size - 1]))
41✔
387
                index = lattice._N_half - 1;
41✔
388
        }
389
        
390
        // Horizontal flip
391
        if(value < 0)
118✔
392
            index = ((~index & lattice._MASK) + 1) & lattice._MASK;
47✔
393
        
394
        return index & lattice._MASK;
118✔
395
    }
145✔
396

397
    static unum2<T> _bound(uint64_t a, uint64_t b) {
67✔
398
        // Given unum has to be points.
399
        // and a < b
400
        // Note: Unum 'a' is operated on, thus changes.
401
        
402
        if(a == b) 
67✔
403
            return a;
26✔
404

405
        auto au = unum2<T>(a);
41✔
406
        auto bu = unum2<T>(b);
41✔
407
        auto res = au._sorn;
41✔
408
        auto criterion = res;
41✔
409

410
        // If b is infinity
411
        if(b == au._lattice._N_half) {
41✔
412
            int shift_count = au._lattice._N - _find_first(au._sorn);
2✔
413
            while(shift_count--) {
26✔
414
                criterion <<= 1;
24✔
415
                res |= criterion;
24✔
416
            }
417

418
            res |= bu._sorn;
2✔
419
        } else {
420
            while(criterion != bu._sorn) {
347✔
421
                criterion <<= 1;
308✔
422
                res |= criterion;
308✔
423
            }
424
        }
425

426
        au._sorn = res;
41✔
427
        return au;
41✔
428
    }
429

430
    static unum2<T> _bound_inverse(uint64_t a, uint64_t b) {
2✔
431
        // When bound a > bound b
432
        auto res = unum2<T>::_bound(b, a);
2✔
433
        res._sorn = res._sorn ^ std::bitset<sorn_length>().set();
2✔
434
        // Lattice should move coutner-clockwise in this case, but including the bounded
435
        // unums.
436
        res._sorn.set(a ^ res._lattice._N_half).set(b ^ res._lattice._N_half);
2✔
437
        return res;
2✔
438
    }
439

440
    static unum2<T> _sumpoint(uint64_t i, uint64_t j, const T& lattice) {
38✔
441
        op_matrix<T>* op_mat;
442

443
        if(UNUM2_USE_OP_MATRIX) {
444
            op_mat = &T::op_matrix_instance();
38✔
445

446
            if(op_mat->has(i, j, OP_MATRIX_TYPE_ADD))
38✔
447
                return op_mat->get(i, j, OP_MATRIX_TYPE_ADD);
13✔
448
        }
449

450
        unum2<T> res;
25✔
451

452
        // i and j both represent infinity
453
        if(i == lattice._N_half && j == lattice._N_half) {
25✔
NEW
454
            res = unum2<T>::everything();
×
NEW
455
            if(UNUM2_USE_OP_MATRIX) op_mat->set(i, j, OP_MATRIX_TYPE_ADD, res);
×
NEW
456
            return res;
×
457
        }
458
        // i or j represent infinity
459
        else if(i == lattice._N_half || j == lattice._N_half) {
25✔
NEW
460
            res = unum2<T>(lattice._N_half);  // inf
×
NEW
461
            if(UNUM2_USE_OP_MATRIX) op_mat->set(i, j, OP_MATRIX_TYPE_ADD, res);
×
NEW
462
            return res;
×
463
        }
464
        // i represents 0
465
        else if(i == 0 || j == 0) {
25✔
466
            res = unum2<T>(j);
1✔
467
            if(UNUM2_USE_OP_MATRIX) op_mat->set(i, j, OP_MATRIX_TYPE_ADD, res);
1✔
468
            return res;
1✔
469
        }
470

471
        // is exact
472
        bool ie = !(i & 0x01);
24✔
473
        bool je = !(j & 0x01);
24✔
474

475
        double i_left;
476
        double i_right;
477
        double j_left;
478
        double j_right;
479

480
        if(ie && je) {
24✔
NEW
481
            res = unum2<T>::from(lattice.exactvalue(i & lattice._MASK) + lattice.exactvalue(j & lattice._MASK));
×
NEW
482
            if(UNUM2_USE_OP_MATRIX) op_mat->set(i, j, OP_MATRIX_TYPE_ADD, res);
×
NEW
483
            return res;
×
484
        } else {
485
            j_left = lattice.exactvalue((j - 1) & lattice._MASK);
24✔
486
            j_right = lattice.exactvalue((j + 1) & lattice._MASK);
24✔
487
        }
488

489
        if(ie) {  // only i is exact
24✔
490
            i_left = lattice.exactvalue(i & lattice._MASK);
8✔
491
            i_right = i_left;
8✔
492
        }
493
        // only j is exact
494
        else if(je) {
16✔
495
            res = _sumpoint(j, i, lattice);
8✔
496
            if(UNUM2_USE_OP_MATRIX) op_mat->set(i, j, OP_MATRIX_TYPE_ADD, res);
8✔
497
            return res;
8✔
498
        } else {
499
            // None is exact
500
            i_left = lattice.exactvalue((i - 1) & lattice._MASK);
8✔
501
            i_right = lattice.exactvalue((i + 1) & lattice._MASK);
8✔
502
        }
503

504
        uint64_t res_left_idx = unum2<T>::_from_index(i_left + j_left);
16✔
505
        uint64_t res_right_idx = unum2<T>::_from_index(i_right + j_right);
16✔
506

507
        res = unum2<T>::_bound(res_left_idx, res_right_idx);
16✔
508
        if(UNUM2_USE_OP_MATRIX) op_mat->set(i, j, OP_MATRIX_TYPE_ADD, res);
16✔
509
        return res;
16✔
510
    }
511

512
    static unum2<T> _mulpoint(uint64_t i, uint64_t j, const T& lattice) {
44✔
513
        op_matrix<T>* op_mat;
514

515
        if(UNUM2_USE_OP_MATRIX) {
516
            op_mat = &T::op_matrix_instance();
44✔
517

518
            if(op_mat->has(i, j, OP_MATRIX_TYPE_MUL)) {
44✔
519
                return op_mat->get(i, j, OP_MATRIX_TYPE_MUL);
1✔
520
            }
521
        }
522

523
        unum2<T> res;
43✔
524

525
        // inf * 0 = everything
526
        if((i == lattice._N_half && j == 0) || (i == 0 && j == lattice._N_half)) {
43✔
NEW
527
            res = unum2<T>::everything();
×
NEW
528
            if(UNUM2_USE_OP_MATRIX) op_mat->set(i, j, OP_MATRIX_TYPE_MUL, res);
×
NEW
529
            return res;
×
530
        }
531
        // inf * 1 = inf
532
        else if((i == lattice._N_half && j == lattice._N_quarter) || (i == lattice._N_quarter && j == lattice._N_half)) {
43✔
NEW
533
            res = unum2<T>(lattice._N_half);  // inf
×
NEW
534
            if(UNUM2_USE_OP_MATRIX) op_mat->set(i, j, OP_MATRIX_TYPE_MUL, res);
×
NEW
535
            return res;
×
536
        } 
537
        // i represents 1
538
        else if(i == lattice._N_quarter) {
43✔
NEW
539
            res = unum2<T>(j);
×
NEW
540
            if(UNUM2_USE_OP_MATRIX) op_mat->set(i, j, OP_MATRIX_TYPE_MUL, res);
×
NEW
541
            return res;
×
542
        }
543
        // j represents 1
544
        else if(j == lattice._N_quarter) {
43✔
NEW
545
            res = unum2<T>(i);
×
NEW
546
            if(UNUM2_USE_OP_MATRIX) op_mat->set(i, j, OP_MATRIX_TYPE_MUL, res);
×
NEW
547
            return res;
×
548
        } 
549
        // i represents 0
550
        else if(i == 0 || j == 0) {
43✔
551
            res = unum2<T>(0);
1✔
552
            if(UNUM2_USE_OP_MATRIX) op_mat->set(i, j, OP_MATRIX_TYPE_MUL, res);
1✔
553
            return res;
1✔
554
        } 
555

556
        // is exact
557
        bool ie = !(i & 0x01);
42✔
558
        bool je = !(j & 0x01);
42✔
559

560
        double i_left;
561
        double i_right;
562
        double j_left;
563
        double j_right;
564

565
        if(ie && je) {
42✔
NEW
566
            res = unum2<T>::from(lattice.exactvalue(i & lattice._MASK) * lattice.exactvalue(j & lattice._MASK));
×
NEW
567
            if(UNUM2_USE_OP_MATRIX) op_mat->set(i, j, OP_MATRIX_TYPE_MUL, res);
×
NEW
568
            return res;
×
569
        }
570
        else {
571
            j_left = lattice.exactvalue((j - 1) & lattice._MASK);
42✔
572
            j_right = lattice.exactvalue((j + 1) & lattice._MASK);
42✔
573
        }
574
            
575
        if(ie) {  // only i is exact
42✔
576
            i_left = lattice.exactvalue(i & lattice._MASK);
14✔
577
            i_right = i_left;
14✔
578
        }
579
        // only j is exact
580
        else if(je) {
28✔
581
            res = _mulpoint(j, i, lattice);
14✔
582
            if(UNUM2_USE_OP_MATRIX) op_mat->set(i, j, OP_MATRIX_TYPE_MUL, res);
14✔
583
            return res;
14✔
584
        } else {
585
            // None is exact
586
            i_left = lattice.exactvalue((i - 1) & lattice._MASK);
14✔
587
            i_right = lattice.exactvalue((i + 1) & lattice._MASK);
14✔
588
        }
589

590
        // candidates
591
        double candidates[] = { i_left * j_left, i_left * j_right, i_right * j_left, i_right * j_right };
28✔
592

593
        // check for NaNs. That should occur only when we encounter things like inf * 0. Return everything
594
        // in this case.
595
        for(int i = 0; i < 4; i++) {
140✔
596
            if(std::isnan(candidates[i])) {
112✔
NEW
597
                res = unum2<T>::everything();
×
NEW
598
                if(UNUM2_USE_OP_MATRIX) op_mat->set(i, j, OP_MATRIX_TYPE_MUL, res);
×
NEW
599
                return res;
×
600
            }    
601
        }
602

603
        double res_left = *std::min_element(candidates, candidates + 4);
28✔
604
        double res_right = *std::max_element(candidates, candidates + 4);
28✔
605
        uint64_t res_left_idx = unum2<T>::_from_index(res_left);
28✔
606
        uint64_t res_right_idx = unum2<T>::_from_index(res_right);
28✔
607

608
        if(!(res_left_idx & 0x01))  // exact
28✔
609
            res_left_idx = (res_left_idx + 1) & lattice._MASK;
10✔
610
        if(!(res_right_idx & 0x01))  // exact
28✔
611
            res_right_idx = (res_right_idx - 1) & lattice._MASK;
12✔
612

613
        res = unum2<T>::_bound(res_left_idx, res_right_idx);
28✔
614
        if(UNUM2_USE_OP_MATRIX) op_mat->set(i, j, OP_MATRIX_TYPE_MUL, res);
28✔
615
        return res;
28✔
616
    }
617

618
    static unum2<T> _powpoint(uint64_t i, double n, const T& lattice) {
17✔
619
        if(!(i & 0x01)) {
17✔
620
            double value = lattice.exactvalue(i);
9✔
621
            value = std::pow(value, n);
9✔
622

623
            // e.g. sqrt(-2) which will result complex number.
624
            if(std::isnan(value))
9✔
NEW
625
                return unum2<T>::empty();
×
626
            
627
            return unum2<T>::from(value);
9✔
628
        }
629

630
        double left_bound = lattice.exactvalue((i - 1) & lattice._MASK);
8✔
631
        double right_bound = lattice.exactvalue((i + 1) & lattice._MASK);
8✔
632

633
        left_bound = std::pow(left_bound, n);
8✔
634
        right_bound = std::pow(right_bound, n);
8✔
635
        if(std::isnan(left_bound) || std::isnan(right_bound)) 
8✔
NEW
636
            return unum2<T>::empty();
×
637

638
        uint64_t left_idx = unum2<T>::_from_index(std::min(left_bound, right_bound));
8✔
639
        uint64_t right_idx = unum2<T>::_from_index(std::max(left_bound, right_bound));
8✔
640

641
        if(!(left_idx & 0x01))  // exact
8✔
642
            left_idx = (left_idx + 1) & lattice._MASK;
2✔
643
        if(!(right_idx & 0x01))  // exact
8✔
644
            right_idx = (right_idx - 1) & lattice._MASK;
2✔
645

646
        return unum2<T>::_bound(left_idx, right_idx);
8✔
647
    }
648
};
649

650

651
template<typename T> unum2<T> pow(unum2<T> u, int n) {
652
    return u ^ n;
653
}
654

655
template<typename T> unum2<T> abs(unum2<T> u) {
656
    return u.abs();
657
}
658

659
}}
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