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