1 #pragma once
2 // manipulators.hpp: definitions of helper functions for classic cfloat type manipulation
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 <iostream>
8 #include <iomanip>
9 #include <typeinfo>  // for typeid()
10 
11 // pull in the color printing for shells utility
12 #include <universal/utility/color_print.hpp>
13 
14 // This file contains functions that manipulate a cfloat type
15 // using cfloat number system knowledge.
16 
17 namespace sw::universal {
18 
19 // Generate a type tag for this cfloat, for example, cfloat<8,1, unsigned char, hasSubnormals, noSupernormals, notSaturating>
20 template<size_t nbits, size_t es, typename bt, bool hasSubnormals, bool hasSupernormals, bool isSaturating>
type_tag(const cfloat<nbits,es,bt,hasSubnormals,hasSupernormals,isSaturating> & v)21 std::string type_tag(const cfloat<nbits, es, bt, hasSubnormals, hasSupernormals, isSaturating>& v) {
22 	std::stringstream s;
23 	s << "cfloat<"
24 		<< std::setw(3) << nbits << ", "
25 		<< std::setw(3) << es << ", "
26 		<< typeid(bt).name() << ", "
27 		<< (hasSubnormals ? "hasSubnormals, " : " noSubnormals, ")
28 		<< (hasSupernormals ? "hasSupernormals, " : " noSupernormals, ")
29 		<< (isSaturating ? "   Saturating>" : "notSaturating>");
30 	if (v.iszero()) s << ' ';
31 	return s.str();
32 }
33 
34 // generate and tabulate subnormals of the cfloat configuration
35 template<typename cfloatConfiguration>
subnormals()36 void subnormals() {
37 	constexpr size_t nbits         = cfloatConfiguration::nbits;
38 	constexpr size_t es            = cfloatConfiguration::es;
39 	using bt                       = typename cfloatConfiguration::BlockType;
40 	constexpr bool hasSubnormals   = cfloatConfiguration::hasSubnormals;
41 	constexpr bool hasSupernormals = cfloatConfiguration::hasSupernormals;
42 	constexpr bool isSaturating    = cfloatConfiguration::isSaturating;
43 	cfloat<nbits, es, bt, hasSubnormals, hasSupernormals, isSaturating> a{ 0 };
44 
45 	// generate the smallest subnormal with ULP set
46 	++a;
47 	if constexpr (hasSubnormals) {
48 		constexpr size_t fbits = cfloatConfiguration::fbits;
49 		std::cout << type_tag(a) << " subnormals\n";
50 		if constexpr (nbits < 65) {
51 			for (size_t i = 0; i < fbits; ++i) {
52 				std::cout << to_binary(a, true) << " : " << color_print(a) << " : " << a << '\n';
53 				uint64_t fraction = a.fraction_ull();
54 				fraction <<= 1;
55 				a.setfraction(fraction);
56 			}
57 		}
58 		else {
59 			blockbinary<fbits, bt> fraction{ 0 };
60 			for (size_t i = 0; i < fbits; ++i) {
61 				std::cout << to_binary(a, true) << " : " << color_print(a) << " : " << a << '\n';
62 				a.fraction(fraction);
63 				fraction <<= 1;
64 				a.setfraction(fraction);
65 			}
66 		}
67 	}
68 	else {
69 		std::cout << type_tag(a) << " has no subnormals\n";
70 	}
71 }
72 
73 // report dynamic range of a type, specialized for a cfloat
74 template<size_t nbits, size_t es, typename bt, bool hasSubnormals, bool hasSupernormals, bool isSaturating>
dynamic_range(const cfloat<nbits,es,bt,hasSubnormals,hasSupernormals,isSaturating> & a)75 std::string dynamic_range(const cfloat<nbits, es, bt, hasSubnormals, hasSupernormals, isSaturating>& a) {
76 	std::stringstream s;
77 	cfloat<nbits, es, bt, hasSubnormals, hasSupernormals, isSaturating> b(SpecificValue::maxneg), c(SpecificValue::minneg), d(SpecificValue::minpos), e(SpecificValue::maxpos);
78 	s << type_tag(a) << ": ";
79 	s << "minpos scale " << std::setw(10) << d.scale() << "     ";
80 	s << "maxpos scale " << std::setw(10) << e.scale() << '\n';
81 	s << "[" << b << " ... " << c << ", -0, +0, " << d << " ... " << e << "]\n";
82 	s << "[" << to_binary(b) << " ... " << to_binary(c) << ", -0, +0, " << to_binary(d) << " ... " << to_binary(e) << "]\n";
83 	cfloat<nbits, es, bt, hasSubnormals, hasSupernormals, isSaturating> ninf(SpecificValue::infneg), pinf(SpecificValue::infpos);
84 	s << "inclusive range = (" << to_binary(ninf) << ", " << to_binary(pinf) << ")\n";
85 	s << "inclusive range = (" << ninf << ", " << pinf << ")\n";
86 	return s.str();
87 }
88 
89 template<size_t nbits, size_t es, typename bt, bool hasSubnormals, bool hasSupernormals, bool isSaturating>
minpos_scale(const cfloat<nbits,es,bt,hasSubnormals,hasSupernormals,isSaturating> & b)90 int minpos_scale(const cfloat<nbits, es, bt, hasSubnormals, hasSupernormals, isSaturating>& b) {
91 	cfloat<nbits, es, bt, hasSubnormals, hasSupernormals, isSaturating> c(b);
92 	return c.minpos().scale();
93 }
94 
95 template<size_t nbits, size_t es, typename bt, bool hasSubnormals, bool hasSupernormals, bool isSaturating>
maxpos_scale(const cfloat<nbits,es,bt,hasSubnormals,hasSupernormals,isSaturating> & b)96 int maxpos_scale(const cfloat<nbits, es, bt, hasSubnormals, hasSupernormals, isSaturating>& b) {
97 	cfloat<nbits, es, bt, hasSubnormals, hasSupernormals, isSaturating> c(b);
98 	return c.maxpos().scale();
99 }
100 
101 template<size_t nbits, size_t es, typename bt, bool hasSubnormals, bool hasSupernormals, bool isSaturating>
max_negative_scale(const cfloat<nbits,es,bt,hasSubnormals,hasSupernormals,isSaturating> & b)102 int max_negative_scale(const cfloat<nbits, es, bt, hasSubnormals, hasSupernormals, isSaturating>& b) {
103 	cfloat<nbits, es, bt, hasSubnormals, hasSupernormals, isSaturating> c(b);
104 	return c.maxneg().scale();
105 }
106 
107 // Generate a string representing the cfloat components: sign, exponent, faction and value
108 template<size_t nbits, size_t es, typename bt, bool hasSubnormals, bool hasSupernormals, bool isSaturating>
components(const cfloat<nbits,es,bt,hasSubnormals,hasSupernormals,isSaturating> & v)109 std::string components(const cfloat<nbits, es, bt, hasSubnormals, hasSupernormals, isSaturating>& v) {
110 	std::stringstream s;
111 	bool sign{ false };
112 	blockbinary<v.es, bt> e;
113 	blockbinary<v.fbits, bt> f;
114 	decode(v, sign, e, f);
115 
116 	// TODO: hardcoded field width is governed by pretty printing cfloat tables, which by construction will always be small cfloats
117 	s << std::setw(14) << to_binary(v)
118 		<< " Sign : " << std::setw(2) << sign
119 		<< " Exponent : " << std::setw(5) << e
120 		<< " Fraction : " << std::setw(8) << f
121 		<< " Value : " << std::setw(16) << v;
122 
123 	return s.str();
124 }
125 
126 // generate a binary string for cfloat
127 template<size_t nbits, size_t es, typename bt, bool hasSubnormals, bool hasSupernormals, bool isSaturating>
to_hex(const cfloat<nbits,es,bt,hasSubnormals,hasSupernormals,isSaturating> & v)128 inline std::string to_hex(const cfloat<nbits, es, bt, hasSubnormals, hasSupernormals, isSaturating>& v) {
129 	constexpr size_t bitsInByte = 8;
130 	constexpr size_t bitsInBlock = sizeof(bt) * bitsInByte;
131 	char hexChar[16] = {
132 		'0', '1', '2', '3', '4', '5', '6', '7',
133 		'8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
134 	};
135 	std::stringstream s;
136 	s << "0x" << std::hex;
137 	long nrNibbles = long(1ull + ((nbits - 1ull) >> 2ull));
138 	for (long n = nrNibbles - 1; n >= 0; --n) {
139 		uint8_t nibble = v.nibble(size_t(n));
140 		s << hexChar[nibble];
141 		if (n > 0 && ((n * 4ll) % bitsInBlock) == 0) s << '\'';
142 	}
143 	return s.str();
144 }
145 
146 // generate a cfloat format ASCII hex format nbits.esxNN...NNa
147 template<size_t nbits, size_t es, typename bt, bool hasSubnormals, bool hasSupernormals, bool isSaturating>
hex_print(const cfloat<nbits,es,bt,hasSubnormals,hasSupernormals,isSaturating> & c)148 inline std::string hex_print(const cfloat<nbits, es, bt, hasSubnormals, hasSupernormals, isSaturating>& c) {
149 	std::stringstream s;
150 	s << nbits << '.' << es << 'x' << to_hex(c) << 'c';
151 	return s.str();
152 }
153 
154 template<size_t nbits, size_t es, typename bt, bool hasSubnormals, bool hasSupernormals, bool isSaturating>
pretty_print(const cfloat<nbits,es,bt,hasSubnormals,hasSupernormals,isSaturating> & r)155 std::string pretty_print(const cfloat<nbits, es, bt, hasSubnormals, hasSupernormals, isSaturating>& r) {
156 	std::stringstream s;
157 	constexpr size_t fbits = cfloat<nbits, es, bt, hasSubnormals, hasSupernormals, isSaturating>::fbits;
158 	bool sign{ false };
159 	blockbinary<es, bt> e;
160 	blockbinary<fbits, bt> f;
161 	decode(r, sign, e, f);
162 
163 	// sign bit
164 	s << (sign ? '1' : '0');
165 
166 	// exponent bits
167 	s << '-';
168 	for (int i = int(es) - 1; i >= 0; --i) {
169 		s << (e.test(static_cast<size_t>(i)) ? '1' : '0');
170 	}
171 
172 	// fraction bits
173 	s << '-';
174 	for (int i = int(r.fbits) - 1; i >= 0; --i) {
175 		s << (f.test(static_cast<size_t>(i)) ? '1' : '0');
176 	}
177 
178 	return s.str();
179 }
180 
181 template<size_t nbits, size_t es, typename bt, bool hasSubnormals, bool hasSupernormals, bool isSaturating>
info_print(const cfloat<nbits,es,bt,hasSubnormals,hasSupernormals,isSaturating> & p,int printPrecision=17)182 std::string info_print(const cfloat<nbits, es, bt, hasSubnormals, hasSupernormals, isSaturating>& p, int printPrecision = 17) {
183 	return "TBD";
184 }
185 
186 // generate a binary, color-coded representation of the cfloat
187 template<size_t nbits, size_t es, typename bt, bool hasSubnormals, bool hasSupernormals, bool isSaturating>
color_print(const cfloat<nbits,es,bt,hasSubnormals,hasSupernormals,isSaturating> & r)188 std::string color_print(const cfloat<nbits, es, bt, hasSubnormals, hasSupernormals, isSaturating>& r) {
189 	using Real = cfloat<nbits, es, bt, hasSubnormals, hasSupernormals, isSaturating>;
190 	std::stringstream s;
191 	bool sign{ false };
192 	blockbinary<es,bt> e;
193 	blockbinary<Real::fbits,bt> f;
194 	decode(r, sign, e, f);
195 
196 	Color red(ColorCode::FG_RED);
197 	Color yellow(ColorCode::FG_YELLOW);
198 	Color blue(ColorCode::FG_BLUE);
199 	Color magenta(ColorCode::FG_MAGENTA);
200 	Color cyan(ColorCode::FG_CYAN);
201 	Color white(ColorCode::FG_WHITE);
202 	Color def(ColorCode::FG_DEFAULT);
203 
204 	// sign bit
205 	s << red << (sign ? '1' : '0');
206 
207 	// exponent bits
208 	for (int i = int(es) - 1; i >= 0; --i) {
209 		s << cyan << (e.test(static_cast<size_t>(i)) ? '1' : '0');
210 	}
211 
212 	// fraction bits
213 	for (int i = int(r.fbits) - 1; i >= 0; --i) {
214 		s << magenta << (f.test(static_cast<size_t>(i)) ? '1' : '0');
215 	}
216 
217 	s << def;
218 	return s.str();
219 }
220 
221 
222 }  // namespace sw::universal