1 #pragma once
2 // clang_long_double.hpp: nonconstexpr implementation of IEEE-754 long double manipulators
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 #if defined(__clang__)
9 /* Clang/LLVM. ---------------------------------------------- */
10 
11 namespace sw::universal {
12 
13 /////////////////////////////////////////////////////////////////////////////////////////////////////////
14 // compiler specific long double IEEE floating point
15 
16 union long_double_decoder {
long_double_decoder()17 	long_double_decoder() : ld{0.0l} {}
long_double_decoder(long double _ld)18 	long_double_decoder(long double _ld) : ld{_ld} {}
19 	long double ld;
20 	struct {
21 		uint64_t fraction : 63;
22 		uint64_t bit63 : 1;
23 		uint64_t exponent : 15;
24 		uint64_t sign : 1;
25 	} parts;
26 };
27 
extractFields(long double value,bool & s,uint64_t & rawExponentBits,uint64_t & rawFractionBits)28 inline void extractFields(long double value, bool& s, uint64_t& rawExponentBits, uint64_t& rawFractionBits) {
29 	long_double_decoder decoder;
30 	decoder.ld = value;
31 	s = decoder.parts.sign == 1 ? true : false;
32 	rawExponentBits = decoder.parts.exponent;
33 	rawFractionBits = decoder.parts.fraction;
34 }
35 
36 // specialization for IEEE long double precision floats
to_base2_scientific(long double number)37 inline std::string to_base2_scientific(long double number) {
38 	std::stringstream s;
39 	long_double_decoder decoder;
40 	decoder.ld = number;
41 	s << (decoder.parts.sign == 1 ? "-" : "+") << "1.";
42 	uint64_t mask = (uint64_t(1) << 63);
43 	for (int i = 63; i >= 0; --i) {
44 		s << ((decoder.parts.fraction & mask) ? '1' : '0');
45 		mask >>= 1;
46 	}
47 	s << "e" << std::showpos << (static_cast<int>(decoder.parts.exponent) - 16383);
48 	return s.str();
49 }
50 
51 // generate a binary string for a native double precision IEEE floating point
to_hex(long double number)52 inline std::string to_hex(long double number) {
53 	std::stringstream s;
54 	long_double_decoder decoder;
55 	decoder.ld = number;
56 	s << (decoder.parts.sign ? '1' : '0') << '.' << std::hex << int(decoder.parts.exponent) << '.' << decoder.parts.fraction;
57 	return s.str();
58 }
59 
60 // generate a binary string for a native double precision IEEE floating point
to_binary(long double number,bool bNibbleMarker=false)61 inline std::string to_binary(long double number, bool bNibbleMarker = false) {
62 	std::stringstream s;
63 	long_double_decoder decoder;
64 	decoder.ld = number;
65 
66 	s << "0b";
67 	// print sign bit
68 	s << (decoder.parts.sign ? '1' : '0') << '.';
69 
70 	// print exponent bits
71 	{
72 		uint64_t mask = 0x4000;
73 		for (int i = 14; i >= 0; --i) {
74 			s << ((decoder.parts.exponent & mask) ? '1' : '0');
75 			if (bNibbleMarker && i != 0 && (i % 4) == 0) s << '\'';
76 			mask >>= 1;
77 		}
78 	}
79 
80 	s << '.';
81 
82 	// print fraction bits
83 	uint64_t mask = (uint64_t(1) << 62);
84 	for (int i = 62; i >= 0; --i) {
85 		s << ((decoder.parts.fraction & mask) ? '1' : '0');
86 		if (bNibbleMarker && i != 0 && (i % 4) == 0) s << '\'';
87 		mask >>= 1;
88 	}
89 
90 	return s.str();
91 }
92 
93 // return in triple form (+, scale, fraction)
to_triple(long double number)94 inline std::string to_triple(long double number) {
95 	std::stringstream s;
96 	long_double_decoder decoder;
97 	decoder.ld = number;
98 
99 	// print sign bit
100 	s << '(' << (decoder.parts.sign ? '-' : '+') << ',';
101 
102 	// exponent
103 	// the exponent value used in the arithmetic is the exponent shifted by a bias
104 	// for the IEEE 754 binary32 case, an exponent value of 127 represents the actual zero
105 	// (i.e. for 2^(e ¿ 127) to be one, e must be 127).
106 	// Exponents range from ¿126 to +127 because exponents of ¿127 (all 0s) and +128 (all 1s) are reserved for special numbers.
107 	if (decoder.parts.exponent == 0) {
108 		s << "exp=0,";
109 	}
110 	else if (decoder.parts.exponent == 0xFF) {
111 		s << "exp=1, ";
112 	}
113 	int scale = int(decoder.parts.exponent) - 16383;
114 	s << scale << ',';
115 
116 	// print fraction bits
117 	s << (decoder.parts.bit63 ? '1' : '0');
118 	uint64_t mask = (uint64_t(1) << 62);
119 	for (int i = 62; i >= 0; --i) {
120 		s << ((decoder.parts.fraction & mask) ? '1' : '0');
121 		mask >>= 1;
122 	}
123 
124 	s << ')';
125 	return s.str();
126 }
127 
128 // generate a color coded binary string for a native double precision IEEE floating point
color_print(long double number)129 inline std::string color_print(long double number) {
130 	std::stringstream s;
131 	long_double_decoder decoder;
132 	decoder.ld = number;
133 
134 	Color red(ColorCode::FG_RED);
135 	Color yellow(ColorCode::FG_YELLOW);
136 	Color blue(ColorCode::FG_BLUE);
137 	Color magenta(ColorCode::FG_MAGENTA);
138 	Color cyan(ColorCode::FG_CYAN);
139 	Color white(ColorCode::FG_WHITE);
140 	Color def(ColorCode::FG_DEFAULT);
141 
142 	// print prefix
143 	s << yellow << "0b";
144 
145 	// print sign bit
146 	s << red << (decoder.parts.sign ? '1' : '0') << '.';
147 
148 	// print exponent bits
149 	{
150 		uint64_t mask = 0x8000;
151 		for (int i = 15; i >= 0; --i) {
152 			s << cyan << ((decoder.parts.exponent & mask) ? '1' : '0');
153 			if (i > 0 && i % 4 == 0) s << cyan << '\'';
154 			mask >>= 1;
155 		}
156 	}
157 
158 	s << '.';
159 
160 	// print fraction bits
161 	s << magenta << (decoder.parts.bit63 ? '1' : '0');
162 	uint64_t mask = (uint64_t(1) << 61);
163 	for (int i = 61; i >= 0; --i) {
164 		s << magenta << ((decoder.parts.fraction & mask) ? '1' : '0');
165 		if (i > 0 && i % 4 == 0) s << magenta << '\'';
166 		mask >>= 1;
167 	}
168 
169 	s << def;
170 	return s.str();
171 }
172 
173 #ifdef CPLUSPLUS_17
extract_fp_components(long double fp,bool & _sign,int & _exponent,long double & _fr,unsigned long long & _fraction)174 inline void extract_fp_components(long double fp, bool& _sign, int& _exponent, long double& _fr, unsigned long long& _fraction) {
175 	static_assert(std::numeric_limits<long double>::digits <= 64, "This function only works when long double significant is <= 64 bit.");
176 	if constexpr (sizeof(long double) == 8) { // it is just a double
177 		_sign = fp < 0.0 ? true : false;
178 		_fr = frexp(double(fp), &_exponent);
179 		_fraction = uint64_t(0x000FFFFFFFFFFFFFull) & reinterpret_cast<uint64_t&>(_fr);
180 	}
181 	else if constexpr (sizeof(long double) == 16 && std::numeric_limits<long double>::digits <= 64) {
182 		_sign = fp < 0.0 ? true : false;
183 		_fr = frexpl(fp, &_exponent);
184 		_fraction = uint64_t(0x7FFFFFFFFFFFFFFFull) & reinterpret_cast<uint64_t&>(_fr); // 80bit extended format only has 63bits of fraction
185 	}
186 }
187 #else
extract_fp_components(long double fp,bool & _sign,int & _exponent,long double & _fr,unsigned long long & _fraction)188 inline void extract_fp_components(long double fp, bool& _sign, int& _exponent, long double& _fr, unsigned long long& _fraction) {
189 	static_assert(std::numeric_limits<long double>::digits <= 64, "This function only works when long double significant is <= 64 bit.");
190 	if (sizeof(long double) == 8) { // it is just a double
191 		_sign = fp < 0.0 ? true : false;
192 		_fr = frexp(double(fp), &_exponent);
193 		_fraction = uint64_t(0x000FFFFFFFFFFFFFull) & reinterpret_cast<uint64_t&>(_fr);
194 	}
195 	else if (sizeof(long double) == 16 && std::numeric_limits<long double>::digits <= 64) {
196 		_sign = fp < 0.0 ? true : false;
197 		_fr = frexpl(fp, &_exponent);
198 		_fraction = uint64_t(0x7FFFFFFFFFFFFFFFull) & reinterpret_cast<uint64_t&>(_fr); // 80bit extended format only has 63bits of fraction
199 	}
200 }
201 #endif
202 
203 
204 } // namespace sw::universal
205 
206 #endif // CLANG/LLVM
207 
208