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