TestCasting.cpp (17223B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "mozilla/Casting.h" 8 9 #include <stdint.h> 10 #include <cstdint> 11 #include <limits> 12 #include <type_traits> 13 #include <iostream> 14 #include <tuple> 15 #include <type_traits> 16 17 using mozilla::AssertedCast; 18 using mozilla::BitwiseCast; 19 using mozilla::SaturatingCast; 20 using mozilla::detail::IsInBounds; 21 22 static const uint8_t floatMantissaBitsPlusOne = 24; 23 static const uint8_t doubleMantissaBitsPlusOne = 53; 24 25 template <typename Uint, typename Ulong, bool = (sizeof(Uint) == sizeof(Ulong))> 26 struct UintUlongBitwiseCast; 27 28 template <typename Uint, typename Ulong> 29 struct UintUlongBitwiseCast<Uint, Ulong, true> { 30 static void test() { 31 MOZ_RELEASE_ASSERT(BitwiseCast<Ulong>(Uint(8675309)) == Ulong(8675309)); 32 } 33 }; 34 35 template <typename Uint, typename Ulong> 36 struct UintUlongBitwiseCast<Uint, Ulong, false> { 37 static void test() {} 38 }; 39 40 static void TestBitwiseCast() { 41 MOZ_RELEASE_ASSERT(BitwiseCast<int>(int(8675309)) == int(8675309)); 42 UintUlongBitwiseCast<unsigned int, unsigned long>::test(); 43 } 44 45 static void TestSameSize() { 46 MOZ_RELEASE_ASSERT((IsInBounds<int16_t, int16_t>(int16_t(0)))); 47 MOZ_RELEASE_ASSERT((IsInBounds<int16_t, int16_t>(int16_t(INT16_MIN)))); 48 MOZ_RELEASE_ASSERT((IsInBounds<int16_t, int16_t>(int16_t(INT16_MAX)))); 49 MOZ_RELEASE_ASSERT((IsInBounds<uint16_t, uint16_t>(uint16_t(UINT16_MAX)))); 50 MOZ_RELEASE_ASSERT((IsInBounds<uint16_t, int16_t>(uint16_t(0)))); 51 MOZ_RELEASE_ASSERT((!IsInBounds<uint16_t, int16_t>(uint16_t(-1)))); 52 MOZ_RELEASE_ASSERT((!IsInBounds<int16_t, uint16_t>(int16_t(-1)))); 53 MOZ_RELEASE_ASSERT((IsInBounds<int16_t, uint16_t>(int16_t(INT16_MAX)))); 54 MOZ_RELEASE_ASSERT((!IsInBounds<int16_t, uint16_t>(int16_t(INT16_MIN)))); 55 MOZ_RELEASE_ASSERT((IsInBounds<int32_t, uint32_t>(int32_t(INT32_MAX)))); 56 MOZ_RELEASE_ASSERT((!IsInBounds<int32_t, uint32_t>(int32_t(INT32_MIN)))); 57 } 58 59 static void TestToBiggerSize() { 60 MOZ_RELEASE_ASSERT((IsInBounds<int16_t, int32_t>(int16_t(0)))); 61 MOZ_RELEASE_ASSERT((IsInBounds<int16_t, int32_t>(int16_t(INT16_MIN)))); 62 MOZ_RELEASE_ASSERT((IsInBounds<int16_t, int32_t>(int16_t(INT16_MAX)))); 63 MOZ_RELEASE_ASSERT((IsInBounds<uint16_t, uint32_t>(uint16_t(UINT16_MAX)))); 64 MOZ_RELEASE_ASSERT((IsInBounds<uint16_t, int32_t>(uint16_t(0)))); 65 MOZ_RELEASE_ASSERT((IsInBounds<uint16_t, int32_t>(uint16_t(-1)))); 66 MOZ_RELEASE_ASSERT((!IsInBounds<int16_t, uint32_t>(int16_t(-1)))); 67 MOZ_RELEASE_ASSERT((IsInBounds<int16_t, uint32_t>(int16_t(INT16_MAX)))); 68 MOZ_RELEASE_ASSERT((!IsInBounds<int16_t, uint32_t>(int16_t(INT16_MIN)))); 69 MOZ_RELEASE_ASSERT((IsInBounds<int32_t, uint64_t>(int32_t(INT32_MAX)))); 70 MOZ_RELEASE_ASSERT((!IsInBounds<int32_t, uint64_t>(int32_t(INT32_MIN)))); 71 } 72 73 static void TestToSmallerSize() { 74 MOZ_RELEASE_ASSERT((IsInBounds<int16_t, int8_t>(int16_t(0)))); 75 MOZ_RELEASE_ASSERT((!IsInBounds<int16_t, int8_t>(int16_t(INT16_MIN)))); 76 MOZ_RELEASE_ASSERT((!IsInBounds<int16_t, int8_t>(int16_t(INT16_MAX)))); 77 MOZ_RELEASE_ASSERT((!IsInBounds<uint16_t, uint8_t>(uint16_t(UINT16_MAX)))); 78 MOZ_RELEASE_ASSERT((IsInBounds<uint16_t, int8_t>(uint16_t(0)))); 79 MOZ_RELEASE_ASSERT((!IsInBounds<uint16_t, int8_t>(uint16_t(-1)))); 80 MOZ_RELEASE_ASSERT((!IsInBounds<int16_t, uint8_t>(int16_t(-1)))); 81 MOZ_RELEASE_ASSERT((!IsInBounds<int16_t, uint8_t>(int16_t(INT16_MAX)))); 82 MOZ_RELEASE_ASSERT((!IsInBounds<int16_t, uint8_t>(int16_t(INT16_MIN)))); 83 MOZ_RELEASE_ASSERT((!IsInBounds<int32_t, uint16_t>(int32_t(INT32_MAX)))); 84 MOZ_RELEASE_ASSERT((!IsInBounds<int32_t, uint16_t>(int32_t(INT32_MIN)))); 85 86 // Boundary cases 87 MOZ_RELEASE_ASSERT((!IsInBounds<int64_t, int32_t>(int64_t(INT32_MIN) - 1))); 88 MOZ_RELEASE_ASSERT((IsInBounds<int64_t, int32_t>(int64_t(INT32_MIN)))); 89 MOZ_RELEASE_ASSERT((IsInBounds<int64_t, int32_t>(int64_t(INT32_MIN) + 1))); 90 MOZ_RELEASE_ASSERT((IsInBounds<int64_t, int32_t>(int64_t(INT32_MAX) - 1))); 91 MOZ_RELEASE_ASSERT((IsInBounds<int64_t, int32_t>(int64_t(INT32_MAX)))); 92 MOZ_RELEASE_ASSERT((!IsInBounds<int64_t, int32_t>(int64_t(INT32_MAX) + 1))); 93 94 MOZ_RELEASE_ASSERT((!IsInBounds<int64_t, uint32_t>(int64_t(-1)))); 95 MOZ_RELEASE_ASSERT((IsInBounds<int64_t, uint32_t>(int64_t(0)))); 96 MOZ_RELEASE_ASSERT((IsInBounds<int64_t, uint32_t>(int64_t(1)))); 97 MOZ_RELEASE_ASSERT((IsInBounds<int64_t, uint32_t>(int64_t(UINT32_MAX) - 1))); 98 MOZ_RELEASE_ASSERT((IsInBounds<int64_t, uint32_t>(int64_t(UINT32_MAX)))); 99 MOZ_RELEASE_ASSERT((!IsInBounds<int64_t, uint32_t>(int64_t(UINT32_MAX) + 1))); 100 } 101 102 template <typename In, typename Out> 103 void checkBoundariesFloating(In aEpsilon = {}, Out aIntegerOffset = {}) { 104 // Check the max value of the input float can't be represented as an integer. 105 // This is true for all floating point and integer width. 106 MOZ_RELEASE_ASSERT((!IsInBounds<In, Out>(std::numeric_limits<In>::max()))); 107 // Check that the max value of the integer, as a float, minus an offset that 108 // depends on the magnitude, can be represented as an integer. 109 MOZ_RELEASE_ASSERT((IsInBounds<In, Out>( 110 static_cast<In>(std::numeric_limits<Out>::max() - aIntegerOffset)))); 111 // Check that the max value of the integer, plus a number that depends on the 112 // magnitude of the number, can't be represented as this integer (because it 113 // becomes too big). 114 MOZ_RELEASE_ASSERT((!IsInBounds<In, Out>( 115 aEpsilon + static_cast<In>(std::numeric_limits<Out>::max())))); 116 if constexpr (std::is_signed_v<In>) { 117 // Same for negative numbers. 118 MOZ_RELEASE_ASSERT( 119 (!IsInBounds<In, Out>(std::numeric_limits<In>::lowest()))); 120 MOZ_RELEASE_ASSERT((IsInBounds<In, Out>( 121 static_cast<In>(std::numeric_limits<Out>::lowest())))); 122 MOZ_RELEASE_ASSERT((!IsInBounds<In, Out>( 123 static_cast<In>(std::numeric_limits<Out>::lowest()) - aEpsilon))); 124 } else { 125 // Check for negative floats and unsigned integer types. 126 MOZ_RELEASE_ASSERT((!IsInBounds<In, Out>(static_cast<In>(-1)))); 127 } 128 } 129 130 void TestFloatConversion() { 131 MOZ_RELEASE_ASSERT((!IsInBounds<uint64_t, float>(UINT64_MAX))); 132 MOZ_RELEASE_ASSERT((!IsInBounds<uint32_t, float>(UINT32_MAX))); 133 MOZ_RELEASE_ASSERT((IsInBounds<uint16_t, float>(UINT16_MAX))); 134 MOZ_RELEASE_ASSERT((IsInBounds<uint8_t, float>(UINT8_MAX))); 135 136 MOZ_RELEASE_ASSERT((!IsInBounds<int64_t, float>(INT64_MAX))); 137 MOZ_RELEASE_ASSERT((!IsInBounds<int64_t, float>(INT64_MIN))); 138 MOZ_RELEASE_ASSERT((!IsInBounds<int32_t, float>(INT32_MAX))); 139 MOZ_RELEASE_ASSERT((!IsInBounds<int32_t, float>(INT32_MIN))); 140 MOZ_RELEASE_ASSERT((IsInBounds<int16_t, float>(INT16_MAX))); 141 MOZ_RELEASE_ASSERT((IsInBounds<int16_t, float>(INT16_MIN))); 142 MOZ_RELEASE_ASSERT((IsInBounds<int8_t, float>(INT8_MAX))); 143 MOZ_RELEASE_ASSERT((IsInBounds<int8_t, float>(INT8_MIN))); 144 145 MOZ_RELEASE_ASSERT((!IsInBounds<uint64_t, double>(UINT64_MAX))); 146 MOZ_RELEASE_ASSERT((IsInBounds<uint32_t, double>(UINT32_MAX))); 147 MOZ_RELEASE_ASSERT((IsInBounds<uint16_t, double>(UINT16_MAX))); 148 MOZ_RELEASE_ASSERT((IsInBounds<uint8_t, double>(UINT8_MAX))); 149 150 MOZ_RELEASE_ASSERT((!IsInBounds<int64_t, double>(INT64_MAX))); 151 MOZ_RELEASE_ASSERT((!IsInBounds<int64_t, double>(INT64_MIN))); 152 MOZ_RELEASE_ASSERT((IsInBounds<int32_t, double>(INT32_MAX))); 153 MOZ_RELEASE_ASSERT((IsInBounds<int32_t, double>(INT32_MIN))); 154 MOZ_RELEASE_ASSERT((IsInBounds<int16_t, double>(INT16_MAX))); 155 MOZ_RELEASE_ASSERT((IsInBounds<int16_t, double>(INT16_MIN))); 156 MOZ_RELEASE_ASSERT((IsInBounds<int8_t, double>(INT8_MAX))); 157 MOZ_RELEASE_ASSERT((IsInBounds<int8_t, double>(INT8_MIN))); 158 159 // Floor check 160 MOZ_RELEASE_ASSERT((IsInBounds<float, uint64_t>(4.3))); 161 MOZ_RELEASE_ASSERT((AssertedCast<uint64_t>(4.3f) == 4u)); 162 MOZ_RELEASE_ASSERT((IsInBounds<float, uint32_t>(4.3))); 163 MOZ_RELEASE_ASSERT((AssertedCast<uint32_t>(4.3f) == 4u)); 164 MOZ_RELEASE_ASSERT((IsInBounds<float, uint16_t>(4.3))); 165 MOZ_RELEASE_ASSERT((AssertedCast<uint16_t>(4.3f) == 4u)); 166 MOZ_RELEASE_ASSERT((IsInBounds<float, uint8_t>(4.3))); 167 MOZ_RELEASE_ASSERT((AssertedCast<uint8_t>(4.3f) == 4u)); 168 169 MOZ_RELEASE_ASSERT((IsInBounds<float, int64_t>(4.3))); 170 MOZ_RELEASE_ASSERT((AssertedCast<int64_t>(4.3f) == 4u)); 171 MOZ_RELEASE_ASSERT((IsInBounds<float, int32_t>(4.3))); 172 MOZ_RELEASE_ASSERT((AssertedCast<int32_t>(4.3f) == 4u)); 173 MOZ_RELEASE_ASSERT((IsInBounds<float, int16_t>(4.3))); 174 MOZ_RELEASE_ASSERT((AssertedCast<int16_t>(4.3f) == 4u)); 175 MOZ_RELEASE_ASSERT((IsInBounds<float, int8_t>(4.3))); 176 MOZ_RELEASE_ASSERT((AssertedCast<int8_t>(4.3f) == 4u)); 177 178 MOZ_RELEASE_ASSERT((IsInBounds<float, int64_t>(-4.3))); 179 MOZ_RELEASE_ASSERT((AssertedCast<int64_t>(-4.3f) == -4)); 180 MOZ_RELEASE_ASSERT((IsInBounds<float, int32_t>(-4.3))); 181 MOZ_RELEASE_ASSERT((AssertedCast<int32_t>(-4.3f) == -4)); 182 MOZ_RELEASE_ASSERT((IsInBounds<float, int16_t>(-4.3))); 183 MOZ_RELEASE_ASSERT((AssertedCast<int16_t>(-4.3f) == -4)); 184 MOZ_RELEASE_ASSERT((IsInBounds<float, int8_t>(-4.3))); 185 MOZ_RELEASE_ASSERT((AssertedCast<int8_t>(-4.3f) == -4)); 186 187 // Bound check for float to unsigned integer conversion. The parameters are 188 // espilons and offsets allowing to check boundaries, that depend on the 189 // magnitude of the numbers. 190 checkBoundariesFloating<double, uint64_t>(2049.); 191 checkBoundariesFloating<double, uint32_t>(1.); 192 checkBoundariesFloating<double, uint16_t>(1.); 193 checkBoundariesFloating<double, uint8_t>(1.); 194 // Large number because of the lack of precision of floats at this magnitude 195 checkBoundariesFloating<float, uint64_t>(1.1e12f); 196 checkBoundariesFloating<float, uint32_t>(1.f, 128u); 197 checkBoundariesFloating<float, uint16_t>(1.f); 198 checkBoundariesFloating<float, uint8_t>(1.f); 199 200 checkBoundariesFloating<double, int64_t>(1025.); 201 checkBoundariesFloating<double, int32_t>(1.); 202 checkBoundariesFloating<double, int16_t>(1.); 203 checkBoundariesFloating<double, int8_t>(1.); 204 // Large number because of the lack of precision of floats at this magnitude 205 checkBoundariesFloating<float, int64_t>(1.1e12f); 206 checkBoundariesFloating<float, int32_t>(256.f, 64u); 207 checkBoundariesFloating<float, int16_t>(1.f); 208 checkBoundariesFloating<float, int8_t>(1.f); 209 210 // Integer to floating point, boundary cases 211 MOZ_RELEASE_ASSERT(!(IsInBounds<int64_t, float>( 212 int64_t(std::pow(2, floatMantissaBitsPlusOne)) + 1))); 213 MOZ_RELEASE_ASSERT((IsInBounds<int64_t, float>( 214 int64_t(std::pow(2, floatMantissaBitsPlusOne))))); 215 MOZ_RELEASE_ASSERT((IsInBounds<int64_t, float>( 216 int64_t(std::pow(2, floatMantissaBitsPlusOne)) - 1))); 217 218 MOZ_RELEASE_ASSERT(!(IsInBounds<int64_t, float>( 219 int64_t(-std::pow(2, floatMantissaBitsPlusOne)) - 1))); 220 MOZ_RELEASE_ASSERT((IsInBounds<int64_t, float>( 221 int64_t(-std::pow(2, floatMantissaBitsPlusOne))))); 222 MOZ_RELEASE_ASSERT((IsInBounds<int64_t, float>( 223 int64_t(-std::pow(2, floatMantissaBitsPlusOne)) + 1))); 224 225 MOZ_RELEASE_ASSERT(!(IsInBounds<int64_t, double>( 226 uint64_t(std::pow(2, doubleMantissaBitsPlusOne)) + 1))); 227 MOZ_RELEASE_ASSERT((IsInBounds<int64_t, double>( 228 uint64_t(std::pow(2, doubleMantissaBitsPlusOne))))); 229 MOZ_RELEASE_ASSERT((IsInBounds<int64_t, double>( 230 uint64_t(std::pow(2, doubleMantissaBitsPlusOne)) - 1))); 231 232 MOZ_RELEASE_ASSERT(!(IsInBounds<int64_t, double>( 233 int64_t(-std::pow(2, doubleMantissaBitsPlusOne)) - 1))); 234 MOZ_RELEASE_ASSERT((IsInBounds<int64_t, double>( 235 int64_t(-std::pow(2, doubleMantissaBitsPlusOne))))); 236 MOZ_RELEASE_ASSERT((IsInBounds<int64_t, double>( 237 int64_t(-std::pow(2, doubleMantissaBitsPlusOne)) + 1))); 238 239 MOZ_RELEASE_ASSERT(!(IsInBounds<uint64_t, double>(UINT64_MAX))); 240 MOZ_RELEASE_ASSERT(!(IsInBounds<int64_t, double>(INT64_MAX))); 241 MOZ_RELEASE_ASSERT(!(IsInBounds<int64_t, double>(INT64_MIN))); 242 243 MOZ_RELEASE_ASSERT( 244 !(IsInBounds<double, float>(std::numeric_limits<double>::max()))); 245 MOZ_RELEASE_ASSERT( 246 !(IsInBounds<double, float>(-std::numeric_limits<double>::max()))); 247 } 248 249 #define ASSERT_EQ(a, b) \ 250 if ((a) != (b)) { \ 251 std::cerr << __FILE__ << ":" << __LINE__ << " Actual: " << +(a) << ", " \ 252 << "expected: " << +(b) << std::endl; \ 253 MOZ_CRASH(); \ 254 } 255 256 #ifdef ENABLE_DEBUG_PRINT 257 # define DEBUG_PRINT(in, out) \ 258 std::cout << "\tIn: " << +in << ", " << "out: " << +out << std::endl; 259 #else 260 # define DEBUG_PRINT(in, out) 261 #endif 262 263 template <typename In, typename Out> 264 void TestTypePairImpl() { 265 std::cout << __PRETTY_FUNCTION__ << std::endl; 266 std::cout << std::fixed; 267 // Test casting infinities to integer works 268 if constexpr (std::is_floating_point_v<In> && 269 !std::is_floating_point_v<Out>) { 270 Out v = SaturatingCast<Out>(std::numeric_limits<In>::infinity()); 271 ASSERT_EQ(v, std::numeric_limits<Out>::max()); 272 v = SaturatingCast<Out>(-std::numeric_limits<In>::infinity()); 273 ASSERT_EQ(v, std::numeric_limits<Out>::lowest()); 274 } 275 // Saturation of a floating point value that is infinity is infinity 276 if constexpr (std::is_floating_point_v<Out> && std::is_floating_point_v<In>) { 277 In in = std::numeric_limits<In>::infinity(); 278 Out v = SaturatingCast<Out>(in); 279 DEBUG_PRINT(in, v); 280 ASSERT_EQ(v, std::numeric_limits<Out>::infinity()); 281 in = -std::numeric_limits<In>::infinity(); 282 v = SaturatingCast<In>(in); 283 DEBUG_PRINT(in, v); 284 ASSERT_EQ(v, -std::numeric_limits<Out>::infinity()); 285 return; 286 } else { 287 if constexpr (sizeof(In) > sizeof(Out) && std::is_integral_v<Out>) { 288 // Test with a value just outside the range of the output type 289 In in = static_cast<In>(std::numeric_limits<Out>::max()) + 1ull; 290 Out v = SaturatingCast<Out>(in); 291 DEBUG_PRINT(in, v); 292 ASSERT_EQ(v, std::numeric_limits<Out>::max()); 293 294 if (std::is_signed_v<In>) { 295 // Test with a value just below the range of the output type 296 Out lowest = std::numeric_limits<Out>::lowest(); 297 in = static_cast<In>(lowest) - 1; 298 v = SaturatingCast<Out>(in); 299 DEBUG_PRINT(in, v); 300 if constexpr (std::is_signed_v<In> && !std::is_signed_v<Out>) { 301 ASSERT_EQ(v, 0); 302 } else { 303 ASSERT_EQ(v, std::numeric_limits<Out>::lowest()); 304 } 305 } 306 } else if constexpr (std::is_integral_v<In> && std::is_integral_v<Out> && 307 sizeof(In) == sizeof(Out) && !std::is_signed_v<In> && 308 std::is_signed_v<Out>) { 309 // Test that max uintXX_t saturates to max intXX_t 310 In in = static_cast<In>(std::numeric_limits<Out>::max()) + 1; 311 Out v = SaturatingCast<Out>(in); 312 DEBUG_PRINT(in, v); 313 ASSERT_EQ(v, std::numeric_limits<Out>::max()); 314 } 315 316 // SaturatingCast of zero is zero 317 In in = static_cast<In>(0); 318 Out v = SaturatingCast<Out>(in); 319 DEBUG_PRINT(in, v); 320 ASSERT_EQ(v, 0); 321 322 if constexpr (sizeof(In) >= sizeof(Out) && std::is_signed_v<Out> && 323 std::is_signed_v<In>) { 324 // Test with a value within the range of the output type 325 In in = static_cast<In>(std::numeric_limits<Out>::max() / 2); 326 Out v = SaturatingCast<Out>(in); 327 DEBUG_PRINT(in, v); 328 ASSERT_EQ(v, in); 329 330 // Test with a negative value within the range of the output type 331 in = static_cast<In>(std::numeric_limits<Out>::lowest() / 2); 332 v = SaturatingCast<Out>(in); 333 DEBUG_PRINT(in, v); 334 ASSERT_EQ(v, in); 335 } 336 } 337 } 338 339 template <typename In, typename Out> 340 void TestTypePair() { 341 constexpr bool fromFloat = std::is_floating_point_v<In>; 342 constexpr bool toFloat = std::is_floating_point_v<Out>; 343 // Don't test casting to the same type 344 if constexpr (!std::is_same_v<In, Out>) { 345 if constexpr ((fromFloat && !toFloat) || (!fromFloat && !toFloat)) { 346 TestTypePairImpl<In, Out>(); 347 } 348 } 349 } 350 351 template <typename T, typename... Ts> 352 void for_each_type_pair(std::tuple<T, Ts...>) { 353 (TestTypePair<T, Ts>(), ...); 354 (TestTypePair<Ts, T>(), ...); 355 if constexpr (sizeof...(Ts) > 1) { 356 for_each_type_pair(std::tuple<Ts...>{}); 357 } 358 } 359 360 template <typename... Args> 361 void TestSaturatingCastImpl() { 362 for_each_type_pair(std::tuple<Args...>{}); 363 } 364 365 template <typename T, typename... Ts> 366 void TestFirstToOthers() { 367 (TestTypePair<T, Ts>(), ...); 368 } 369 370 void TestSaturatingCast() { 371 // Each integer type against every other 372 TestSaturatingCastImpl<short, int, long, int8_t, uint8_t, int16_t, uint16_t, 373 int32_t, uint32_t, int64_t, uint64_t>(); 374 375 // Floating point types to every integer type 376 TestFirstToOthers<float, short, int, long, int8_t, uint8_t, int16_t, uint16_t, 377 int32_t, uint32_t, int64_t, uint64_t>(); 378 TestFirstToOthers<double, short, int, long, int8_t, uint8_t, int16_t, 379 uint16_t, int32_t, uint32_t, int64_t, uint64_t>(); 380 } 381 382 int main() { 383 TestBitwiseCast(); 384 385 TestSameSize(); 386 TestToBiggerSize(); 387 TestToSmallerSize(); 388 TestFloatConversion(); 389 TestSaturatingCast(); 390 391 return 0; 392 }