<div dir="ltr">Thanks, I had only checked scalar code generation initially.  In an isolated scalar test on x86_64, the new form is 7 instructions versus  10 for the existing form, but I can reproduce that benchmark results depend on optimization context.  With GCC 17 -O3 generic, the loop favors the existing form, while with -fno-tree-vectorize or -march=native the new form is faster on my machine.<br><div><br></div><div>Given that, I have to do more benchmarking before claiming this is a performance improvement, focus was mostly on number of instructions </div><br></div><br><div class="gmail_quote gmail_quote_container"><div dir="ltr" class="gmail_attr">Em qui., 30 de jul. de 2026 às 12:11, Jonathan Wakely <<a href="mailto:jwakely@redhat.com">jwakely@redhat.com</a>> escreveu:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">On Thu, 30 Jul 2026 at 01:58, Francisco Muniz wrote:<br>
><br>
> Use Falk Hueffner's leap-year test for year::is_leap after shifting<br>
> the valid std::chrono::year range by a multiple of 400. The shift<br>
> preserves divisibility by 4, 100, and 400, and the unsigned conversion<br>
> gives the intended modulo 2^32 arithmetic.<br>
><br>
> Idea by Cassio Neri: add 32800, which is 82 * 400, to shift the signed<br>
> year range into the supported non-negative range.<br>
<br>
This is interesting, but your new code is slower than Cassio's code<br>
when I benchmark it. Have you done your own benchmarking?<br>
<br>
><br>
> Tested on x86_64-pc-linux-gnu:<br>
>   make -j$(nproc) all-gcc<br>
>   make -j$(nproc) all-target-libstdc++-v3<br>
>   make check RUNTESTFLAGS='conformance.exp=std/time/year/1.cc'<br>
>   make check RUNTESTFLAGS='conformance.exp=std/time/year/2.cc'<br>
><br>
> libstdc++-v3/ChangeLog:<br>
><br>
>         * include/std/chrono (year::is_leap): Use Hueffner leap-year<br>
>         test after biasing the year by a multiple of 400.<br>
><br>
> Signed-off-by: Francisco Muniz <<a href="mailto:munizfco@gmail.com" target="_blank">munizfco@gmail.com</a>><br>
> ---<br>
>  libstdc++-v3/include/std/chrono | 31 ++++++++-----------------------<br>
>  1 file changed, 8 insertions(+), 23 deletions(-)<br>
><br>
> diff --git a/libstdc++-v3/include/std/chrono b/libstdc++-v3/include/std/chrono<br>
> index 692fd6025e7..4483914c08b 100644<br>
> --- a/libstdc++-v3/include/std/chrono<br>
> +++ b/libstdc++-v3/include/std/chrono<br>
> @@ -904,29 +904,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION<br>
>        constexpr bool<br>
>        is_leap() const noexcept<br>
>        {<br>
> -       // Testing divisibility by 100 first gives better performance [1], i.e.,<br>
> -       //     return _M_y % 100 == 0 ? _M_y % 400 == 0 : _M_y % 16 == 0;<br>
> -       // Furthermore, if _M_y % 100 == 0, then _M_y % 400 == 0 is equivalent<br>
> -       // to _M_y % 16 == 0, so we can simplify it to<br>
> -       //     return _M_y % 100 == 0 ? _M_y % 16 == 0 : _M_y % 4 == 0.  // #1<br>
> -       // Similarly, we can replace 100 with 25 (which is good since<br>
> -       // _M_y % 25 == 0 requires one fewer instruction than _M_y % 100 == 0<br>
> -       // [2]):<br>
> -       //     return _M_y % 25 == 0 ? _M_y % 16 == 0 : _M_y % 4 == 0.  // #2<br>
> -       // Indeed, first assume _M_y % 4 != 0.  Then _M_y % 16 != 0 and hence,<br>
> -       // _M_y % 4 == 0 and _M_y % 16 == 0 are both false.  Therefore, #2<br>
> -       // returns false as it should (regardless of _M_y % 25.) Now assume<br>
> -       // _M_y % 4 == 0.  In this case, _M_y % 25 == 0 if, and only if,<br>
> -       // _M_y % 100 == 0, that is, #1 and #2 are equivalent.  Finally, #2 is<br>
> -       // equivalent to<br>
> -       //     return (_M_y & (_M_y % 25 == 0 ? 15 : 3)) == 0.<br>
> -<br>
> -       // References:<br>
> -       // [1] <a href="https://github.com/cassioneri/calendar" rel="noreferrer" target="_blank">https://github.com/cassioneri/calendar</a><br>
> -       // [2] <a href="https://godbolt.org/z/55G8rn77e" rel="noreferrer" target="_blank">https://godbolt.org/z/55G8rn77e</a><br>
> -       // [3] <a href="https://gcc.gnu.org/pipermail/libstdc++/2021-June/052815.html" rel="noreferrer" target="_blank">https://gcc.gnu.org/pipermail/libstdc++/2021-June/052815.html</a><br>
> -<br>
> -       return (_M_y & (_M_y % 25 == 0 ? 15 : 3)) == 0;<br>
> +       // Shift into the range supported by Falk Hueffner's leap-year test:<br>
> +       // <a href="http://hueffner.de/falk/blog/a-leap-year-check-in-three-instructions.html" rel="noreferrer" target="_blank">hueffner.de/falk/blog/a-leap-year-check-in-three-instructions.html</a><br>
> +       // Adding a multiple of 400 preserves divisibility by 4, 100, and 400.<br>
> +       // Idea by Cassio Neri: add 32800 (82 * 400).<br>
> +       // The conversion to uint32_t gives the algorithm's intended modulo 2^32<br>
> +       // arithmetic.<br>
> +       const auto __y = static_cast<uint32_t>(_M_y) + 32800u;<br>
> +       return ((__y * 1073750999u) & 3221352463u) <= 126976u;<br>
>        }<br>
><br>
>        explicit constexpr<br>
> --<br>
> 2.47.3<br>
><br>
<br>
</blockquote></div>