xref: /llvm-project/libcxx/test/std/numerics/bit/bit.cast/bit_cast.pass.cpp (revision b1fb3d75c953fa2e02ebddb6ebbf100f99786f0c)
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 // std::bit_cast does not preserve padding bits, so if T has padding bits,
25 // the results might not memcmp cleanly.
26 template<bool HasUniqueObjectRepresentations = true, typename T>
27 void test_roundtrip_through_buffer(T from) {
28     struct Buffer { char buffer[sizeof(T)]; };
29     Buffer middle = std::bit_cast<Buffer>(from);
30     T to = std::bit_cast<T>(middle);
31     Buffer middle2 = std::bit_cast<Buffer>(to);
32 
33     assert((from == to) == (from == from)); // because NaN
34 
35     if constexpr (HasUniqueObjectRepresentations) {
36         assert(std::memcmp(&from, &middle, sizeof(T)) == 0);
37         assert(std::memcmp(&to, &middle, sizeof(T)) == 0);
38         assert(std::memcmp(&middle, &middle2, sizeof(T)) == 0);
39     }
40 }
41 
42 template<bool HasUniqueObjectRepresentations = true, typename T>
43 void test_roundtrip_through_nested_T(T from) {
44     struct Nested { T x; };
45     static_assert(sizeof(Nested) == sizeof(T));
46 
47     Nested middle = std::bit_cast<Nested>(from);
48     T to = std::bit_cast<T>(middle);
49     Nested middle2 = std::bit_cast<Nested>(to);
50 
51     assert((from == to) == (from == from)); // because NaN
52 
53     if constexpr (HasUniqueObjectRepresentations) {
54         assert(std::memcmp(&from, &middle, sizeof(T)) == 0);
55         assert(std::memcmp(&to, &middle, sizeof(T)) == 0);
56         assert(std::memcmp(&middle, &middle2, sizeof(T)) == 0);
57     }
58 }
59 
60 template <typename Intermediate, bool HasUniqueObjectRepresentations = true, typename T>
61 void test_roundtrip_through(T from) {
62     static_assert(sizeof(Intermediate) == sizeof(T));
63 
64     Intermediate middle = std::bit_cast<Intermediate>(from);
65     T to = std::bit_cast<T>(middle);
66     Intermediate middle2 = std::bit_cast<Intermediate>(to);
67 
68     assert((from == to) == (from == from)); // because NaN
69 
70     if constexpr (HasUniqueObjectRepresentations) {
71         assert(std::memcmp(&from, &middle, sizeof(T)) == 0);
72         assert(std::memcmp(&to, &middle, sizeof(T)) == 0);
73         assert(std::memcmp(&middle, &middle2, sizeof(T)) == 0);
74     }
75 }
76 
77 template <typename T>
78 constexpr std::array<T, 10> generate_signed_integral_values() {
79     return {std::numeric_limits<T>::min(),
80             std::numeric_limits<T>::min() + 1,
81             static_cast<T>(-2), static_cast<T>(-1),
82             static_cast<T>(0), static_cast<T>(1),
83             static_cast<T>(2), static_cast<T>(3),
84             std::numeric_limits<T>::max() - 1,
85             std::numeric_limits<T>::max()};
86 }
87 
88 template <typename T>
89 constexpr std::array<T, 6> generate_unsigned_integral_values() {
90     return {static_cast<T>(0), static_cast<T>(1),
91             static_cast<T>(2), static_cast<T>(3),
92             std::numeric_limits<T>::max() - 1,
93             std::numeric_limits<T>::max()};
94 }
95 
96 bool tests() {
97     for (bool b : {false, true}) {
98         test_roundtrip_through_nested_T(b);
99         test_roundtrip_through_buffer(b);
100         test_roundtrip_through<char>(b);
101     }
102 
103     for (char c : {'\0', 'a', 'b', 'c', 'd'}) {
104         test_roundtrip_through_nested_T(c);
105         test_roundtrip_through_buffer(c);
106     }
107 
108     // Fundamental signed integer types
109     for (signed char i : generate_signed_integral_values<signed char>()) {
110         test_roundtrip_through_nested_T(i);
111         test_roundtrip_through_buffer(i);
112     }
113 
114     for (short i : generate_signed_integral_values<short>()) {
115         test_roundtrip_through_nested_T(i);
116         test_roundtrip_through_buffer(i);
117     }
118 
119     for (int i : generate_signed_integral_values<int>()) {
120         test_roundtrip_through_nested_T(i);
121         test_roundtrip_through_buffer(i);
122         test_roundtrip_through<float>(i);
123     }
124 
125     for (long i : generate_signed_integral_values<long>()) {
126         test_roundtrip_through_nested_T(i);
127         test_roundtrip_through_buffer(i);
128     }
129 
130     for (long long i : generate_signed_integral_values<long long>()) {
131         test_roundtrip_through_nested_T(i);
132         test_roundtrip_through_buffer(i);
133         test_roundtrip_through<double>(i);
134     }
135 
136     // Fundamental unsigned integer types
137     for (unsigned char i : generate_unsigned_integral_values<unsigned char>()) {
138         test_roundtrip_through_nested_T(i);
139         test_roundtrip_through_buffer(i);
140     }
141 
142     for (unsigned short i : generate_unsigned_integral_values<unsigned short>()) {
143         test_roundtrip_through_nested_T(i);
144         test_roundtrip_through_buffer(i);
145     }
146 
147     for (unsigned int i : generate_unsigned_integral_values<unsigned int>()) {
148         test_roundtrip_through_nested_T(i);
149         test_roundtrip_through_buffer(i);
150         test_roundtrip_through<float>(i);
151     }
152 
153     for (unsigned long i : generate_unsigned_integral_values<unsigned long>()) {
154         test_roundtrip_through_nested_T(i);
155         test_roundtrip_through_buffer(i);
156     }
157 
158     for (unsigned long long i : generate_unsigned_integral_values<unsigned long long>()) {
159         test_roundtrip_through_nested_T(i);
160         test_roundtrip_through_buffer(i);
161         test_roundtrip_through<double>(i);
162     }
163 
164     // Fixed width signed integer types
165     for (std::int32_t i : generate_signed_integral_values<std::int32_t>()) {
166         test_roundtrip_through_nested_T(i);
167         test_roundtrip_through_buffer(i);
168         test_roundtrip_through<int>(i);
169         test_roundtrip_through<std::uint32_t>(i);
170         test_roundtrip_through<float>(i);
171     }
172 
173     for (std::int64_t i : generate_signed_integral_values<std::int64_t>()) {
174         test_roundtrip_through_nested_T(i);
175         test_roundtrip_through_buffer(i);
176         test_roundtrip_through<long long>(i);
177         test_roundtrip_through<std::uint64_t>(i);
178         test_roundtrip_through<double>(i);
179     }
180 
181     // Fixed width unsigned integer types
182     for (std::uint32_t i : generate_unsigned_integral_values<std::uint32_t>()) {
183         test_roundtrip_through_nested_T(i);
184         test_roundtrip_through_buffer(i);
185         test_roundtrip_through<int>(i);
186         test_roundtrip_through<std::int32_t>(i);
187         test_roundtrip_through<float>(i);
188     }
189 
190     for (std::uint64_t i : generate_unsigned_integral_values<std::uint64_t>()) {
191         test_roundtrip_through_nested_T(i);
192         test_roundtrip_through_buffer(i);
193         test_roundtrip_through<long long>(i);
194         test_roundtrip_through<std::int64_t>(i);
195         test_roundtrip_through<double>(i);
196     }
197 
198     // Floating point types
199     for (float i : {0.0f, 1.0f, -1.0f, 10.0f, -10.0f, 1e10f, 1e-10f, 1e20f, 1e-20f, 2.71828f, 3.14159f,
200                     std::nanf(""),
201                     __builtin_nanf("0x55550001"), // NaN with a payload
202                     std::numeric_limits<float>::signaling_NaN(),
203                     std::numeric_limits<float>::quiet_NaN()}) {
204         test_roundtrip_through_nested_T(i);
205         test_roundtrip_through_buffer(i);
206         test_roundtrip_through<int>(i);
207     }
208 
209     for (double i : {0.0, 1.0, -1.0, 10.0, -10.0, 1e10, 1e-10, 1e100, 1e-100,
210                      2.718281828459045,
211                      3.141592653589793238462643383279502884197169399375105820974944,
212                      std::nan(""),
213                      std::numeric_limits<double>::signaling_NaN(),
214                      std::numeric_limits<double>::quiet_NaN()}) {
215         test_roundtrip_through_nested_T(i);
216         test_roundtrip_through_buffer(i);
217         test_roundtrip_through<long long>(i);
218     }
219 
220     for (long double i : {0.0l, 1.0l, -1.0l, 10.0l, -10.0l, 1e10l, 1e-10l, 1e100l, 1e-100l,
221                           2.718281828459045l,
222                           3.141592653589793238462643383279502884197169399375105820974944l,
223                           std::nanl(""),
224                           std::numeric_limits<long double>::signaling_NaN(),
225                           std::numeric_limits<long double>::quiet_NaN()}) {
226         // Note that x86's `long double` has 80 value bits and 48 padding bits.
227         test_roundtrip_through_nested_T<false>(i);
228         test_roundtrip_through_buffer<false>(i);
229 
230         // On arm64 on Apple platforms, long double is just double, so we don't
231         // test against int128, but instead against double itself. Otherwise,
232         // we test against int128 if we have those types available.
233 #if defined(__aarch64__) && defined(__APPLE__)
234 #   define LONG_DOUBLE_IS_DOUBLE
235 #endif
236 
237 #if defined(LONG_DOUBLE_IS_DOUBLE)
238         test_roundtrip_through<double, false>(i);
239 #elif !defined(_LIBCPP_HAS_NO_INT128)
240         test_roundtrip_through<__int128_t, false>(i);
241         test_roundtrip_through<__uint128_t, false>(i);
242 #endif
243     }
244 
245     return true;
246 }
247 
248 // TODO: There doesn't seem to be a way to perform non-trivial correctness
249 //       tests inside constexpr.
250 constexpr bool basic_constexpr_test() {
251     struct Nested { char buffer[sizeof(int)]; };
252     int from = 3;
253     Nested middle = std::bit_cast<Nested>(from);
254     int to = std::bit_cast<int>(middle);
255     assert(from == to);
256     return true;
257 }
258 
259 int main(int, char**) {
260     tests();
261     static_assert(basic_constexpr_test());
262     return 0;
263 }
264