xref: /llvm-project/libcxx/test/std/numerics/bit/bit.cast/bit_cast.pass.cpp (revision c792de28dfaf3a13703e83e4eb09dd44574b3a3e)
1 //===----------------------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 // UNSUPPORTED: c++03, c++11, c++14, c++17
10 
11 // <bit>
12 //
13 // template<class To, class From>
14 //   constexpr To bit_cast(const From& from) noexcept; // C++20
15 
16 #include <array>
17 #include <bit>
18 #include <cassert>
19 #include <cmath>
20 #include <cstdint>
21 #include <cstring>
22 #include <limits>
23 
24 #include "test_macros.h"
25 
26 // std::bit_cast does not preserve padding bits, so if T has padding bits,
27 // the results might not memcmp cleanly.
28 template<bool HasUniqueObjectRepresentations = true, typename T>
29 void test_roundtrip_through_buffer(T from) {
30     struct Buffer { char buffer[sizeof(T)]; };
31     Buffer middle = std::bit_cast<Buffer>(from);
32     T to = std::bit_cast<T>(middle);
33     Buffer middle2 = std::bit_cast<Buffer>(to);
34 
35     assert((from == to) == (from == from)); // because NaN
36 
37     if constexpr (HasUniqueObjectRepresentations) {
38         assert(std::memcmp(&from, &middle, sizeof(T)) == 0);
39         assert(std::memcmp(&to, &middle, sizeof(T)) == 0);
40         assert(std::memcmp(&middle, &middle2, sizeof(T)) == 0);
41     }
42 }
43 
44 template<bool HasUniqueObjectRepresentations = true, typename T>
45 void test_roundtrip_through_nested_T(T from) {
46     struct Nested { T x; };
47     static_assert(sizeof(Nested) == sizeof(T));
48 
49     Nested middle = std::bit_cast<Nested>(from);
50     T to = std::bit_cast<T>(middle);
51     Nested middle2 = std::bit_cast<Nested>(to);
52 
53     assert((from == to) == (from == from)); // because NaN
54 
55     if constexpr (HasUniqueObjectRepresentations) {
56         assert(std::memcmp(&from, &middle, sizeof(T)) == 0);
57         assert(std::memcmp(&to, &middle, sizeof(T)) == 0);
58         assert(std::memcmp(&middle, &middle2, sizeof(T)) == 0);
59     }
60 }
61 
62 template <typename Intermediate, bool HasUniqueObjectRepresentations = true, typename T>
63 void test_roundtrip_through(T from) {
64     static_assert(sizeof(Intermediate) == sizeof(T));
65 
66     Intermediate middle = std::bit_cast<Intermediate>(from);
67     T to = std::bit_cast<T>(middle);
68     Intermediate middle2 = std::bit_cast<Intermediate>(to);
69 
70     assert((from == to) == (from == from)); // because NaN
71 
72     if constexpr (HasUniqueObjectRepresentations) {
73         assert(std::memcmp(&from, &middle, sizeof(T)) == 0);
74         assert(std::memcmp(&to, &middle, sizeof(T)) == 0);
75         assert(std::memcmp(&middle, &middle2, sizeof(T)) == 0);
76     }
77 }
78 
79 template <typename T>
80 constexpr std::array<T, 10> generate_signed_integral_values() {
81     return {std::numeric_limits<T>::min(),
82             std::numeric_limits<T>::min() + 1,
83             static_cast<T>(-2), static_cast<T>(-1),
84             static_cast<T>(0), static_cast<T>(1),
85             static_cast<T>(2), static_cast<T>(3),
86             std::numeric_limits<T>::max() - 1,
87             std::numeric_limits<T>::max()};
88 }
89 
90 template <typename T>
91 constexpr std::array<T, 6> generate_unsigned_integral_values() {
92     return {static_cast<T>(0), static_cast<T>(1),
93             static_cast<T>(2), static_cast<T>(3),
94             std::numeric_limits<T>::max() - 1,
95             std::numeric_limits<T>::max()};
96 }
97 
98 bool tests() {
99     for (bool b : {false, true}) {
100         test_roundtrip_through_nested_T(b);
101         test_roundtrip_through_buffer(b);
102         test_roundtrip_through<char>(b);
103     }
104 
105     for (char c : {'\0', 'a', 'b', 'c', 'd'}) {
106         test_roundtrip_through_nested_T(c);
107         test_roundtrip_through_buffer(c);
108     }
109 
110     // Fundamental signed integer types
111     for (signed char i : generate_signed_integral_values<signed char>()) {
112         test_roundtrip_through_nested_T(i);
113         test_roundtrip_through_buffer(i);
114     }
115 
116     for (short i : generate_signed_integral_values<short>()) {
117         test_roundtrip_through_nested_T(i);
118         test_roundtrip_through_buffer(i);
119     }
120 
121     for (int i : generate_signed_integral_values<int>()) {
122         test_roundtrip_through_nested_T(i);
123         test_roundtrip_through_buffer(i);
124         test_roundtrip_through<float>(i);
125     }
126 
127     for (long i : generate_signed_integral_values<long>()) {
128         test_roundtrip_through_nested_T(i);
129         test_roundtrip_through_buffer(i);
130     }
131 
132     for (long long i : generate_signed_integral_values<long long>()) {
133         test_roundtrip_through_nested_T(i);
134         test_roundtrip_through_buffer(i);
135         test_roundtrip_through<double>(i);
136     }
137 
138     // Fundamental unsigned integer types
139     for (unsigned char i : generate_unsigned_integral_values<unsigned char>()) {
140         test_roundtrip_through_nested_T(i);
141         test_roundtrip_through_buffer(i);
142     }
143 
144     for (unsigned short i : generate_unsigned_integral_values<unsigned short>()) {
145         test_roundtrip_through_nested_T(i);
146         test_roundtrip_through_buffer(i);
147     }
148 
149     for (unsigned int i : generate_unsigned_integral_values<unsigned int>()) {
150         test_roundtrip_through_nested_T(i);
151         test_roundtrip_through_buffer(i);
152         test_roundtrip_through<float>(i);
153     }
154 
155     for (unsigned long i : generate_unsigned_integral_values<unsigned long>()) {
156         test_roundtrip_through_nested_T(i);
157         test_roundtrip_through_buffer(i);
158     }
159 
160     for (unsigned long long i : generate_unsigned_integral_values<unsigned long long>()) {
161         test_roundtrip_through_nested_T(i);
162         test_roundtrip_through_buffer(i);
163         test_roundtrip_through<double>(i);
164     }
165 
166     // Fixed width signed integer types
167     for (std::int32_t i : generate_signed_integral_values<std::int32_t>()) {
168         test_roundtrip_through_nested_T(i);
169         test_roundtrip_through_buffer(i);
170         test_roundtrip_through<int>(i);
171         test_roundtrip_through<std::uint32_t>(i);
172         test_roundtrip_through<float>(i);
173     }
174 
175     for (std::int64_t i : generate_signed_integral_values<std::int64_t>()) {
176         test_roundtrip_through_nested_T(i);
177         test_roundtrip_through_buffer(i);
178         test_roundtrip_through<long long>(i);
179         test_roundtrip_through<std::uint64_t>(i);
180         test_roundtrip_through<double>(i);
181     }
182 
183     // Fixed width unsigned integer types
184     for (std::uint32_t i : generate_unsigned_integral_values<std::uint32_t>()) {
185         test_roundtrip_through_nested_T(i);
186         test_roundtrip_through_buffer(i);
187         test_roundtrip_through<int>(i);
188         test_roundtrip_through<std::int32_t>(i);
189         test_roundtrip_through<float>(i);
190     }
191 
192     for (std::uint64_t i : generate_unsigned_integral_values<std::uint64_t>()) {
193         test_roundtrip_through_nested_T(i);
194         test_roundtrip_through_buffer(i);
195         test_roundtrip_through<long long>(i);
196         test_roundtrip_through<std::int64_t>(i);
197         test_roundtrip_through<double>(i);
198     }
199 
200     // Floating point types
201     for (float i : {0.0f, 1.0f, -1.0f, 10.0f, -10.0f, 1e10f, 1e-10f, 1e20f, 1e-20f, 2.71828f, 3.14159f,
202                     std::nanf(""),
203                     __builtin_nanf("0x55550001"), // NaN with a payload
204                     std::numeric_limits<float>::signaling_NaN(),
205                     std::numeric_limits<float>::quiet_NaN()}) {
206         test_roundtrip_through_nested_T(i);
207         test_roundtrip_through_buffer(i);
208         test_roundtrip_through<int>(i);
209     }
210 
211     for (double i : {0.0, 1.0, -1.0, 10.0, -10.0, 1e10, 1e-10, 1e100, 1e-100,
212                      2.718281828459045,
213                      3.141592653589793238462643383279502884197169399375105820974944,
214                      std::nan(""),
215                      std::numeric_limits<double>::signaling_NaN(),
216                      std::numeric_limits<double>::quiet_NaN()}) {
217         test_roundtrip_through_nested_T(i);
218         test_roundtrip_through_buffer(i);
219         test_roundtrip_through<long long>(i);
220     }
221 
222     for (long double i : {0.0l, 1.0l, -1.0l, 10.0l, -10.0l, 1e10l, 1e-10l, 1e100l, 1e-100l,
223                           2.718281828459045l,
224                           3.141592653589793238462643383279502884197169399375105820974944l,
225                           std::nanl(""),
226                           std::numeric_limits<long double>::signaling_NaN(),
227                           std::numeric_limits<long double>::quiet_NaN()}) {
228         // Note that x86's `long double` has 80 value bits and 48 padding bits.
229         test_roundtrip_through_nested_T<false>(i);
230         test_roundtrip_through_buffer<false>(i);
231 
232 #ifdef TEST_LONG_DOUBLE_IS_DOUBLE
233         test_roundtrip_through<double, false>(i);
234 #endif
235 #if defined(__SIZEOF_INT128__) && __SIZEOF_LONG_DOUBLE__ == __SIZEOF_INT128__ &&                                       \
236     !TEST_HAS_FEATURE(memory_sanitizer) // Some bits are just padding.
237         test_roundtrip_through<__int128_t, false>(i);
238         test_roundtrip_through<__uint128_t, false>(i);
239 #endif
240     }
241 
242     // Test pointers
243     {
244         {
245             int obj = 3;
246             void* p = &obj;
247             test_roundtrip_through_nested_T(p);
248             test_roundtrip_through_buffer(p);
249             test_roundtrip_through<void*>(p);
250             test_roundtrip_through<char*>(p);
251             test_roundtrip_through<int*>(p);
252         }
253         {
254             int obj = 3;
255             int* p = &obj;
256             test_roundtrip_through_nested_T(p);
257             test_roundtrip_through_buffer(p);
258             test_roundtrip_through<int*>(p);
259             test_roundtrip_through<char*>(p);
260             test_roundtrip_through<void*>(p);
261         }
262     }
263 
264     return true;
265 }
266 
267 // TODO: There doesn't seem to be a way to perform non-trivial correctness
268 //       tests inside constexpr.
269 constexpr bool basic_constexpr_test() {
270     struct Nested { char buffer[sizeof(int)]; };
271     int from = 3;
272     Nested middle = std::bit_cast<Nested>(from);
273     int to = std::bit_cast<int>(middle);
274     assert(from == to);
275     return true;
276 }
277 
278 int main(int, char**) {
279     tests();
280     static_assert(basic_constexpr_test());
281     return 0;
282 }
283