On Thu, 30 Jul 2026 at 01:58, Francisco Muniz wrote: > > Use Falk Hueffner's leap-year test for year::is_leap after shifting > the valid std::chrono::year range by a multiple of 400. The shift > preserves divisibility by 4, 100, and 400, and the unsigned conversion > gives the intended modulo 2^32 arithmetic. > > Idea by Cassio Neri: add 32800, which is 82 * 400, to shift the signed > year range into the supported non-negative range.
This is interesting, but your new code is slower than Cassio's code when I benchmark it. Have you done your own benchmarking? > > Tested on x86_64-pc-linux-gnu: > make -j$(nproc) all-gcc > make -j$(nproc) all-target-libstdc++-v3 > make check RUNTESTFLAGS='conformance.exp=std/time/year/1.cc' > make check RUNTESTFLAGS='conformance.exp=std/time/year/2.cc' > > libstdc++-v3/ChangeLog: > > * include/std/chrono (year::is_leap): Use Hueffner leap-year > test after biasing the year by a multiple of 400. > > Signed-off-by: Francisco Muniz <[email protected]> > --- > libstdc++-v3/include/std/chrono | 31 ++++++++----------------------- > 1 file changed, 8 insertions(+), 23 deletions(-) > > diff --git a/libstdc++-v3/include/std/chrono b/libstdc++-v3/include/std/chrono > index 692fd6025e7..4483914c08b 100644 > --- a/libstdc++-v3/include/std/chrono > +++ b/libstdc++-v3/include/std/chrono > @@ -904,29 +904,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > constexpr bool > is_leap() const noexcept > { > - // Testing divisibility by 100 first gives better performance [1], > i.e., > - // return _M_y % 100 == 0 ? _M_y % 400 == 0 : _M_y % 16 == 0; > - // Furthermore, if _M_y % 100 == 0, then _M_y % 400 == 0 is equivalent > - // to _M_y % 16 == 0, so we can simplify it to > - // return _M_y % 100 == 0 ? _M_y % 16 == 0 : _M_y % 4 == 0. // #1 > - // Similarly, we can replace 100 with 25 (which is good since > - // _M_y % 25 == 0 requires one fewer instruction than _M_y % 100 == 0 > - // [2]): > - // return _M_y % 25 == 0 ? _M_y % 16 == 0 : _M_y % 4 == 0. // #2 > - // Indeed, first assume _M_y % 4 != 0. Then _M_y % 16 != 0 and hence, > - // _M_y % 4 == 0 and _M_y % 16 == 0 are both false. Therefore, #2 > - // returns false as it should (regardless of _M_y % 25.) Now assume > - // _M_y % 4 == 0. In this case, _M_y % 25 == 0 if, and only if, > - // _M_y % 100 == 0, that is, #1 and #2 are equivalent. Finally, #2 is > - // equivalent to > - // return (_M_y & (_M_y % 25 == 0 ? 15 : 3)) == 0. > - > - // References: > - // [1] https://github.com/cassioneri/calendar > - // [2] https://godbolt.org/z/55G8rn77e > - // [3] https://gcc.gnu.org/pipermail/libstdc++/2021-June/052815.html > - > - return (_M_y & (_M_y % 25 == 0 ? 15 : 3)) == 0; > + // Shift into the range supported by Falk Hueffner's leap-year test: > + // hueffner.de/falk/blog/a-leap-year-check-in-three-instructions.html > + // Adding a multiple of 400 preserves divisibility by 4, 100, and 400. > + // Idea by Cassio Neri: add 32800 (82 * 400). > + // The conversion to uint32_t gives the algorithm's intended modulo > 2^32 > + // arithmetic. > + const auto __y = static_cast<uint32_t>(_M_y) + 32800u; > + return ((__y * 1073750999u) & 3221352463u) <= 126976u; > } > > explicit constexpr > -- > 2.47.3 >
