C++ Library Extensions 2022.12.09
To help learn modern C++ programming
tpf_bitwise.hpp
Go to the documentation of this file.
1#ifndef _TPF_BITWISE_HPP
2#define _TPF_BITWISE_HPP
3
4#ifndef TBB_SUPPRESS_DEPRECATED_MESSAGES
5 #define TBB_SUPPRESS_DEPRECATED_MESSAGES 1
6#endif // end of TBB_SUPPRESS_DEPRECATED_MESSAGES
7
8#ifndef NOMINMAX
9#define NOMINMAX
10#endif
11
12#include <iostream>
13#include <type_traits>
14#include <limits>
15#include <string>
16#include <sstream>
17#include <iomanip>
18#include <cstddef> // for std::byte
19#include <bitset>
20#include <cmath>
21
22namespace tpf::bitwise
23{
24 namespace hidden
25 {
26 template<typename T> struct st_is_character
27 {
28 static const bool value = false;
29 };
30
31 template<> struct st_is_character<char>
32 {
33 static const bool value = true;
34 };
35
36 template<> struct st_is_character<unsigned char>
37 {
38 static const bool value = true;
39 };
40
41 template<> struct st_is_character<signed char>
42 {
43 static const bool value = true;
44 };
45
46 template<> struct st_is_character<wchar_t>
47 {
48 static const bool value = true;
49 };
50 }
51
52 template<typename T>
54
55 template<typename T> constexpr T limits_max = std::numeric_limits<T>::max();
56 template<typename T> constexpr T limits_min = std::numeric_limits<T>::min();
57
58 // count bits in the mask
59 // mask ( 7) : 0000 0111, should return 3
60 // mask (-1) : 1111 1111, should return 8
61 // mask (0x80) : 1000 0000, should return 1
62 // mask (0) : 0000 0000, should return 0
63 // mask (1) : 0000 0001, should return 1
64
65 template<typename T>
66 using enable_if_integral = std::enable_if_t<std::is_integral<T>::value>;
67
68 template<typename T, typename = enable_if_integral<T>>
69 using unsigned_t = std::make_unsigned_t<T>;
70
71 template<typename T, typename = enable_if_integral<T>>
72 using signed_t = std::make_signed_t<T>;
73
74 template<typename S, typename T>
75 using common_t = std::common_type_t<S, T>;
76
77 template<typename S, typename T,
78 typename = enable_if_integral<S>,
79 typename = enable_if_integral<T>>
81
82 template<typename S, typename T,
83 typename = enable_if_integral<S>,
84 typename = enable_if_integral<T>>
86
87 template<typename T>
88 constexpr int sizeof_bits = sizeof(T) * 8;
89
90 template<typename T>
92 = unsigned_t<T>(1) << (sizeof_bits<T>-1);
93
94 template<typename T, typename = enable_if_integral<T>>
95 int count_set_bits(T bits)
96 {
97 unsigned_t<T> mask = bits;
98
99 int count = 0;
100
101 for (; mask; mask >>= 1)
102 count += (int)(mask & 1);
103
104 return count;
105 }
106
107 template<typename T, typename = enable_if_integral<T>>
108 std::string to_bits(T bits)
109 {
110 unsigned_t<T> mask = bits;
111
112 int count = 0; std::string str;
113
114 for (int pos = sizeof_bits<T>; pos; mask <<= 1, --pos)
115 {
116 if (!str.empty() && (pos % 4 == 0))
117 str.push_back('\'');
118
119 str.push_back((mask & high_bit_mask<T>) ? '1' : '0');
120 }
121
122 return str;
123 }
124
125 template<typename T, typename = enable_if_integral<T>>
126 std::string to_hex(T v)
127 {
128 std::string str;
129
130 if (is_char_v<T>)
131 {
132 std::ostringstream os;
133
134 os << std::setfill('0') << std::setw(sizeof(T) * 2)
135 << std::uppercase << std::hex << (short)v;
136
137 str = os.str();
138
139 if (str.size()==4)
140 {
141 if (v > 0)
142 {
143 str.pop_back();
144 str.pop_back();
145 }
146 else
147 {
148 std::string ss;
149
150 ss.push_back(str[2]);
151 ss.push_back(str[3]);
152
153 str = ss;
154 }
155 }
156 }
157 else
158 {
159 std::ostringstream os;
160
161 os << std::setfill('0') << std::setw(sizeof(T) * 2)
162 << std::uppercase << std::hex <<v;
163
164 str = os.str();
165 }
166
167 return str;
168 }
169
170 template<typename T, std::size_t N>
171 std::string to_hex_reverse(T(&v)[N])
172 {
173 std::ostringstream os;
174
175 for(int n = (int)N-1; n > 0 ; --n)
176 {
177 os << to_hex(v[n]) <<" | ";
178 }
179
180 os << to_hex(v[0]);
181
182 return os.str();
183 }
184
185 template<typename T, std::size_t N>
186 std::string to_hex(T(&v)[N])
187 {
188 std::ostringstream os;
189
190 for(int n = 0; n < (int)N - 1 ; ++n)
191 {
192 os << to_hex(v[n]) <<" | ";
193 }
194
195 os << to_hex(v[N-1]);
196
197 return os.str();
198 }
199
200 template<typename T, typename = enable_if_integral<T>>
201 std::string to_dec(T v)
202 {
203 std::ostringstream os;
204
205 if (is_char_v<T>)
206 os << (short)v;
207 else
208 os << v;
209
210 return os.str();
211 }
212
213 template<typename T, std::size_t N>
214 std::string to_dec_reverse(T(&v)[N])
215 {
216 std::ostringstream os;
217
218 for(int n = (int)N-1; n > 0; --n)
219 {
220 os << to_dec(v[n]) <<" | ";
221 }
222
223 os << to_dec(v[0]);
224
225 return os.str();
226 }
227
228 template<typename T, std::size_t N>
229 std::string to_dec(T(&v)[N])
230 {
231 std::ostringstream os;
232
233 for(int n = 0; n < (int)N - 1; ++n)
234 {
235 os << to_dec(v[n]) <<" | ";
236 }
237
238 os << to_dec(v[N-1]);
239
240 return os.str();
241 }
242
243 template<typename T, typename = enable_if_integral<T>>
244 int field_with(T v)
245 {
246 std::ostringstream os;
247
248 if (is_char_v<T>)
249 os << (short)v;
250 else
251 os << v;
252
253 return (int) os.str().size();
254 }
255
256 template<typename T, typename = enable_if_integral<T>>
258 {
259 int a = field_with(std::numeric_limits<T>::max());
260 int b = field_with(std::numeric_limits<T>::min());
261
262 return a > b ? a : b;
263 }
264
265 template<typename T, typename = enable_if_integral<T>>
266 std::string to_dec_width(T v)
267 {
268 std::ostringstream os;
269
270 int max_field = numeric_width<T>();
271
272 if (is_char_v<T>)
273 os << std::setw(max_field)<<(short)v;
274 else
275 os << std::setw(max_field)<<v;
276
277 return os.str();
278 }
279
280 template<typename T, typename = enable_if_integral<T>>
281 std::string numeric_base(T v)
282 {
283 std::ostringstream os;
284
285 os << to_dec_width(v) << " (" << to_hex<T>(v) << "): " << to_bits<T>(v);
286
287 return os.str();
288 }
289
290 template<typename T, typename = enable_if_integral<T>>
291 std::string numeric_type_info()
292 {
293 std::ostringstream os;
294
295 auto minimum = std::numeric_limits<T>::min();
296 auto maximum = std::numeric_limits<T>::max();
297
298 os << "Type name: " << Tpf_GetTypeName(T)
299 << ",\tByte size: " << sizeof(T) << ",\tBit count: " << sizeof_bits<T>
300 << "\nMinimum: "<< numeric_base(minimum)
301 << "\nMaximum: "<< numeric_base(maximum);
302
303 return os.str();
304 }
305
306 template<typename = void>
307 std::string integral_type_info()
308 {
309 std::ostringstream os;
310
311 os << numeric_type_info<char>() << "\n\n";
312 os << numeric_type_info<unsigned char>() << "\n\n";
313 os << numeric_type_info<short>() << "\n\n";
314 os << numeric_type_info<unsigned short>() << "\n\n";
315 os << numeric_type_info<int>() << "\n\n";
316 os << numeric_type_info<unsigned int>() << "\n\n";
317 os << numeric_type_info<long>() << "\n\n";
318 os << numeric_type_info<unsigned long>() << "\n\n";
319 os << numeric_type_info<long long>() << "\n\n";
320 os << numeric_type_info<unsigned long long>() << "\n";
321
322 return os.str();
323 }
324
325 template<typename T, typename = enable_if_integral<T>>
326 std::string twos_complement(T c)
327 {
328 std::ostringstream os;
329 os << numeric_type_info<T>() << "\n";
330
331 T c1 = ~c; // 1's complement of s1
332 T c2 = ~c + 1; // 2's complement of s1
333 T c3 = c + c2; // c + (~c+1)
334 os << "original value : c = " << numeric_base(c) << "\n";
335 os << "1's complement : ~c = " << numeric_base(c1) << "\n";
336 os << "2's complement : ~c + 1 = " << numeric_base(c2) << "\n";
337 os << "complement check: c + (~c+1) = " << numeric_base(c3) << "\n";
338
339 return os.str();
340 }
341
342 template<typename T,
345 {
346 constexpr static size_t byte_size = sizeof(T);
347 constexpr static T min_limit = std::numeric_limits<T>::min();
348 constexpr static T max_limit = std::numeric_limits<T>::max();
349
350 // on some machine, bit count of a byte is not 8 bits, but 16 bits
351 // but I will ignore it, and assume 1 byte represents 8 bits
352 constexpr static size_t bit_size = sizeof(T) * 8;
353
354 union
355 {
356 // n for number or numerical interpretation
357 T n{};
358
359 // b for binary digits - b is array of type std::byte
360 std::byte b[byte_size];
361 };
362
363 // most significant byte
364 const std::byte& msb() const noexcept
365 {
366 return b[byte_size - 1];
367 }
368
369 // most significant byte
370 std::byte& msb() noexcept
371 {
372 return b[byte_size - 1];
373 }
374
375 // least significant byte
376 const std::byte& lsb() const noexcept
377 {
378 return b[0];
379 }
380
381 // least significant byte
382 std::byte& lsb() noexcept
383 {
384 return b[0];
385 }
386
387 template<typename S, typename = tpf::types::enable_if_numerical_number_t<S>>
388 bit_pattern& operator=(S s) noexcept
389 {
390 if constexpr(tpf::types::is_real_number_v<S>)
391 {
392 // s is a floating point type
393 this->n = (T) std::round(s); // 1. round(), then 2. typecast S to T
394 }
395 else
396 {
397 // s is an integral type
398 this->n = (T)s; // typecast S to T
399 }
400
401 return *this;
402 }
403
404 // we allow both integral and real numbers to be assigned by this class
405 template<typename S, typename = tpf::types::enable_if_numerical_number_t<S>>
406 operator S() const noexcept
407 {
408 return (S)this->n;
409 }
410
411 }; // end of class bit_pattern
412}
413
414#define Tpf_SignedCommonType(a, b) tpf::bitwise::signed_common_t<decltype(a), decltype(b)>
415#define Tpf_UnignedCommonType(a, b) tpf::bitwise::unsigned_common_t<decltype(a), decltype(b)>
416
417#endif // end of file _TPF_BITWISE_HPP
std::atomic< int > count
Definition: 022-mutex.cpp:10
constexpr bool is_char_v
Definition: tpf_bitwise.hpp:53
constexpr unsigned_t< T > high_bit_mask
Definition: tpf_bitwise.hpp:92
std::make_signed_t< T > signed_t
Definition: tpf_bitwise.hpp:72
std::enable_if_t< std::is_integral< T >::value > enable_if_integral
Definition: tpf_bitwise.hpp:66
std::string numeric_base(T v)
std::string twos_complement(T c)
std::common_type_t< S, T > common_t
Definition: tpf_bitwise.hpp:75
int count_set_bits(T bits)
Definition: tpf_bitwise.hpp:95
std::string to_dec(T v)
std::string numeric_type_info()
std::string to_dec_width(T v)
std::string integral_type_info()
int numeric_width()
std::string to_dec_reverse(T(&v)[N])
constexpr int sizeof_bits
Definition: tpf_bitwise.hpp:88
std::string to_hex_reverse(T(&v)[N])
int field_with(T v)
std::make_unsigned_t< T > unsigned_t
Definition: tpf_bitwise.hpp:69
std::string to_hex(T v)
std::string to_bits(T bits)
unsigned_t< common_t< S, T > > unsigned_common_t
Definition: tpf_bitwise.hpp:85
signed_t< common_t< S, T > > signed_common_t
Definition: tpf_bitwise.hpp:80
constexpr T limits_max
Definition: tpf_bitwise.hpp:55
constexpr T limits_min
Definition: tpf_bitwise.hpp:56
hidden::enable_if_unsigned_integral_t< Type, ReturnType > enable_if_unsigned_integral_t
Definition: tpf_types.hpp:5086
auto minimum(Type_1 a, Type_2 b)
Definition: tpf_types.hpp:196
auto maximum(Type_1 a, Type_2 b)
Definition: tpf_types.hpp:181
std::byte & lsb() noexcept
const std::byte & lsb() const noexcept
std::byte & msb() noexcept
bit_pattern & operator=(S s) noexcept
static constexpr size_t bit_size
std::byte b[byte_size]
static constexpr T max_limit
static constexpr T min_limit
static constexpr size_t byte_size
const std::byte & msb() const noexcept
#define Tpf_GetTypeName(type_arg)
A macro that returns type_arg's string name.
Definition: tpf_types.hpp:1422