1 #pragma once
2 // quire.hpp: definition of a parameterized quire configurations for IEEE float/double/long double
3 //
4 // Copyright (C) 2017-2021 Stillwater Supercomputing, Inc.
5 //
6 // This file is part of the universal numbers project, which is released under an MIT Open Source license.
7 #include <universal/native/boolean_logic_operators.hpp>
8 #include <universal/internal/value/value.hpp>
9 #include <universal/number/quire/exceptions.hpp>
10
11 namespace sw::ieee {
12
13 // inject sw::universal namespaces into the public interface
14
15 using namespace sw::universal;
16 using namespace sw::universal::internal;
17
18 // template class representing a quire associated with an ieee float configuration
19 // capacity indicates the power of 2 number of accumulations the quire can support
20 template<size_t nbits, size_t es, size_t capacity = 30>
21 class quire {
22 public:
23 // fixed-point representation of a float multiply requires 1 + 2*(2^ebits + mbits), where ebits and mbits are the number of bits in exponent and mantissa, respectively
24 // type size ebits mbits exponent range capacity quire size total
25 // float 32 8 24 256 30 561 591
26 // double 64 11 53 2048 30 4203 4233
27 // lng dbl 128 15 113 32768 30 65763 65793
28
29 static constexpr size_t ebits = es;
30 static constexpr size_t mbits = nbits - es;
31 static constexpr size_t escale = 2*((size_t(1) << es) + mbits + 1);
32 static constexpr size_t range = escale; // dynamic range of the float configuration
33 static constexpr size_t half_range = range >> 1; // position of the fixed point
34 static constexpr size_t upper_range = half_range + 1; // size of the upper accumulator
35 static constexpr size_t qbits = range + capacity; // size of the quire minus the sign bit: we are managing the sign explicitly
36
quire()37 quire() : _sign(false) { _capacity.reset(); _upper.reset(); _lower.reset(); }
quire(int8_t initial_value)38 quire(int8_t initial_value) { *this = initial_value; }
quire(int16_t initial_value)39 quire(int16_t initial_value) { *this = initial_value; }
quire(int32_t initial_value)40 quire(int32_t initial_value) { *this = initial_value; }
quire(int64_t initial_value)41 quire(int64_t initial_value) { *this = initial_value; }
quire(uint64_t initial_value)42 quire(uint64_t initial_value) { *this = initial_value; }
quire(float initial_value)43 quire(float initial_value) { *this = initial_value; }
quire(double initial_value)44 quire(double initial_value) { *this = initial_value; }
45 template<size_t fbits>
quire(const sw::universal::internal::value<fbits> & rhs)46 quire(const sw::universal::internal::value<fbits>& rhs) { *this = rhs; } // TODO: an internal type in a public interface
47
48 // TODO: we are clamping the values of the RHS to be withing the dynamic range of the float
49 // TODO: however, on the upper side we also have the capacity bits, which gives us the opportunity to accept larger scale values than the dynamic range of the float.
50 // TODO: is that a good idea?
51 template<size_t fbits>
operator =(const value<fbits> & rhs)52 quire& operator=(const value<fbits>& rhs) {
53 reset();
54 _sign = rhs.sign();
55 int i,f, scale = rhs.scale();
56 if (scale > int(half_range)) {
57 throw operand_too_large_for_quire{};
58 }
59 if (scale < -int(half_range)) {
60 throw operand_too_small_for_quire{};
61 }
62 sw::universal::internal::bitblock<fbits+1> fraction = rhs.get_fixed_point();
63 // divide bits between upper and lower accumulator
64 if (scale - int(fbits) >= 0) {
65 // all upper accumulator
66 for (i = scale, f = int(fbits); i >= 0 && f >= 0; i--, f--) {
67 _upper[i] = fraction[f];
68 }
69 }
70 else if (scale < 0) {
71 // all lower accumulator
72 for (i = half_range + scale, f = int(fbits); i >= 0 && f >= 0; i--, f--) {
73 _lower[i] = fraction[f];
74 }
75 }
76 else {
77 // part upper, and part lower accumulator
78 // first assign the bits in the upper accumulator
79 for (i = scale, f = int(fbits); i >= 0 && f >= 0; i--, f--) {
80 _upper[i] = fraction[f];
81 }
82 // next assign the bits in the lower accumulator
83 for (i = half_range - 1; i >= 0 && f >= 0; i--, f--) {
84 _lower[i] = fraction[f];
85 }
86 }
87 return *this;
88 }
operator =(signed char rhs)89 quire& operator=(signed char rhs) {
90 *this = (long long)(rhs);
91 return *this;
92 }
operator =(short int rhs)93 quire& operator=(short int rhs) {
94 *this = (long long)(rhs);
95 return *this;
96 }
operator =(int rhs)97 quire& operator=(int rhs) {
98 *this = (long long)(rhs);
99 return *this;
100 }
operator =(long long rhs)101 quire& operator=(long long rhs) {
102 clear();
103 // transform to sign-magnitude
104 _sign = rhs & 0x8000000000000000;
105 unsigned long long magnitude;
106 magnitude = _sign ? -rhs : rhs;
107 unsigned msb = findMostSignificantBit(magnitude);
108 if (msb > half_range + capacity) {
109 throw operand_too_large_for_quire{};
110 }
111 else {
112 // copy the value into the quire
113 uint64_t mask = uint64_t(1);
114 for (size_t i = 0; i < msb && i < half_range; i++) {
115 _upper[i] = magnitude & mask;
116 mask <<= 1;
117 }
118 if (msb >= half_range) {
119 size_t c = 0;
120 for (size_t i = half_range; i < msb && i < half_range + capacity; i++, c++) {
121 _capacity[c] = magnitude & mask;
122 mask <<= 1;
123 }
124 }
125 }
126 return *this;
127 }
operator =(long long unsigned rhs)128 quire& operator=(long long unsigned rhs) {
129 reset();
130 unsigned msb = findMostSignificantBit(rhs);
131 if (msb > half_range + capacity) {
132 throw operand_too_large_for_quire{};
133 }
134 else {
135 // copy the value into the quire
136 uint64_t mask = uint64_t(1);
137 for (size_t i = 0; i < msb && i < half_range; i++) {
138 _upper[i] = rhs & mask;
139 mask <<= 1;
140 }
141 if (msb >= half_range) {
142 size_t c = 0;
143 for (size_t i = half_range; i < msb && i < half_range + capacity; i++, c++) {
144 _capacity[c] = rhs & mask;
145 mask <<= 1;
146 }
147 }
148 }
149 return *this;
150 }
operator =(float rhs)151 quire& operator=(float rhs) {
152 constexpr int bits = std::numeric_limits<float>::digits - 1;
153 *this = sw::universal::internal::value<bits>(rhs);
154 return *this;
155 }
operator =(double rhs)156 quire& operator=(double rhs) {
157 constexpr int bits = std::numeric_limits<double>::digits - 1;
158 *this = sw::universal::internal::value<bits>(rhs);
159 return *this;
160 }
operator =(long double rhs)161 quire& operator=(long double rhs) {
162 constexpr int bits = std::numeric_limits<long double>::digits - 1;
163 *this = sw::universal::internal::value<bits>(rhs);
164 return *this;
165 }
166
167 template<size_t fbits>
operator +=(const value<fbits> & rhs)168 quire& operator+=(const value<fbits>& rhs) {
169 if (rhs.iszero()) return *this;
170 int i, f, scale = rhs.scale();
171 if (scale > int(half_range)) {
172 throw operand_too_large_for_quire{};
173 }
174 if (scale < -int(half_range)) {
175 throw operand_too_small_for_quire{};
176 }
177 if (rhs.sign()) { // subtract
178 // lsb in the quire of the lowest bit of the explicit fixed point value including the hidden bit of the fraction
179 int lsb = scale - int(fbits);
180 bool borrow = false;
181 bitblock<fbits + 1> fraction = rhs.get_fixed_point();
182 // divide bits between upper and lower accumulator
183 if (scale < 0) { // all lower accumulator
184 int lsb = int(half_range) + scale - int(fbits);
185 int qlsb = lsb > 0 ? lsb : 0;
186 int flsb = lsb >= 0 ? 0 : -lsb;
187 for (i = qlsb, f = flsb; i < int(half_range) && f <= int(fbits); i++, f++) {
188 bool _a = _lower[i];
189 bool _b = fraction[f];
190 _lower[i] = bxor(_a, _b, borrow);
191 borrow = (!_a && _b) || (bxnor(!_a, !_b) && borrow);
192 }
193 // propagate any borrows to the end of the lower accumulator
194 while (borrow && i < int(half_range)) {
195 bool _a = _lower[i];
196 _lower[i] = bxor(_a, borrow);
197 borrow = borrow && !_a;
198 i++;
199 }
200 if (borrow) { // borrow propagate to the _upper accumulator
201 // need to decrement the _upper
202 i = 0;
203 while (borrow && i < int(upper_range)) {
204 bool _a = _upper[i];
205 _upper[i] = bxor(_a, borrow);
206 borrow = borrow && !_a;
207 i++;
208 }
209 if (borrow) {
210 // propagate the borrow into the capacity segment
211 i = 0;
212 while (borrow && i < int(capacity)) {
213 bool _a = _capacity[i];
214 _capacity[i] = bxor(_a, borrow);
215 borrow = borrow && !_a;
216 i++;
217 }
218 }
219 }
220 }
221 else if (lsb >= 0) { // all upper accumulator
222 for (i = lsb, f = 0; i <= scale && f <= int(fbits); i++, f++) {
223 bool _a = _upper[i];
224 bool _b = fraction[f];
225 _upper[i] = sw::universal::bxor(_a, _b, borrow);
226 borrow = (!_a && _b) || (sw::universal::bxnor(!_a, !_b) && borrow);
227 }
228 // propagate any borrows to the end of the upper accumulator
229 while (borrow && i < int(upper_range)) {
230 bool _a = _upper[i];
231 _upper[i] = sw::universal::bxor(_a, borrow);
232 borrow = borrow && !_a;
233 i++;
234 }
235 if (borrow) {
236 // propagate the borrow into the capacity segment
237 i = 0;
238 while (borrow && i < int(capacity)) {
239 bool _a = _capacity[i];
240 _capacity[i] = sw::universal::bxor(_a, borrow);
241 borrow = borrow && !_a;
242 i++;
243 }
244 }
245 }
246 else { // lsb < 0 && scale >= 0
247 // part upper, and part lower accumulator
248 // first add the lower accumulator component
249 lsb = int(half_range) + lsb; // remember lsb is negative in this block
250 int qlsb = lsb > 0 ? lsb : 0;
251 int flsb = lsb >= 0 ? 0 : -lsb;
252 for (i = qlsb, f = flsb; i < int(half_range) && f <= int(fbits); i++, f++) {
253 bool _a = _lower[i];
254 bool _b = fraction[f];
255 _lower[i] = sw::universal::bxor(_a, _b, borrow);
256 borrow = (!_a && _b) || (sw::universal::bxnor(!_a, !_b) && borrow);
257 }
258 // next add the bits in the upper accumulator
259 for (i = 0; i <= scale && f <= int(fbits); i++, f++) {
260 bool _a = _upper[i];
261 bool _b = fraction[f];
262 _upper[i] = sw::universal::bxor(_a, _b, borrow);
263 borrow = (!_a && _b) || (sw::universal::bxnor(!_a, !_b) && borrow);
264 }
265 // propagate any borrows to the end of the upper accumulator
266 while (borrow && i < int(upper_range)) {
267 bool _a = _upper[i];
268 _upper[i] = _a ^ borrow;
269 borrow = borrow && !_a;
270 i++;
271 }
272 if (borrow) {
273 // propagate the borrow into the capacity segment
274 i = 0;
275 while (borrow && i < int(capacity)) {
276 bool _a = _capacity[i];
277 _capacity[i] = _a ^ borrow;
278 borrow = borrow && !_a;
279 i++;
280 }
281 }
282 }
283 }
284 else { // add
285 // scale is the location of the msb in the fixed point representation
286 // so scale = 0 is the hidden bit at location 0, scale 1 = bit 1, etc.
287 // and scale = -1 is the first bit of the fraction
288 // we manage scale >= 0 in the _upper accumulator, and scale < 0 in the _lower accumulator
289 int lsb = scale - int(fbits);
290 bool carry = false;
291 bitblock<fbits + 1> fraction = rhs.get_fixed_point();
292 // divide bits between upper and lower accumulator
293 if (scale < 0) { // all lower accumulator
294 int lsb = int(half_range) + scale - int(fbits);
295 int qlsb = lsb > 0 ? lsb : 0;
296 int flsb = lsb >= 0 ? 0 : -lsb;
297 for (i = qlsb, f = flsb; i < int(half_range) && f <= int(fbits); i++, f++) {
298 bool _a = _lower[i];
299 bool _b = fraction[f];
300 _lower[i] = sw::universal::bxor(_a, _b, carry);
301 carry = (_a && _b) || (carry && sw::universal::bxor(_a, _b));
302 }
303 // propagate any carries to the end of the lower accumulator
304 while (carry && i < int(half_range)) {
305 bool _a = _lower[i];
306 _lower[i] = _a ^ carry;
307 carry = carry && _a;
308 i++;
309 }
310 if (carry) { // carry propagate to the _upper accumulator
311 // need to increment the _upper
312 i = 0;
313 while (carry && i < int(upper_range)) {
314 bool _a = _upper[i];
315 _upper[i] = _a ^ carry;
316 carry = carry && _a;
317 i++;
318 }
319 if (carry) {
320 // next add the bits to the capacity segment
321 i = 0;
322 while (carry && i < int(capacity)) {
323 bool _a = _capacity[i];
324 _capacity[i] = _a ^ carry;
325 carry = carry && _a;
326 i++;
327 }
328 }
329 }
330 }
331 else if (lsb >= 0) { // all upper accumulator
332 for (i = lsb, f = 0; i <= scale && f <= int(fbits); i++, f++) {
333 bool _a = _upper[i];
334 bool _b = fraction[f];
335 _upper[i] = sw::universal::bxor(_a, _b, carry);
336 carry = (_a && _b) || (carry && sw::universal::bxor(_a, _b));
337 }
338 while (carry && i < int(upper_range)) {
339 bool _a = _upper[i];
340 _upper[i] = _a ^ carry;
341 carry = carry && _a;
342 i++;
343 }
344 if (carry) {
345 // next add the bits to the capacity segment
346 i = 0;
347 while (carry && i < int(capacity)) {
348 bool _a = _capacity[i];
349 _capacity[i] = _a ^ carry;
350 carry = carry && _a;
351 i++;
352 }
353 }
354 }
355 else { // lsb < 0 && scale > 0
356 // part upper, and part lower accumulator
357 // first add the lower accumulator component
358 lsb = int(half_range) + lsb; // remember lsb is negative in this block
359 int qlsb = lsb > 0 ? lsb : 0;
360 int flsb = lsb >= 0 ? 0 : -lsb;
361 for (i = qlsb, f = flsb; i < int(half_range) && f <= int(fbits); i++, f++) {
362 bool _a = _lower[i];
363 bool _b = fraction[f];
364 _lower[i] = sw::universal::bxor(_a, _b, carry);
365 carry = (_a && _b) || (carry && sw::universal::bxor(_a, _b));
366 }
367 // next add the bits in the upper accumulator
368 for (i = 0; i <= scale && f <= int(fbits); i++, f++) {
369 bool _a = _upper[i];
370 bool _b = fraction[f];
371 _upper[i] = sw::universal::bxor(_a, _b, carry);
372 carry = (_a && _b) || (carry && sw::universal::bxor(_a, _b));
373 }
374 // propagate any carries to the end of the upper accumulator
375 while (carry && i < int(upper_range)) {
376 bool _a = _upper[i];
377 _upper[i] = _a ^ carry;
378 carry = carry && _a;
379 i++;
380 }
381 // next add the bits to the capacity segment
382 if (carry) {
383 i = 0;
384 while (carry && i < int(capacity)) {
385 bool _a = _capacity[i];
386 _capacity[i] = _a ^ carry;
387 carry = carry && _a;
388 i++;
389 }
390 }
391 }
392 }
393 return *this;
394 }
395 // reset the state of a quire to zero
reset()396 void reset() {
397 _sign = false;
398 _lower.reset();
399 _upper.reset();
400 _capacity.reset();
401 }
402 // clear the state of a quire to zero
clear()403 void clear() { reset(); }
dynamic_range() const404 int dynamic_range() const { return range; }
radix_point() const405 int radix_point() const { return half_range; }
max_scale() const406 int max_scale() const { return half_range; }
min_scale() const407 int min_scale() const { return -int(half_range); }
capacity_range() const408 int capacity_range() const { return capacity; }
isneg() const409 bool isneg() const { return _sign; }
ispos() const410 bool ispos() const { return !_sign; }
iszero() const411 bool iszero() const { return _capacity.none() && _upper.none() && _lower.none(); }
412
413 // Return value of the sign bit: true indicates a negative number, false a positive number or zero
get_sign() const414 bool get_sign() const { return _sign; }
sign_value() const415 float sign_value() const { return (_sign ? -1.0 : 1.0); }
to_value() const416 value<qbits> to_value() const {
417 // find the MSB and build the fraction
418 bitblock<qbits> fraction;
419 bool isZero = false;
420 bool isNaR = false; // TODO
421 int i;
422 int qbit = int(qbits);
423 int fbit = qbit - 1;
424 int scale = qbits;
425 for (i = int(capacity) - 1; i >= 0; i--, qbit--) {
426 if (scale == qbits) {
427 if (_capacity.test(i)) scale = qbit - int(half_range);
428 }
429 else {
430 fraction[fbit--] = _capacity[i];
431 }
432 }
433 for (i = int(upper_range) - 1; i >= 0; i--, qbit--) {
434 if (scale == qbits) {
435 if (_upper.test(i)) scale = qbit - int(half_range);
436 }
437 else {
438 fraction[fbit--] = _upper[i];
439 }
440 }
441 for (i = int(half_range) - 1; i >= 0; i--, qbit--) {
442 if (scale == qbits) {
443 if (_lower.test(i)) scale = qbit - int(half_range);
444 }
445 else {
446 fraction[fbit--] = _lower[i];
447 }
448 }
449 if (scale == qbits) {
450 isZero = true;
451 scale = 0;
452 }
453 return value<qbits>(_sign, scale, fraction, isZero, isNaR);
454 }
455
456 private:
457 bool _sign;
458 // segmented accumulator to demonstrate potential hw concurrency for high performance quires
459 bitblock<half_range> _lower;
460 bitblock<upper_range> _upper;
461 bitblock<capacity> _capacity;
462
463 #if TEMPLATIZED_TYPE
464 // when we figure out how to templatize the extraction of exponent bits from type
465
466 // template parameters need names different from class template parameters (for gcc and clang)
467 template<typename TTy, size_t ncapacity>
468 friend std::ostream& operator<< (std::ostream& ostr, const quire<TTy,ncapacity>& q);
469 template<typename TTy, size_t ncapacity>
470 friend std::istream& operator>> (std::istream& istr, quire<TTy, ncapacity>& q);
471
472 template<size_t nnbits, size_t nes, size_t ncapacity>
473 friend bool operator==(const quire<TTy, ncapacity>& lhs, const quire<TTy, ncapacity>& rhs);
474 template<typename TTy, size_t ncapacity>
475 friend bool operator!=(const quire<TTy, ncapacity>& lhs, const quire<TTy, ncapacity>& rhs);
476 template<typename TTy, size_t ncapacity>
477 friend bool operator< (const quire<TTy, ncapacity>& lhs, const quire<TTy, ncapacity>& rhs);
478 template<typename TTy, size_t ncapacity>
479 friend bool operator> (const quire<TTy, ncapacity>& lhs, const quire<TTy, ncapacity>& rhs);
480 template<typename TTy, size_t ncapacity>
481 friend bool operator<=(const quire<TTy, ncapacity>& lhs, const quire<TTy, ncapacity>& rhs);
482 template<typename TTy, size_t ncapacity>
483 friend bool operator>=(const quire<TTy, ncapacity>& lhs, const quire<TTy, ncapacity>& rhs);
484
485 #else
486
487 // template parameters need names different from class template parameters (for gcc and clang)
488 template<size_t nnbits, size_t nes, size_t ncapacity>
489 friend std::ostream& operator<< (std::ostream& ostr, const quire<nnbits, nes, ncapacity>& q);
490 template<size_t nnbits, size_t nes, size_t ncapacity>
491 friend std::istream& operator>> (std::istream& istr, quire<nnbits, nes, ncapacity>& q);
492
493 template<size_t nnbits, size_t nes, size_t ncapacity>
494 friend bool operator==(const quire<nnbits, nes, ncapacity>& lhs, const quire<nnbits, nes, ncapacity>& rhs);
495 template<size_t nnbits, size_t nes, size_t ncapacity>
496 friend bool operator!=(const quire<nnbits, nes, ncapacity>& lhs, const quire<nnbits, nes, ncapacity>& rhs);
497 template<size_t nnbits, size_t nes, size_t ncapacity>
498 friend bool operator< (const quire<nnbits, nes, ncapacity>& lhs, const quire<nnbits, nes, ncapacity>& rhs);
499 template<size_t nnbits, size_t nes, size_t ncapacity>
500 friend bool operator> (const quire<nnbits, nes, ncapacity>& lhs, const quire<nnbits, nes, ncapacity>& rhs);
501 template<size_t nnbits, size_t nes, size_t ncapacity>
502 friend bool operator<=(const quire<nnbits, nes, ncapacity>& lhs, const quire<nnbits, nes, ncapacity>& rhs);
503 template<size_t nnbits, size_t nes, size_t ncapacity>
504 friend bool operator>=(const quire<nnbits, nes, ncapacity>& lhs, const quire<nnbits, nes, ncapacity>& rhs);
505
506 #endif // TEMPLATIZED_TYPE
507 };
508
509 #if TEMPLATIZED_TYPE
510
511 // QUIRE BINARY ARITHMETIC OPERATORS
512 template<typename Ty, size_t capacity>
operator +(const quire<Ty,capacity> & lhs,const quire<Ty,capacity> & rhs)513 inline quire<Ty, capacity> operator+(const quire<Ty, capacity>& lhs, const quire<Ty, capacity>& rhs) {
514 quire<Ty, capacity> sum = lhs;
515 sum += rhs;
516 return sum;
517 }
518
519
520 ////////////////// QUIRE operators
521 template<typename Ty, size_t capacity>
operator <<(std::ostream & ostr,const quire<Ty,capacity> & q)522 inline std::ostream& operator<<(std::ostream& ostr, const quire<Ty, capacity>& q) {
523 ostr << (q._sign ? "-1" : " 1") << ": " << q._capacity << "_" << q._upper << "." << q._lower;
524 return ostr;
525 }
526
527 template<typename Ty, size_t capacity>
operator >>(std::istream & istr,const quire<Ty,capacity> & q)528 inline std::istream& operator>> (std::istream& istr, const quire<Ty, capacity>& q) {
529 istr >> q._accu;
530 return istr;
531 }
532
533 template<typename Ty, size_t capacity>
operator ==(const quire<Ty,capacity> & lhs,const quire<Ty,capacity> & rhs)534 inline bool operator==(const quire<Ty, capacity>& lhs, const quire<Ty, capacity>& rhs) { return lhs._sign == rhs._sign && lhs._capacity == rhs._capacity && lhs._upper == rhs._upper && lhs._lower == rhs._lower; }
535 template<typename Ty, size_t capacity>
operator !=(const quire<Ty,capacity> & lhs,const quire<Ty,capacity> & rhs)536 inline bool operator!=(const quire<Ty, capacity>& lhs, const quire<Ty, capacity>& rhs) { return !operator==(lhs, rhs); }
537 template<typename Ty, size_t capacity>
operator <(const quire<Ty,capacity> & lhs,const quire<Ty,capacity> & rhs)538 inline bool operator< (const quire<Ty, capacity>& lhs, const quire<Ty, capacity>& rhs) {
539 bool bSmaller = false;
540 if (!lhs._sign && rhs._sign) {
541 bSmaller = true;
542 }
543 else if (lhs._sign == rhs._sign) {
544 if (lhs._capacity < rhs._capacity) {
545 bSmaller = true;
546 }
547 else if (lhs._capacity == rhs._capacity && lhs._upper < rhs._upper) {
548 bSmaller = true;
549 }
550 else if (lhs._capacity == rhs._capacity && lhs._upper == rhs._upper && lhs._lower < rhs._lower) {
551 bSmaller = true;
552 }
553 }
554 return bSmaller;
555 }
556 template<typename Ty, size_t capacity>
operator >(const quire<Ty,capacity> & lhs,const quire<Ty,capacity> & rhs)557 inline bool operator> (const quire<Ty, capacity>& lhs, const quire<Ty, capacity>& rhs) { return operator< (rhs, lhs); }
558 template<typename Ty, size_t capacity>
operator <=(const quire<Ty,capacity> & lhs,const quire<Ty,capacity> & rhs)559 inline bool operator<=(const quire<Ty, capacity>& lhs, const quire<Ty, capacity>& rhs) { return !operator> (lhs, rhs) || lhs == rhs; }
560 template<typename Ty, size_t capacity>
operator >=(const quire<Ty,capacity> & lhs,const quire<Ty,capacity> & rhs)561 inline bool operator>=(const quire<Ty, capacity>& lhs, const quire<Ty, capacity>& rhs) { return !operator< (lhs, rhs) || lhs == rhs; }
562
563 #else
564
565 // QUIRE BINARY ARITHMETIC OPERATORS
566 template<size_t nbits, size_t es, size_t capacity>
operator +(const quire<nbits,es,capacity> & lhs,const quire<nbits,es,capacity> & rhs)567 inline quire<nbits, es, capacity> operator+(const quire<nbits, es, capacity>& lhs, const quire<nbits, es, capacity>& rhs) {
568 quire<nbits, es, capacity> sum = lhs;
569 sum += rhs;
570 return sum;
571 }
572
573
574 ////////////////// QUIRE operators
575 template<size_t nnbits, size_t nes, size_t capacity>
operator <<(std::ostream & ostr,const quire<nnbits,nes,capacity> & q)576 inline std::ostream& operator<<(std::ostream& ostr, const quire<nnbits, nes, capacity>& q) {
577 ostr << (q._sign ? "-1" : " 1") << ": " << q._capacity << "_" << q._upper << "." << q._lower;
578 return ostr;
579 }
580
581 template<size_t nnbits, size_t nes, size_t capacity>
operator >>(std::istream & istr,const quire<nnbits,nes,capacity> & q)582 inline std::istream& operator>> (std::istream& istr, const quire<nnbits, nes, capacity>& q) {
583 istr >> q._accu;
584 return istr;
585 }
586
587 template<size_t nbits, size_t es, size_t capacity>
operator ==(const quire<nbits,es,capacity> & lhs,const quire<nbits,es,capacity> & rhs)588 inline bool operator==(const quire<nbits, es, capacity>& lhs, const quire<nbits, es, capacity>& rhs) { return lhs._sign == rhs._sign && lhs._capacity == rhs._capacity && lhs._upper == rhs._upper && lhs._lower == rhs._lower; }
589 template<size_t nbits, size_t es, size_t capacity>
operator !=(const quire<nbits,es,capacity> & lhs,const quire<nbits,es,capacity> & rhs)590 inline bool operator!=(const quire<nbits, es, capacity>& lhs, const quire<nbits, es, capacity>& rhs) { return !operator==(lhs, rhs); }
591 template<size_t nbits, size_t es, size_t capacity>
operator <(const quire<nbits,es,capacity> & lhs,const quire<nbits,es,capacity> & rhs)592 inline bool operator< (const quire<nbits, es, capacity>& lhs, const quire<nbits, es, capacity>& rhs) {
593 bool bSmaller = false;
594 if (!lhs._sign && rhs._sign) {
595 bSmaller = true;
596 }
597 else if (lhs._sign == rhs._sign) {
598 if (lhs._capacity < rhs._capacity) {
599 bSmaller = true;
600 }
601 else if (lhs._capacity == rhs._capacity && lhs._upper < rhs._upper) {
602 bSmaller = true;
603 }
604 else if (lhs._capacity == rhs._capacity && lhs._upper == rhs._upper && lhs._lower < rhs._lower) {
605 bSmaller = true;
606 }
607 }
608 return bSmaller;
609 }
610 template<size_t nbits, size_t es, size_t capacity>
operator >(const quire<nbits,es,capacity> & lhs,const quire<nbits,es,capacity> & rhs)611 inline bool operator> (const quire<nbits, es, capacity>& lhs, const quire<nbits, es, capacity>& rhs) { return operator< (rhs, lhs); }
612 template<size_t nbits, size_t es, size_t capacity>
operator <=(const quire<nbits,es,capacity> & lhs,const quire<nbits,es,capacity> & rhs)613 inline bool operator<=(const quire<nbits, es, capacity>& lhs, const quire<nbits, es, capacity>& rhs) { return !operator> (lhs, rhs) || lhs == rhs; }
614 template<size_t nbits, size_t es, size_t capacity>
operator >=(const quire<nbits,es,capacity> & lhs,const quire<nbits,es,capacity> & rhs)615 inline bool operator>=(const quire<nbits, es, capacity>& lhs, const quire<nbits, es, capacity>& rhs) { return !operator< (lhs, rhs) || lhs == rhs; }
616 #endif
617
618 } // namespace sw::ieee
619