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