From 1ff404a1e2c94aa41cf947b49bec80fe630a5ac2 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Mon, 30 Jun 2025 16:39:13 +0200 Subject: [PATCH 01/12] `_Div_ceil`: _Rename `_Size` template param to `_Int` --- stl/inc/ranges | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 0033e663051..bea9aa2b1f3 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -5470,9 +5470,9 @@ namespace ranges { _EXPORT_STD inline constexpr _Enumerate_fn enumerate; } // namespace views - template - _NODISCARD constexpr _Size _Div_ceil(const _Size _Num, const _Size _Denom) noexcept { - _Size _Result = _Num / _Denom; + template + _NODISCARD constexpr _Int _Div_ceil(const _Int _Num, const _Int _Denom) noexcept { + _Int _Result = _Num / _Denom; if (_Num % _Denom != 0) { ++_Result; } From 87314beff69069311e795f5d0612e4e4dac4bbb7 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Mon, 30 Jun 2025 16:42:33 +0200 Subject: [PATCH 02/12] `_Div_ceil`: Add extra internal assertions --- stl/inc/ranges | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/stl/inc/ranges b/stl/inc/ranges index bea9aa2b1f3..fd583945cd9 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -5472,6 +5472,10 @@ namespace ranges { template _NODISCARD constexpr _Int _Div_ceil(const _Int _Num, const _Int _Denom) noexcept { + _STL_INTERNAL_STATIC_ASSERT(_Signed_integer_like<_Int>); + _STL_INTERNAL_CHECK(_Num >= 0); + _STL_INTERNAL_CHECK(_Denom >= 0); + _Int _Result = _Num / _Denom; if (_Num % _Denom != 0) { ++_Result; From f33e17a1cefcbf537e54de51b748e985713d4b38 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Mon, 30 Jun 2025 19:12:18 +0200 Subject: [PATCH 03/12] `_Div_ceil`: Add benchmark --- benchmarks/CMakeLists.txt | 1 + benchmarks/src/ranges_div_ceil.cpp | 39 ++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 benchmarks/src/ranges_div_ceil.cpp diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index f54fdae227c..ce84b7c717f 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -124,6 +124,7 @@ add_benchmark(nth_element src/nth_element.cpp) add_benchmark(path_lexically_normal src/path_lexically_normal.cpp) add_benchmark(priority_queue_push_range src/priority_queue_push_range.cpp) add_benchmark(random_integer_generation src/random_integer_generation.cpp) +add_benchmark(ranges_div_ceil src/ranges_div_ceil.cpp) add_benchmark(regex_search src/regex_search.cpp) add_benchmark(remove src/remove.cpp) add_benchmark(replace src/replace.cpp) diff --git a/benchmarks/src/ranges_div_ceil.cpp b/benchmarks/src/ranges_div_ceil.cpp new file mode 100644 index 00000000000..2d6a927e8c1 --- /dev/null +++ b/benchmarks/src/ranges_div_ceil.cpp @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include + +using namespace std; + +struct Data { + _Signed128 num; + _Signed128 den; +}; + +constexpr Data d1{_Signed128{0x0000'1111'2222'3333LL, 0x4444'5555'6666'7777LL}, 0x7777'8888}; +static_assert(d1.num > numeric_limits::max()); +static_assert(d1.den <= numeric_limits::max()); + +constexpr Data d2{_Signed128{0x0000'1111'2222'3333LL, 0x4444'5555'6666'7777LL}, 0x7777'8888'9999'AAAA}; +static_assert(d2.num > numeric_limits::max()); +static_assert(d2.den <= numeric_limits::max()); + +constexpr Data d3{ + _Signed128{0x0000'1111'2222'3333LL, 0x4444'5555'6666'7777LL}, _Signed128{0x7777'8888'9999'AAAA, 0xBBBB}}; +static_assert(d3.num > numeric_limits::max()); +static_assert(d3.den > numeric_limits::max()); + +void bm(benchmark::State& state, const Data& data) { + for (auto _ : state) { + benchmark::DoNotOptimize(ranges::_Div_ceil(data.num, data.den)); + } +} + +BENCHMARK_CAPTURE(bm, div_ceil_int128_uint32, d1); +BENCHMARK_CAPTURE(bm, div_ceil_int128_uint64, d2); +BENCHMARK_CAPTURE(bm, div_ceil_int128_int128, d3); + +BENCHMARK_MAIN(); From c1f9e022c7d2975fe10026c181d6a267c5e22080 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Tue, 1 Jul 2025 12:41:12 +0200 Subject: [PATCH 04/12] `__msvc_int128.hpp`: Implement `_Signed128::_Div_ceil` function --- stl/inc/__msvc_int128.hpp | 170 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) diff --git a/stl/inc/__msvc_int128.hpp b/stl/inc/__msvc_int128.hpp index e0a0dd34ddb..4634a764451 100644 --- a/stl/inc/__msvc_int128.hpp +++ b/stl/inc/__msvc_int128.hpp @@ -682,6 +682,168 @@ struct return _Num; } +#if _HAS_CXX23 +#if !_STL_128_DIV_INTRINSICS + _NODISCARD static constexpr _Base128 _Div_ceil(const _Base128& _Num, const uint32_t _Den) noexcept { + _Base128 _Result; + _Result._Word[1] = _Num._Word[1] / _Den; + uint64_t _Rem = ((_Num._Word[1] % _Den) << 32) | (_Num._Word[0] >> 32); + _Result._Word[0] = (_Rem / _Den) << 32; + _Rem = ((_Rem % _Den) << 32) | static_cast(_Num._Word[0]); + _Result._Word[0] |= static_cast(_Rem / _Den); + if (_Rem % _Den != 0) { + ++_Result; + } + + return _Result; + } +#endif // !_STL_128_DIV_INTRINSICS + + _NODISCARD static constexpr _Base128 _Div_ceil(const _Base128& _Num, const uint64_t _Den) noexcept { + _Base128 _Result; + _Result._Word[1] = _Num._Word[1] / _Den; + uint64_t _Rem; + _Result._Word[0] = _UDiv128(_Num._Word[1] % _Den, _Num._Word[0], _Den, _Rem); + if (_Rem != 0) { + ++_Result; + } + + return _Result; + } + + _NODISCARD static constexpr _Base128 _Div_ceil(_Base128 _Num, _Base128 _Den) noexcept { + // establish _Den < _Num and _Num._Word[1] > 0 + if (_Den._Word[1] >= _Num._Word[1]) { + if (_Den._Word[1] > _Num._Word[1]) { + return _Num != 0 ? 1 : 0; + } + + _Base128 _Rem; + if (_Den._Word[0] <= _Num._Word[0]) { + _Rem = _Num._Word[1] == 0 ? _Num._Word[0] % _Den._Word[0] : _Num._Word[0] - _Den._Word[0]; + } else { + _Rem = _Num; + } + + _Base128 _Result = _Num._Word[1] == 0 ? _Num._Word[0] / _Den._Word[0] : _Num._Word[0] >= _Den._Word[0]; + if (_Rem != 0) { + ++_Result; + } + + return _Result; + } + + // establish _Den has more than 1 non-zero "digit" + if (_Den._Word[1] == 0) { +#if !_STL_128_DIV_INTRINSICS + if (_Den._Word[0] < (1ull << 32)) { + return _Div_ceil(_Num, static_cast(_Den._Word[0])); + } else +#endif // !_STL_128_DIV_INTRINSICS + { + return _Div_ceil(_Num, _Den._Word[0]); + } + } + + _Base128 _Result; +#if _STL_128_INTRINSICS + // Knuth 4.3.1D, 2-digit by 2-digit divide in base 2^64 + // _STL_INTERNAL_CHECK(_Den._Word[1] != 0); + // _STL_INTERNAL_CHECK(_Num._Word[1] > _Den._Word[1]); + // Normalize by shifting both left until _Den's high bit is set (So _Den's high digit is >= b / 2) + const auto __d = _STD _Countl_zero_internal(_Den._Word[1]); + _Den <<= __d; + auto _High_digit = __d == 0 ? 0 : _Num._Word[1] >> (64 - __d); // This creates a third digit for _Num + _Num <<= __d; + + _Base128 __qhat; + __qhat._Word[1] = _High_digit >= _Den._Word[1]; + uint64_t __rhat; + __qhat._Word[0] = _UDiv128(_High_digit >= _Den._Word[1] ? _High_digit - _Den._Word[1] : _High_digit, + _Num._Word[1], _Den._Word[1], __rhat); + + for (;;) { + if (__qhat._Word[1] > 0) { + --__qhat; + } else { + _Base128 _Prod; + _Prod._Word[0] = _UMul128(__qhat._Word[0], _Den._Word[0], _Prod._Word[1]); + if (_Prod <= _Base128{_Num._Word[0], __rhat}) { + break; + } + --__qhat._Word[0]; + } + + const auto _Sum = __rhat + _Den._Word[1]; + if (__rhat > _Sum) { + break; + } + __rhat = _Sum; + } + // _STL_INTERNAL_CHECK(__qhat._Word[1] == 0); + + // [_High_digit | _Num] -= __qhat * _Den [Since __qhat < b, this is 3-digit - 1-digit * 2-digit] + uint64_t _Prod0_hi; + uint64_t _Prod_lo = _UMul128(__qhat._Word[0], _Den._Word[0], _Prod0_hi); + auto _Borrow = _SubBorrow64(0, _Num._Word[0], _Prod_lo, _Num._Word[0]); + uint64_t _Prod1_hi; + _Prod_lo = _UMul128(__qhat._Word[0], _Den._Word[1], _Prod1_hi); + _Prod1_hi += _AddCarry64(0, _Prod_lo, _Prod0_hi, _Prod_lo); + _Borrow = _SubBorrow64(_Borrow, _Num._Word[1], _Prod_lo, _Num._Word[1]); + _Borrow = _SubBorrow64(_Borrow, _High_digit, _Prod1_hi, _High_digit); + if (_Borrow) { + --__qhat._Word[0]; + auto _Carry = _AddCarry64(0, _Num._Word[0], _Den._Word[0], _Num._Word[0]); + (void) _AddCarry64(_Carry, _Num._Word[1], _Den._Word[1], _Num._Word[1]); + } + _Result = __qhat; +#else // ^^^ 128-bit intrinsics / no such intrinsics vvv + auto __d = _Countl_zero_internal(_Den._Word[1]); + const bool _Three_word_den = __d >= 32; + __d &= 31; + uint32_t __u[5]{ + static_cast(_Num._Word[0] << __d), + static_cast(_Num._Word[0] >> (32 - __d)), + static_cast(_Num._Word[1] << __d), + static_cast(_Num._Word[1] >> (32 - __d)), + 0, + }; + uint32_t __v[4] = { + static_cast(_Den._Word[0] << __d), + static_cast(_Den._Word[0] >> (32 - __d)), + static_cast(_Den._Word[1] << __d), + static_cast(_Den._Word[1] >> (32 - __d)), + }; + if (__d != 0) { + __u[2] |= _Num._Word[0] >> (64 - __d); + __u[4] |= _Num._Word[1] >> (64 - __d); + __v[2] |= _Den._Word[0] >> (64 - __d); + } + + uint32_t __q[2] = {}; + if (_Three_word_den) { + // 4-digit by 3-digit base 2^32 division + _Knuth_4_3_1_D(__u, 5, __v, 3, __q); + // _STL_INTERNAL_CHECK(__u[3] == 0); + } else { + // 4-digit by 4-digit base 2^32 division + _Knuth_4_3_1_D(__u, 5, __v, 4, __q); + } + + _Result = (static_cast(__q[1]) << 32) | __q[0]; + _Num._Word[0] = (static_cast(__u[1]) << 32) | __u[0]; + _Num._Word[1] = (static_cast(__u[3]) << 32) | __u[2]; +#endif // _STL_128_INTRINSICS + + _Num >>= __d; + if (_Num != 0) { + ++_Result; + } + + return _Result; + } +#endif // _HAS_CXX23 + _TEMPLATE_CLASS_INTEGRAL(_Ty) friend constexpr _Ty& operator&=(_Ty& _Left, const _Base128& _Right) noexcept { _Left &= _Right._Word[0]; @@ -1331,6 +1493,14 @@ struct _Signed128 : _Base128 { return _Left; } +#if _HAS_CXX23 + _NODISCARD static constexpr _Signed128 _Div_ceil(_Signed128 _Num, _Signed128 _Den) noexcept { + // _STL_INTERNAL_CHECK(_Num >= 0); + // _STL_INTERNAL_CHECK(_Den >= 0); + return _Signed128{_Base128::_Div_ceil(_Num, _Den)}; + } +#endif // _HAS_CXX23 + _NODISCARD friend constexpr _Signed128 operator&(const _Signed128& _Left, const _Signed128& _Right) noexcept { return _Signed128{_Left._Word[0] & _Right._Word[0], _Left._Word[1] & _Right._Word[1]}; } From 1fc1b980aa23aa54569a5f4ba0df860fb0ad21ac Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Mon, 30 Jun 2025 23:48:31 +0200 Subject: [PATCH 05/12] `__msvc_int128.hpp`: Test `_Signed128::_Div_ceil` --- tests/std/test.lst | 1 + .../tests/P2442R1_signed128_div_ceil/env.lst | 4 + .../tests/P2442R1_signed128_div_ceil/test.cpp | 109 ++++++++++++++++++ 3 files changed, 114 insertions(+) create mode 100644 tests/std/tests/P2442R1_signed128_div_ceil/env.lst create mode 100644 tests/std/tests/P2442R1_signed128_div_ceil/test.cpp diff --git a/tests/std/test.lst b/tests/std/test.lst index 666f255aae7..6310aa68d4b 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -681,6 +681,7 @@ tests\P2440R1_ranges_alg_shift_left tests\P2440R1_ranges_alg_shift_right tests\P2440R1_ranges_numeric_iota tests\P2441R2_views_join_with +tests\P2442R1_signed128_div_ceil tests\P2442R1_views_chunk tests\P2442R1_views_chunk_death tests\P2442R1_views_slide diff --git a/tests/std/tests/P2442R1_signed128_div_ceil/env.lst b/tests/std/tests/P2442R1_signed128_div_ceil/env.lst new file mode 100644 index 00000000000..642f530ffad --- /dev/null +++ b/tests/std/tests/P2442R1_signed128_div_ceil/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_latest_matrix.lst diff --git a/tests/std/tests/P2442R1_signed128_div_ceil/test.cpp b/tests/std/tests/P2442R1_signed128_div_ceil/test.cpp new file mode 100644 index 00000000000..d73cbfb1bc5 --- /dev/null +++ b/tests/std/tests/P2442R1_signed128_div_ceil/test.cpp @@ -0,0 +1,109 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include <__msvc_int128.hpp> +#include +#include + +constexpr void test() { + using std::_Signed128; + + assert(_Signed128::_Div_ceil(0, 1) == 0); + assert(_Signed128::_Div_ceil(1, 1) == 1); + assert(_Signed128::_Div_ceil(2, 1) == 2); + + assert(_Signed128::_Div_ceil(116, 3) == 39); + assert(_Signed128::_Div_ceil(205, 24) == 9); + assert(_Signed128::_Div_ceil(83, 1) == 83); + assert(_Signed128::_Div_ceil(240, 13) == 19); + assert(_Signed128::_Div_ceil(100, 13) == 8); + + assert(_Signed128::_Div_ceil(149, 147) == 2); + assert(_Signed128::_Div_ceil(122, 152) == 1); + assert(_Signed128::_Div_ceil(154, 236) == 1); + assert(_Signed128::_Div_ceil(163, 251) == 1); + assert(_Signed128::_Div_ceil(89, 199) == 1); + + assert(_Signed128::_Div_ceil(1069796746, 23678) == 45182); + assert(_Signed128::_Div_ceil(1200533627, 8126) == 147740); + assert(_Signed128::_Div_ceil(1483929008, 31399) == 47261); + assert(_Signed128::_Div_ceil(40124559, 11161) == 3596); + assert(_Signed128::_Div_ceil(616936466, 18476) == 33392); + + assert(_Signed128::_Div_ceil(3823598863U, 26233) == 145756); + assert(_Signed128::_Div_ceil(1748736303, 60486) == 28912); + assert(_Signed128::_Div_ceil(3727652564U, 11487) == 324511); + assert(_Signed128::_Div_ceil(443086302, 51740) == 8564); + assert(_Signed128::_Div_ceil(2309966593U, 45205) == 51100); + + assert(_Signed128::_Div_ceil(1688186001, 2331624069U) == 1); + assert(_Signed128::_Div_ceil(4072558058U, 202653587) == 21); + assert(_Signed128::_Div_ceil(551570633, 2681152300U) == 1); + assert(_Signed128::_Div_ceil(3166723164U, 852231237) == 4); + assert(_Signed128::_Div_ceil(3238568162U, 1640506845) == 2); + + assert(_Signed128::_Div_ceil(6242708709713706173ULL, 986240334) == 6329804709ULL); + assert(_Signed128::_Div_ceil(5971429753141872861ULL, 773448951) == 7720522144ULL); + assert(_Signed128::_Div_ceil(3773313860741815071ULL, 2965323624U) == 1272479615); + assert(_Signed128::_Div_ceil(5545364762704150466ULL, 264975532) == 20927837076ULL); + assert(_Signed128::_Div_ceil(3730152929068983090ULL, 2492151938U) == 1496759838); + + assert(_Signed128::_Div_ceil(10191699552117669113ULL, 3524412433243714339ULL) == 3); + assert(_Signed128::_Div_ceil(3073175754216384423ULL, 8155635103385767810ULL) == 1); + assert(_Signed128::_Div_ceil(14454347299931791225ULL, 1125386058911220070ULL) == 13); + assert(_Signed128::_Div_ceil(13659816168029181560ULL, 5789718827007400904ULL) == 3); + assert(_Signed128::_Div_ceil(1069287642992917145ULL, 738486755903428333ULL) == 2); + + assert((_Signed128::_Div_ceil(_Signed128{1329496395917005583ULL, 4825206483521586190ULL}, 2328480880092472314ULL) + == _Signed128{1332872165572535026ULL, 2})); + assert(_Signed128::_Div_ceil(_Signed128{9730685006318077192ULL, 7232491004677529961ULL}, 7551609526229071216ULL) + == 17667215196349628672ULL); + assert((_Signed128::_Div_ceil(_Signed128{12172154442470927259ULL, 8777428261169746305ULL}, 8637741546317560676ULL) + == _Signed128{298314676997257110ULL, 1})); + assert((_Signed128::_Div_ceil(_Signed128{15481671640377153953ULL, 8903114101091695213ULL}, 6090891903637929972ULL) + == _Signed128{8517035596026637315ULL, 1})); + assert(_Signed128::_Div_ceil(_Signed128{15743145484066333195ULL, 805665491188157799ULL}, 6585636204170695090ULL) + == 2256715169835129064ULL); + + assert(_Signed128::_Div_ceil(_Signed128{10731879507814251049ULL, 3873445590U}, + _Signed128{12450032536891019435ULL, 9105649371241646359ULL}) + == 1); + assert(_Signed128::_Div_ceil(_Signed128{6629798443635676577ULL, 2327383978U}, + _Signed128{9839716022772647304ULL, 7922581345850742364ULL}) + == 1); + assert(_Signed128::_Div_ceil(_Signed128{11339831796615584288ULL, 4257443653U}, + _Signed128{4227111794755706639ULL, 670374651106134374ULL}) + == 1); + assert(_Signed128::_Div_ceil(_Signed128{8670574441769227246ULL, 2507472313U}, + _Signed128{6980882583348139841ULL, 4304389704649692956ULL}) + == 1); + assert(_Signed128::_Div_ceil(0, _Signed128{6980882583348139841ULL, 4304389704649692956ULL}) == 0); + + assert(_Signed128::_Div_ceil(_Signed128{6864055997479949219ULL, 8622452334377655874ULL}, + _Signed128{2561354846261717143ULL, 1392383021258532795ULL}) + == 7); + assert(_Signed128::_Div_ceil(_Signed128{939441510092257833ULL, 520549398019160129ULL}, + _Signed128{2009633185566611151ULL, 8480432536590844791ULL}) + == 1); + assert(_Signed128::_Div_ceil(_Signed128{10105653851413328809ULL, 6012530092550053736ULL}, + _Signed128{5123113788439280060ULL, 6703818145194006067ULL}) + == 1); + assert(_Signed128::_Div_ceil(_Signed128{10851415781288494064ULL, 6183428757366606812ULL}, + _Signed128{2541106769657089592ULL, 2041369374582624895ULL}) + == 4); + assert(_Signed128::_Div_ceil(_Signed128{3797056150347910189ULL, 6463249530114157684ULL}, + _Signed128{12268041577665946814ULL, 7107698248126780543ULL}) + == 1); + + constexpr _Signed128 mx = std::numeric_limits<_Signed128>::max(); + assert(_Signed128::_Div_ceil(mx, mx) == 1); + assert(_Signed128::_Div_ceil(mx - 1, mx) == 1); + assert(_Signed128::_Div_ceil(mx, mx - 1) == 2); + assert(_Signed128::_Div_ceil(mx, 1) == mx); + assert(_Signed128::_Div_ceil(mx, 2) == mx / 2 + 1); +} + +int main() { + static_assert((test(), true)); + test(); +} From b3a97d601524a02e3315c9da82febf987fd0487a Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Mon, 30 Jun 2025 16:49:40 +0200 Subject: [PATCH 06/12] `_Div_ceil`: Use `_Int::_Div_ceil` when `_Int` is `_Signed128` --- stl/inc/ranges | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index fd583945cd9..0461ef0d4e9 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -5476,11 +5476,15 @@ namespace ranges { _STL_INTERNAL_CHECK(_Num >= 0); _STL_INTERNAL_CHECK(_Denom >= 0); - _Int _Result = _Num / _Denom; - if (_Num % _Denom != 0) { - ++_Result; + if constexpr (same_as<_Int, _Signed128>) { + return _Int::_Div_ceil(_Num, _Denom); + } else { + _Int _Result = _Num / _Denom; + if (_Num % _Denom != 0) { + ++_Result; + } + return _Result; } - return _Result; } _EXPORT_STD template From 5f14e683b94f9846f63a93b248bd00079d337293 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Wed, 16 Jul 2025 16:02:32 +0200 Subject: [PATCH 07/12] `_Div_ceil`: `_Denom >= 0` => `_Denom > 0` --- stl/inc/__msvc_int128.hpp | 2 +- stl/inc/ranges | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/__msvc_int128.hpp b/stl/inc/__msvc_int128.hpp index 4634a764451..c8e87494792 100644 --- a/stl/inc/__msvc_int128.hpp +++ b/stl/inc/__msvc_int128.hpp @@ -1496,7 +1496,7 @@ struct _Signed128 : _Base128 { #if _HAS_CXX23 _NODISCARD static constexpr _Signed128 _Div_ceil(_Signed128 _Num, _Signed128 _Den) noexcept { // _STL_INTERNAL_CHECK(_Num >= 0); - // _STL_INTERNAL_CHECK(_Den >= 0); + // _STL_INTERNAL_CHECK(_Den > 0); return _Signed128{_Base128::_Div_ceil(_Num, _Den)}; } #endif // _HAS_CXX23 diff --git a/stl/inc/ranges b/stl/inc/ranges index 0461ef0d4e9..1374e0bce35 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -5474,7 +5474,7 @@ namespace ranges { _NODISCARD constexpr _Int _Div_ceil(const _Int _Num, const _Int _Denom) noexcept { _STL_INTERNAL_STATIC_ASSERT(_Signed_integer_like<_Int>); _STL_INTERNAL_CHECK(_Num >= 0); - _STL_INTERNAL_CHECK(_Denom >= 0); + _STL_INTERNAL_CHECK(_Denom > 0); if constexpr (same_as<_Int, _Signed128>) { return _Int::_Div_ceil(_Num, _Denom); From f5c106beab8649c44849f1fa9c413e5bfeb514d1 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Thu, 17 Jul 2025 13:59:10 +0200 Subject: [PATCH 08/12] `__msvc_int128.hpp`: Cleanup logic in `if (_Den._Word[1] >= _Num._Word[1])` statement --- stl/inc/__msvc_int128.hpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/stl/inc/__msvc_int128.hpp b/stl/inc/__msvc_int128.hpp index c8e87494792..758c1fe6aa4 100644 --- a/stl/inc/__msvc_int128.hpp +++ b/stl/inc/__msvc_int128.hpp @@ -715,18 +715,20 @@ struct // establish _Den < _Num and _Num._Word[1] > 0 if (_Den._Word[1] >= _Num._Word[1]) { if (_Den._Word[1] > _Num._Word[1]) { - return _Num != 0 ? 1 : 0; + return static_cast<_Base128>(_Num != 0); } - _Base128 _Rem; - if (_Den._Word[0] <= _Num._Word[0]) { - _Rem = _Num._Word[1] == 0 ? _Num._Word[0] % _Den._Word[0] : _Num._Word[0] - _Den._Word[0]; - } else { - _Rem = _Num; + if (_Num._Word[1] == 0) { + _Base128 _Result = _Num._Word[0] / _Den._Word[0]; + if (_Num._Word[0] % _Den._Word[0] != 0) { + ++_Result; + } + + return _Result; } - _Base128 _Result = _Num._Word[1] == 0 ? _Num._Word[0] / _Den._Word[0] : _Num._Word[0] >= _Den._Word[0]; - if (_Rem != 0) { + _Base128 _Result = _Num._Word[0] >= _Den._Word[0]; + if (_Num._Word[0] - _Den._Word[0] != 0) { ++_Result; } From 7fd6f829ee8ba852305759d92b6311605dd39eb6 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Thu, 17 Jul 2025 15:47:43 +0200 Subject: [PATCH 09/12] `__msvc_int128.hpp`: Remove unnecessary shift --- stl/inc/__msvc_int128.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/stl/inc/__msvc_int128.hpp b/stl/inc/__msvc_int128.hpp index 758c1fe6aa4..dfe3e363fdd 100644 --- a/stl/inc/__msvc_int128.hpp +++ b/stl/inc/__msvc_int128.hpp @@ -837,7 +837,6 @@ struct _Num._Word[1] = (static_cast(__u[3]) << 32) | __u[2]; #endif // _STL_128_INTRINSICS - _Num >>= __d; if (_Num != 0) { ++_Result; } From 06e45ed18ceae65e901ef82d15f40363d99f37b0 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 5 Sep 2025 15:06:35 -0700 Subject: [PATCH 10/12] Oh, Diablo Canyon `_Divide`, why can't you be more like Diablo Canyon `_Div_ceil`? Use `_ZERO_OR_NO_INIT` for downlevel support. --- stl/inc/__msvc_int128.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/__msvc_int128.hpp b/stl/inc/__msvc_int128.hpp index f2dcca41af8..4804d9284cc 100644 --- a/stl/inc/__msvc_int128.hpp +++ b/stl/inc/__msvc_int128.hpp @@ -435,8 +435,8 @@ struct alignas(16) _Base128 { _NODISCARD static constexpr _Base128 _Divide(const _Base128& _Num, const uint64_t _Den) noexcept { _Base128 _Result; _Result._Word[1] = _Num._Word[1] / _Den; - uint64_t _Rem = _Num._Word[1] % _Den; - _Result._Word[0] = _UDiv128(_Rem, _Num._Word[0], _Den, _Rem); + uint64_t _Rem _ZERO_OR_NO_INIT; + _Result._Word[0] = _UDiv128(_Num._Word[1] % _Den, _Num._Word[0], _Den, _Rem); return _Result; } _NODISCARD static constexpr _Base128 _Divide(_Base128 _Num, _Base128 _Den) noexcept { From 38b43b79ff0d86398cbe03c6fd2bb555ec3d86c5 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 5 Sep 2025 16:02:31 -0700 Subject: [PATCH 11/12] Simplify/optimize the case of equal non-zero high words. --- stl/inc/__msvc_int128.hpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/stl/inc/__msvc_int128.hpp b/stl/inc/__msvc_int128.hpp index 4804d9284cc..cadaf383014 100644 --- a/stl/inc/__msvc_int128.hpp +++ b/stl/inc/__msvc_int128.hpp @@ -723,12 +723,11 @@ struct alignas(16) _Base128 { return _Result; } - _Base128 _Result = _Num._Word[0] >= _Den._Word[0]; - if (_Num._Word[0] - _Den._Word[0] != 0) { - ++_Result; + if (_Num._Word[0] > _Den._Word[0]) { + return 2u; } - return _Result; + return 1u; } // establish _Den has more than 1 non-zero "digit" From a28ab262bf45437abe4248374b40ecfe722f3ad4 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 5 Sep 2025 16:24:34 -0700 Subject: [PATCH 12/12] Optimize the zero high word case, explain in a comment. --- stl/inc/__msvc_int128.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/__msvc_int128.hpp b/stl/inc/__msvc_int128.hpp index cadaf383014..34f9bc99003 100644 --- a/stl/inc/__msvc_int128.hpp +++ b/stl/inc/__msvc_int128.hpp @@ -715,7 +715,7 @@ struct alignas(16) _Base128 { } if (_Num._Word[1] == 0) { - _Base128 _Result = _Num._Word[0] / _Den._Word[0]; + uint64_t _Result = _Num._Word[0] / _Den._Word[0]; // with 64-bit inputs, the ceiling is also 64-bit if (_Num._Word[0] % _Den._Word[0] != 0) { ++_Result; }