1 #pragma once
2 // exponent.hpp: definition of a posit exponent
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 
8 namespace sw { namespace universal {
9 
10 static constexpr int GEOMETRIC_ROUND_DOWN   = -2;
11 static constexpr int ARITHMETIC_ROUND_DOWN  = -1;
12 static constexpr int NO_ADDITIONAL_ROUNDING =  0;
13 static constexpr int ARITHMETIC_ROUND_UP    =  1;
14 static constexpr int GEOMETRIC_ROUND_UP     =  2;
15 static constexpr int ARITHMETIC_ROUNDING    =  5;
16 
17 // exponent
18 template<size_t nbits, size_t es>
19 class exponent {
20 public:
exponent()21 	exponent() : _NrOfBits(0) {}
22 
23 	exponent(const exponent& r) = default;
24 	exponent(exponent&& r) = default;
25 
26 	exponent& operator=(const exponent& r) = default;
27 	exponent& operator=(exponent&& r) = default;
28 
reset()29 	void reset() {
30 		_NrOfBits = 0;
31 		_Bits.reset();
32 	}
setzero()33 	void setzero() { reset(); }
nrBits() const34 	size_t nrBits() const {
35 		return _NrOfBits;
36 	}
scale() const37 	int scale() const {
38 		return int(_Bits.to_ulong());
39 	}
value() const40 	long double value() const {
41 		return (long double)(uint64_t(1) << scale());
42 	}
get() const43 	bitblock<es> get() const {
44 		return _Bits;
45 	}
set(const bitblock<es> & raw,size_t nrExponentBits)46 	void set(const bitblock<es>& raw, size_t nrExponentBits) {
47 		_Bits = raw;
48 		_NrOfBits = nrExponentBits;
49 	}
50 
51 	// extract the exponent bits given a pattern and the location of the starting point
extract_exponent_bits(const bitblock<nbits> & _raw_bits,size_t nrRegimeBits)52 	void extract_exponent_bits(const bitblock<nbits>& _raw_bits, size_t nrRegimeBits) {
53 		_Bits.reset();
54 		// start of exponent is nbits - (sign_bit + regime_bits)
55 		int msb = int(static_cast<int>(nbits) - 1ull - (1ull + nrRegimeBits));
56 		if (es > 0) {
57 			size_t nrExponentBits = 0;
58 			bitblock<es> _exp;
59 			if (msb >= 0 && es > 0) {
60 				nrExponentBits = (static_cast<size_t>(msb) >= es - 1ull ? es : static_cast<size_t>(msb) + 1ull);
61 				for (size_t i = 0; i < nrExponentBits; i++) {
62 					_exp[es - 1 - i] = _raw_bits[msb - i];
63 				}
64 			}
65 			set(_exp, nrExponentBits);
66 		}
67 	}
68 
69 	// calculate the exponent given a number's scale: esval = Mod[scale, 2^es];
70 	// DEPRECATED
_assign(int scale)71 	void _assign(int scale) {
72 		_Bits.reset();
73 		unsigned int my_exponent = (scale < 0) ? (-scale >> es) : (scale >> es);
74 		// convert value into bitset
75 		uint32_t mask = uint32_t(1);  // es will be small, so pick a single word sized mask for efficiency
76 		for (unsigned i = 0; i < es; i++) {
77 			_Bits[i] = my_exponent & mask;
78 			mask <<= 1;
79 		}
80 	}
81 	// calculate the exponent given a number's scale and the number of regime bits,
82 	// returning an indicator which type of rounding is required to complete the posit
83 	// DEPRECATED
assign_exponent_bits(int scale,int k,size_t nrRegimeBits)84 	int assign_exponent_bits(int scale, int k, size_t nrRegimeBits) {
85 		int rounding_mode = NO_ADDITIONAL_ROUNDING;
86 		_Bits.reset();
87 		// we need to get to an adjusted scale that encodes regime and exponent
88 		// value scale = useed ^ k * 2 ^ exponent = 2^(k*2^es) * 2^e -> k*2^es + e
89 		// e = scale - k*2^es
90 		int raw = scale - k*(1 << es);
91 		size_t my_exponent = raw < 0 ? -raw : raw;
92 		// convert value into bitset
93 		size_t mask = 0x1;
94 		for (unsigned i = 0; i < es; i++) {
95 			_Bits[i] = my_exponent & mask;
96 			mask <<= 1;
97 		}
98 		_NrOfBits = (nbits - 1 - nrRegimeBits > es ? es : nbits - 1 - nrRegimeBits);
99 		if (_NrOfBits > 0) {
100 			if (_NrOfBits < es) {
101 				rounding_mode = _Bits[es - 1 - _NrOfBits] ? GEOMETRIC_ROUND_UP : GEOMETRIC_ROUND_DOWN; // check the next bit to see if we need to geometric round
102 				if (_trace_rounding) std::cout << "truncated exp" << (rounding_mode == GEOMETRIC_ROUND_UP ? " geo-up " : " geo-dw ");
103 			}
104 			else {
105 				if (nbits - 1 - nrRegimeBits - es > 0) {
106 					// use the fraction to determine rounding as this posit has fraction bits
107 					rounding_mode = ARITHMETIC_ROUNDING;
108 					if (_trace_rounding) std::cout << "arithmetic  rounding ";
109 
110 				}
111 				else {
112 					// this posit is in the geometric regime and has consumed all the bits
113 					rounding_mode = ARITHMETIC_ROUNDING; //  NO_ADDITIONAL_ROUNDING;
114 					if (_trace_rounding) std::cout << "no rounding alltaken ";
115 				}
116 			}
117 		}
118 		else {
119 			// we ran out of bits
120 			if (es > 0) {
121 				rounding_mode = _Bits[es-1] ? GEOMETRIC_ROUND_UP : GEOMETRIC_ROUND_DOWN;
122 				if (_trace_rounding) std::cout << "no exp left: " << (rounding_mode == GEOMETRIC_ROUND_UP ? " geo-up " : " geo-dw ");
123 			}
124 			else {
125 				// this posit doesn't have an exponent field,
126 				// so we need to look at the fraction to see if we need to round up or down
127 				rounding_mode = ARITHMETIC_ROUNDING;
128 				if (_trace_rounding) std::cout << "ar rounding no e field ";
129 			}
130 		}
131 		return rounding_mode;
132 	}
133 
increment()134 	bool increment() {
135 		bool carry = false;
136 		if (es > 0) {
137 			carry = increment_unsigned(_Bits, es);
138 		}
139 		return carry;
140 	}
141 private:
142 	bitblock<es>    _Bits;
143 	size_t			_NrOfBits;
144 
145 	// template parameters need names different from class template parameters (for gcc and clang)
146 	template<size_t nnbits, size_t ees>
147 	friend std::ostream& operator<< (std::ostream& ostr, const exponent<nnbits, ees>& e);
148 	template<size_t nnbits, size_t ees>
149 	friend std::istream& operator>> (std::istream& istr, exponent<nnbits, ees>& e);
150 
151 	template<size_t nnbits, size_t ees>
152 	friend bool operator==(const exponent<nnbits, ees>& lhs, const exponent<nnbits, ees>& rhs);
153 	template<size_t nnbits, size_t ees>
154 	friend bool operator!=(const exponent<nnbits, ees>& lhs, const exponent<nnbits, ees>& rhs);
155 	template<size_t nnbits, size_t ees>
156 	friend bool operator< (const exponent<nnbits, ees>& lhs, const exponent<nnbits, ees>& rhs);
157 	template<size_t nnbits, size_t ees>
158 	friend bool operator> (const exponent<nnbits, ees>& lhs, const exponent<nnbits, ees>& rhs);
159 	template<size_t nnbits, size_t ees>
160 	friend bool operator<=(const exponent<nnbits, ees>& lhs, const exponent<nnbits, ees>& rhs);
161 	template<size_t nnbits, size_t ees>
162 	friend bool operator>=(const exponent<nnbits, ees>& lhs, const exponent<nnbits, ees>& rhs);
163 };
164 
165 template<size_t nbits, size_t es>
scale(const exponent<nbits,es> & e)166 inline int scale(const exponent<nbits, es>& e) { return e.scale(); }
167 
168 /////////////////// EXPONENT operators
169 template<size_t nbits, size_t es>
operator <<(std::ostream & ostr,const exponent<nbits,es> & e)170 inline std::ostream& operator<<(std::ostream& ostr, const exponent<nbits, es>& e) {
171 	size_t nrOfExponentBitsProcessed = 0;
172 	if constexpr (es > 0) {
173 		for (int i = int(es) - 1; i >= 0; --i) {
174 			if (e._NrOfBits > nrOfExponentBitsProcessed++) {
175 				ostr << (e._Bits[size_t(i)] ? "1" : "0");
176 			}
177 			else {
178 				ostr << "-";
179 			}
180 		}
181 	}
182 	else {
183 		ostr << "~"; // for proper alignment in tables
184 	}
185 	return ostr;
186 }
187 
188 template<size_t nbits, size_t es>
operator >>(std::istream & istr,const exponent<nbits,es> & e)189 inline std::istream& operator>> (std::istream& istr, const exponent<nbits, es>& e) {
190 	istr >> e._Bits;
191 	return istr;
192 }
193 
194 template<size_t nbits, size_t es>
to_string(const exponent<nbits,es> & e,bool dashExtent=true,bool nibbleMarker=false)195 inline std::string to_string(const exponent<nbits, es>& e, bool dashExtent = true, bool nibbleMarker = false) {
196 	std::stringstream sstr;
197 	size_t nrOfExponentBitsProcessed = 0;
198 	if constexpr (es > 0) {
199 		for (int i = int(es) - 1; i >= 0; --i) {
200 			if (e.nrBits() > nrOfExponentBitsProcessed++) {
201 				bitblock<es> bb = e.get();
202 				sstr << (bb[size_t(i)] ? '1' : '0');
203 			}
204 			else {
205 				sstr << (dashExtent ? "-" : "");
206 			}
207 			if (nibbleMarker && ((i % 4) == 0) && i != 0) sstr << '\'';
208 		}
209 	}
210 	else {
211 		sstr << '~'; // for proper alignment in tables
212 	}
213 	return sstr.str();
214 }
215 
216 template<size_t nbits, size_t es>
operator ==(const exponent<nbits,es> & lhs,const exponent<nbits,es> & rhs)217 inline bool operator==(const exponent<nbits, es>& lhs, const exponent<nbits, es>& rhs) { return lhs._Bits == rhs._Bits && lhs._NrOfBits == rhs._NrOfBits; }
218 template<size_t nbits, size_t es>
operator !=(const exponent<nbits,es> & lhs,const exponent<nbits,es> & rhs)219 inline bool operator!=(const exponent<nbits, es>& lhs, const exponent<nbits, es>& rhs) { return !operator==(lhs, rhs); }
220 template<size_t nbits, size_t es>
operator <(const exponent<nbits,es> & lhs,const exponent<nbits,es> & rhs)221 inline bool operator< (const exponent<nbits, es>& lhs, const exponent<nbits, es>& rhs) { return lhs._NrOfBits == rhs._NrOfBits && lhs._Bits < rhs._Bits; }
222 template<size_t nbits, size_t es>
operator >(const exponent<nbits,es> & lhs,const exponent<nbits,es> & rhs)223 inline bool operator> (const exponent<nbits, es>& lhs, const exponent<nbits, es>& rhs) { return  operator< (rhs, lhs); }
224 template<size_t nbits, size_t es>
operator <=(const exponent<nbits,es> & lhs,const exponent<nbits,es> & rhs)225 inline bool operator<=(const exponent<nbits, es>& lhs, const exponent<nbits, es>& rhs) { return !operator> (lhs, rhs); }
226 template<size_t nbits, size_t es>
operator >=(const exponent<nbits,es> & lhs,const exponent<nbits,es> & rhs)227 inline bool operator>=(const exponent<nbits, es>& lhs, const exponent<nbits, es>& rhs) { return !operator< (lhs, rhs); }
228 
229 }}  // namespace sw::universal
230 
231