Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions benchmarks/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,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)
Expand Down
39 changes: 39 additions & 0 deletions benchmarks/src/ranges_div_ceil.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <benchmark/benchmark.h>
#include <cstdint>
#include <limits>
#include <ranges>

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<uint64_t>::max());
static_assert(d1.den <= numeric_limits<uint32_t>::max());

constexpr Data d2{_Signed128{0x0000'1111'2222'3333LL, 0x4444'5555'6666'7777LL}, 0x7777'8888'9999'AAAA};
static_assert(d2.num > numeric_limits<uint64_t>::max());
static_assert(d2.den <= numeric_limits<uint64_t>::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<uint64_t>::max());
static_assert(d3.den > numeric_limits<uint64_t>::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();
174 changes: 172 additions & 2 deletions stl/inc/__msvc_int128.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -678,6 +678,168 @@ struct alignas(16) _Base128 {
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<uint32_t>(_Num._Word[0]);
_Result._Word[0] |= static_cast<uint32_t>(_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);
Comment thread
StephanTLavavej marked this conversation as resolved.
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 static_cast<_Base128>(_Num != 0);
}

if (_Num._Word[1] == 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;
}

return _Result;
}

if (_Num._Word[0] > _Den._Word[0]) {
return 2u;
}

return 1u;
}

// 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<uint32_t>(_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<uint32_t>(_Num._Word[0] << __d),
static_cast<uint32_t>(_Num._Word[0] >> (32 - __d)),
static_cast<uint32_t>(_Num._Word[1] << __d),
static_cast<uint32_t>(_Num._Word[1] >> (32 - __d)),
0,
};
uint32_t __v[4] = {
static_cast<uint32_t>(_Den._Word[0] << __d),
static_cast<uint32_t>(_Den._Word[0] >> (32 - __d)),
static_cast<uint32_t>(_Den._Word[1] << __d),
static_cast<uint32_t>(_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<uint64_t>(__q[1]) << 32) | __q[0];
_Num._Word[0] = (static_cast<uint64_t>(__u[1]) << 32) | __u[0];
_Num._Word[1] = (static_cast<uint64_t>(__u[3]) << 32) | __u[2];
#endif // _STL_128_INTRINSICS

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];
Expand Down Expand Up @@ -1327,6 +1489,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]};
}
Expand Down
20 changes: 14 additions & 6 deletions stl/inc/ranges
Original file line number Diff line number Diff line change
Expand Up @@ -5470,13 +5470,21 @@ namespace ranges {
_EXPORT_STD inline constexpr _Enumerate_fn enumerate;
} // namespace views

template <class _Size>
_NODISCARD constexpr _Size _Div_ceil(const _Size _Num, const _Size _Denom) noexcept {
_Size _Result = _Num / _Denom;
if (_Num % _Denom != 0) {
++_Result;
template <class _Int>
_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);

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 <view _Vw>
Expand Down
1 change: 1 addition & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,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
Expand Down
4 changes: 4 additions & 0 deletions tests/std/tests/P2442R1_signed128_div_ceil/env.lst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

RUNALL_INCLUDE ..\usual_latest_matrix.lst
109 changes: 109 additions & 0 deletions tests/std/tests/P2442R1_signed128_div_ceil/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <__msvc_int128.hpp>
#include <cassert>
#include <limits>

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();
}