// The MIT License (MIT) // // Copyright (c) 2018 Mateusz Pusz // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. #include #include #include #include #ifdef MP_UNITS_IMPORT_STD import std; #else #include #include #include #include #include #endif using namespace mp_units; using namespace mp_units::detail; template requires(N == sizeof...(T) && N == sizeof...(I)) std::tuple at(const std::array& idx, std::integer_sequence, const std::vector&... src) { return {src[idx[I]]...}; } template std::vector> cartesian_product(const std::vector&... src) { std::vector> ret; constexpr std::size_t N = sizeof...(src); std::array sizes; { std::size_t n = 1; std::size_t k = 0; for (std::size_t s : {src.size()...}) { sizes[k++] = s; n *= s; } ret.reserve(n); } std::array idx = {}; bool done = false; while (!done) { ret.push_back(at(idx, std::make_index_sequence{}, src...)); for (std::size_t k = 0; k < idx.size(); ++k) { if (++idx[k] < sizes[k]) break; if (k + 1 >= idx.size()) { done = true; break; } idx[k] = 0; } } return ret; } template using half_width_int_for_t = std::conditional_t, min_width_int_t / 2>, min_width_uint_t / 2>>; template requires(integer_rep_width_v == integer_rep_width_v) auto combine_bits(Hi hi, Lo lo) { using ret_t = double_width_int_for_t; return (static_cast(hi) << integer_rep_width_v)+static_cast(lo); } template void check(double_width_int value, V&& visitor) { using DT = double_width_int_for_t; auto as_standard_int = static_cast
(value); auto expected = visitor(as_standard_int); auto actual = visitor(value); auto actual_as_standard = static_cast
(actual); REQUIRE(actual_as_standard == expected); } // Produce some test integers in the vicinity (~ +-1, modulo overflow) // of those areas in their representation at risk of causing problems: // intmin,intmin/2,zero,intmax/2,intmax... template std::vector test_values() { using U = std::make_unsigned_t; std::vector ret; for (int msb : {0, 1, 2, 3}) { // vicinities reached from msb=: // 0: signed: zero; unsigned: zero, intmin and intmax // 1: signed: intmax/2 // 2: unsigned: intmax/2; signed: intmin and intmax // 3: signed: intmin/2 auto ref = static_cast(msb) << (integer_rep_width_v - 2); for (int lsb_corr : {-2, -1, 0, 1, 2}) { auto corr = static_cast(lsb_corr); U value = ref + corr; ret.push_back(static_cast(value)); } } return ret; } using u32 = std::uint32_t; using i32 = std::int32_t; using u64 = std::uint64_t; using i64 = std::int64_t; using du32 = double_width_int; using di32 = double_width_int; MP_UNITS_DIAGNOSTIC_PUSH // double_width_int implements the same sign-conversion rules as the standard int types, and we want to verify that; // even if those sign-conversion rules are frowned upon. MP_UNITS_DIAGNOSTIC_IGNORE_SIGN_CONVERSION TEST_CASE("double_width_int addition and subtraction", "[double_width_int]") { SECTION("u32x2 +/- u32") { for (auto [lhi, llo, rhs] : cartesian_product(test_values(), test_values(), test_values())) { CAPTURE(lhi, llo, rhs); auto lhs = double_width_int::from_hi_lo(lhi, llo); check(lhs, [r = rhs](auto v) { return v + r; }); check(lhs, [r = rhs](auto v) { return v - r; }); check(lhs, [r = rhs](auto v) { return r - v; }); } } SECTION("u32x2 +/- i32") { for (auto [lhi, llo, rhs] : cartesian_product(test_values(), test_values(), test_values())) { CAPTURE(lhi, llo, rhs); auto lhs = double_width_int::from_hi_lo(lhi, llo); check(lhs, [r = rhs](auto v) { return v + r; }); check(lhs, [r = rhs](auto v) { return v - r; }); check(lhs, [r = rhs](auto v) { return r - v; }); } } SECTION("i32x2 +/- u32") { for (auto [lhi, llo, rhs] : cartesian_product(test_values(), test_values(), test_values())) { CAPTURE(lhi, llo, rhs); auto lhs = double_width_int::from_hi_lo(lhi, llo); check(lhs, [r = rhs](auto v) { return v + r; }); check(lhs, [r = rhs](auto v) { return v - r; }); check(lhs, [r = rhs](auto v) { return r - v; }); } } SECTION("i32x2 +/- i32") { for (auto [lhi, llo, rhs] : cartesian_product(test_values(), test_values(), test_values())) { CAPTURE(lhi, llo, rhs); auto lhs = double_width_int::from_hi_lo(lhi, llo); check(lhs, [r = rhs](auto v) { return v + r; }); check(lhs, [r = rhs](auto v) { return v - r; }); check(lhs, [r = rhs](auto v) { return r - v; }); } } } TEST_CASE("double_width_int multiplication", "[double_width_int]") { SECTION("u32 * u32") { for (auto [lhs, rhs] : cartesian_product(test_values(), test_values())) { CAPTURE(lhs, rhs); u64 expected = u64{lhs} * u64{rhs}; auto actual = double_width_int::wide_product_of(lhs, rhs); auto actual_as_std = static_cast(actual); REQUIRE(actual_as_std == expected); } } SECTION("i32 * u32") { for (auto [lhs, rhs] : cartesian_product(test_values(), test_values())) { CAPTURE(lhs, rhs); i64 expected = i64{lhs} * i64{rhs}; auto actual = double_width_int::wide_product_of(lhs, rhs); auto actual_as_std = static_cast(actual); REQUIRE(actual_as_std == expected); } } SECTION("u32x2 * u32") { for (auto [lhi, llo, rhs] : cartesian_product(test_values(), test_values(), test_values())) { CAPTURE(lhi, llo, rhs); auto lhs = double_width_int::from_hi_lo(lhi, llo); check(lhs, [r = rhs](auto v) { return v * r; }); } } SECTION("u32x2 * i32") { for (auto [lhi, llo, rhs] : cartesian_product(test_values(), test_values(), test_values())) { CAPTURE(lhi, llo, rhs); auto lhs = double_width_int::from_hi_lo(lhi, llo); check(lhs, [r = rhs](auto v) { return v * r; }); } } SECTION("i32x2 * u32") { for (auto [lhi, llo, rhs] : cartesian_product(test_values(), test_values(), test_values())) { CAPTURE(lhi, llo, rhs); auto lhs = double_width_int::from_hi_lo(lhi, llo); check(lhs, [r = rhs](auto v) { return v * r; }); } } SECTION("i32x2 * i32") { for (auto [lhi, llo, rhs] : cartesian_product(test_values(), test_values(), test_values())) { CAPTURE(lhi, llo, rhs); auto lhs = double_width_int::from_hi_lo(lhi, llo); check(lhs, [r = rhs](auto v) { return v * r; }); } } } MP_UNITS_DIAGNOSTIC_POP