1 #pragma once
2 // constexpr754.hpp: constexpr manipulation functions for IEEE-754 native types using C++20 <bit>
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 <sstream>
8 #include <iomanip>
9 #include <bit>    // bit_cast
10 
11 #include <universal/native/integers.hpp>
12 #include <universal/utility/color_print.hpp>
13 
14 namespace sw::universal {
15 
16 ////////////////////////////////////////////////////////////////////////
17 // numerical helpers
18 
19 template<typename Real>
extractFields(Real value,bool & s,uint64_t & rawExponentBits,uint64_t & rawFractionBits)20 inline constexpr void extractFields(Real value, bool& s, uint64_t& rawExponentBits, uint64_t& rawFractionBits) noexcept {
21 	if (value == 0) {
22 		s = false;
23 		rawExponentBits = 0ull;
24 		rawFractionBits = 0ull;
25 	}
26 	if (value < 0) s = true;
27 }
28 template<>
extractFields(float value,bool & s,uint64_t & rawExponentBits,uint64_t & rawFractionBits)29 inline constexpr void extractFields(float value, bool& s, uint64_t& rawExponentBits, uint64_t& rawFractionBits) noexcept {
30 	uint64_t bc = std::bit_cast<uint32_t, float>(value);
31 	s = (ieee754_parameter<float>::smask & bc);
32 	rawExponentBits = (ieee754_parameter<float>::emask & bc) >> ieee754_parameter<float>::fbits;
33 	rawFractionBits = (ieee754_parameter<float>::fmask & bc);
34 }
35 template<>
extractFields(double value,bool & s,uint64_t & rawExponentBits,uint64_t & rawFractionBits)36 inline constexpr void extractFields(double value, bool& s, uint64_t& rawExponentBits, uint64_t& rawFractionBits) noexcept {
37 	uint64_t bc = std::bit_cast<uint64_t, double>(value);
38 	s = (ieee754_parameter<double>::smask & bc);
39 	rawExponentBits = (ieee754_parameter<double>::emask & bc) >> ieee754_parameter<double>::fbits;
40 	rawFractionBits = (ieee754_parameter<double>::fmask & bc);
41 }
42 
43 // generate a hex formatted string for a native IEEE floating point
44 template<typename Real,
45 	typename = typename std::enable_if< std::is_floating_point<Real>::value, Real >::type
46 >
to_hex(Real number)47 inline std::string to_hex(Real number) {
48 	std::stringstream s;
49 	bool sign{ false };
50 	uint64_t rawExponent{ 0 };
51 	uint64_t rawFraction{ 0 };
52 	extractFields(number, sign, rawExponent, rawFraction);
53 	s << (sign ? '1' : '0') << '.' << std::hex << int(rawExponent) << '.' << rawFraction;
54 	return s.str();
55 }
56 
57 // generate a binary string for a native IEEE floating point
58 template<typename Real,
59 	typename = typename std::enable_if< std::is_floating_point<Real>::value, Real >::type
60 >
to_binary(Real number,bool bNibbleMarker=false)61 inline std::string to_binary(Real number, bool bNibbleMarker = false) {
62 	std::stringstream s;
63 
64 	bool sign{ false };
65 	uint64_t rawExponent{ 0 };
66 	uint64_t rawFraction{ 0 };
67 	extractFields(number, sign, rawExponent, rawFraction);
68 
69 	s << "0b";
70 	// print sign bit
71 	s << (sign ? '1' : '0') << '.';
72 
73 	// print exponent bits
74 	{
75 		uint32_t mask = (uint32_t(1) << (ieee754_parameter<Real>::ebits-1));
76 		for (int i = ieee754_parameter<Real>::ebits - 1; i >= 0; --i) {
77 			s << ((rawExponent & mask) ? '1' : '0');
78 			if (bNibbleMarker && i != 0 && (i % 4) == 0) s << '\'';
79 			mask >>= 1;
80 		}
81 	}
82 
83 	s << '.';
84 
85 	// print fraction bits
86 	uint64_t mask = (uint64_t(1) << (ieee754_parameter<Real>::fbits - 1));
87 	for (int i = ieee754_parameter<Real>::fbits - 1; i >= 0; --i) {
88 		s << ((rawFraction & mask) ? '1' : '0');
89 		if (bNibbleMarker && i != 0 && (i % 4) == 0) s << '\'';
90 		mask >>= 1;
91 	}
92 
93 	return s.str();
94 }
95 
96 // return in triple form (sign, scale, fraction)
97 template<typename Real,
98 	typename = typename std::enable_if< std::is_floating_point<Real>::value, Real >::type
99 >
to_triple(Real number,bool bNibbleMarker=false)100 inline std::string to_triple(Real number, bool bNibbleMarker = false) {
101 	std::stringstream s;
102 
103 	bool sign{ false };
104 	uint64_t rawExponent{ 0 };
105 	uint64_t rawFraction{ 0 };
106 	extractFields(number, sign, rawExponent, rawFraction);
107 
108 	// print sign bit
109 	s << '(' << (sign ? '-' : '+') << ',';
110 
111 	// exponent
112 	// the exponent value used in the arithmetic is the exponent shifted by a bias
113 	// for the IEEE 754 binary32 case, an exponent value of 127 represents the actual zero
114 	// (i.e. for 2^(e - 127) to be one, e must be 127).
115 	// Exponents range from -126 to +127 because exponents of -127 (all 0s) and 128 (all 1s) are reserved for special numbers.
116 	if (rawExponent == 0) {
117 		s << "exp=0, ";
118 	}
119 	else if (rawExponent == ieee754_parameter<Real>::eallset) {
120 		s << "exp=1, ";
121 	}
122 	else {
123 		int scale = static_cast<int>(rawExponent) - ieee754_parameter<Real>::bias;
124 		s << std::setw(4) << scale << ", ";
125 	}
126 
127 	// print fraction bits
128 	uint64_t mask = (uint64_t(1) << (ieee754_parameter<Real>::fbits - 1));
129 	s << "0b";
130 	for (int i = (ieee754_parameter<Real>::fbits - 1); i >= 0; --i) {
131 		s << ((rawFraction & mask) ? '1' : '0');
132 		if (bNibbleMarker && i != 0 && (i % 4) == 0) s << '\'';
133 		mask >>= 1;
134 	}
135 
136 	s << ')';
137 	return s.str();
138 }
139 
140 template<typename Real,
141 	typename = typename std::enable_if< std::is_floating_point<Real>::value, Real >::type
142 >
to_base2_scientific(Real number)143 inline std::string to_base2_scientific(Real number) {
144 	std::stringstream s;
145 
146 	bool sign{ false };
147 	uint64_t rawExponent{ 0 };
148 	uint64_t rawFraction{ 0 };
149 	extractFields(number, sign, rawExponent, rawFraction);
150 
151 	s << (sign == 1 ? "-" : "+") << "1.";
152 	uint64_t mask = (uint64_t(1) << (ieee754_parameter<Real>::fbits - 1));
153 	for (int i = (ieee754_parameter<Real>::fbits - 1); i >= 0; --i) {
154 		s << ((rawFraction & mask) ? '1' : '0');
155 		mask >>= 1;
156 	}
157 	s << "e2^" << std::showpos << (rawExponent - ieee754_parameter<Real>::bias);
158 
159 	return s.str();
160 }
161 
162 // generate a color coded binary string for a native single precision IEEE floating point
163 template<typename Real,
164 	typename = typename std::enable_if< std::is_floating_point<Real>::value, Real >::type
165 >
color_print(Real number)166 inline std::string color_print(Real number) {
167 	std::stringstream s;
168 
169 	bool sign{ false };
170 	uint64_t rawExponent{ 0 };
171 	uint64_t rawFraction{ 0 };
172 	extractFields(number, sign, rawExponent, rawFraction);
173 
174 	Color red(ColorCode::FG_RED);
175 	Color yellow(ColorCode::FG_YELLOW);
176 	Color blue(ColorCode::FG_BLUE);
177 	Color magenta(ColorCode::FG_MAGENTA);
178 	Color cyan(ColorCode::FG_CYAN);
179 	Color white(ColorCode::FG_WHITE);
180 	Color def(ColorCode::FG_DEFAULT);
181 
182 	// print sign bit
183 	s << red << (sign ? '1' : '0'); // << '.';
184 
185 	// print exponent bits
186 	{
187 		uint64_t mask = (1 << (ieee754_parameter<Real>::ebits - 1));
188 		for (int i = (ieee754_parameter<Real>::ebits - 1); i >= 0; --i) {
189 			s << cyan << ((rawExponent & mask) ? '1' : '0');
190 //			if (i > 0 && i % 4 == 0) s << cyan << '\'';
191 			mask >>= 1;
192 		}
193 	}
194 
195 //	s << '.';
196 
197 	// print fraction bits
198 	uint64_t mask = (uint64_t(1) << (ieee754_parameter<Real>::fbits - 1));
199 	for (int i = (ieee754_parameter<Real>::fbits - 1); i >= 0; --i) {
200 		s << magenta << ((rawFraction & mask) ? '1' : '0');
201 //		if (i > 0 && i % 4 == 0) s << magenta << '\'';
202 		mask >>= 1;
203 	}
204 
205 	s << def;
206 	return s.str();
207 }
208 
209 } // namespace sw::universal
210 
211