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