diff --git a/STYLE_GUIDE.md b/STYLE_GUIDE.md index 24b6cf1..2bfb14d 100644 --- a/STYLE_GUIDE.md +++ b/STYLE_GUIDE.md @@ -73,7 +73,7 @@ - g++ および clang++ の C++20(`-std=c++20`)でコンパイルできるようにする。 - コンパイラ依存の実装にしない。 - 前提を満たさない入力に対する救済処理を過度に入れない。 - - 必要な前提は `assert` などで確認する。 + - 必要な前提は `assert` などで確認する。ただし、`private` な関数について、全ての呼び出し箇所でその関数に必要な前提が満たされることを保証する場合は、`assert` などによる確認を省略してもよい。 - 前提を満たす入力に対する性能を著しく落とすような救済処理を入れない。 - 公開 API の名前は VS Code 等による補完を使用しやすい名前にする。 - 用途から思い出しにくい名前や、短すぎる名前にしない。 diff --git a/internal/int128.hpp b/internal/int128.hpp index 52e0a60..f42950c 100644 --- a/internal/int128.hpp +++ b/internal/int128.hpp @@ -133,7 +133,22 @@ class UInt128 { assert(rhs != UInt128{}); UInt128 q, r; - div_mod_32bit_words(lhs, rhs, q, r); + if (rhs.high_ == 0) { + if (lhs.high_ == 0) { + q = UInt128(lhs.low_ / rhs.low_); + r = UInt128(lhs.low_ % rhs.low_); + } else if (rhs.low_ == 1) { + q = lhs; + r = UInt128{}; + } else { + div_mod_32bit_words(lhs, rhs, q, r); + } + } else if (lhs < rhs) { + q = UInt128{}; + r = lhs; + } else { + div_mod_32bit_words(lhs, rhs, q, r); + } quotient = q; remainder = r; } @@ -336,6 +351,7 @@ class UInt128 { } } + // 呼び出し元で lhs >= rhs が保証されていることを仮定する。 static constexpr void div_mod_32bit_words(UInt128 lhs, UInt128 rhs, UInt128 "ient, UInt128 &remainder) { @@ -348,12 +364,6 @@ class UInt128 { const int rhs_length = word_length(rhs_words); assert(rhs_length > 0); - if (lhs_length < rhs_length || lhs < rhs) { - quotient = UInt128{}; - remainder = lhs; - return; - } - if (rhs_length == 1) { std::uint32_t rem = 0; quotient = div_mod_uint32(lhs, rhs_words[0], rem); diff --git a/verify/standalone-int128.test.cpp b/verify/standalone-int128.test.cpp index 2704d03..5564a0e 100644 --- a/verify/standalone-int128.test.cpp +++ b/verify/standalone-int128.test.cpp @@ -115,12 +115,39 @@ void test_signed_division(Int128 lhs, Int128 rhs) { assert(remainder.is_negative() == lhs.is_negative()); } } +constexpr bool test_uint128_div_mod_fast_paths() { + UInt128 quotient; + UInt128 remainder; + + UInt128::div_mod(UInt128::from_words(0, ~std::uint64_t{}), + UInt128::from_words(0, (std::uint64_t{1} << 63) + 1), + quotient, remainder); + if (quotient != UInt128(1) || + remainder != UInt128::from_words(0, (std::uint64_t{1} << 63) - 2)) { + return false; + } + + const UInt128 wide = + UInt128::from_words(0x123456789abcdef0ULL, 0xfedcba9876543210ULL); + UInt128::div_mod(wide, UInt128(1), quotient, remainder); + if (quotient != wide || remainder != UInt128{}) { + return false; + } + + const UInt128 smaller = UInt128::from_words(1, ~std::uint64_t{}); + const UInt128 larger = UInt128::from_words(2, 0); + UInt128::div_mod(smaller, larger, quotient, remainder); + return quotient == UInt128{} && remainder == smaller; +} } // namespace int main() { using NicheLibrary::Int128; using NicheLibrary::UInt128; + static_assert(test_uint128_div_mod_fast_paths(), + "UInt128 division fast paths must be constexpr-correct."); + static_assert(std::numeric_limits::is_integer, "UInt128 must be integer."); static_assert(!std::numeric_limits::is_signed,