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