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