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