libstdc++: More efficient std::chrono::year::leap
authorCassio Neri <cassio.neri@gmail.com>
Wed, 23 Jun 2021 14:32:16 +0000 (15:32 +0100)
committerJonathan Wakely <jwakely@redhat.com>
Wed, 23 Jun 2021 17:28:08 +0000 (18:28 +0100)
Simple change to std::chrono::year::is_leap. If a year is multiple of 100,
then it's divisible by 400 if and only if it's divisible by 16. The latter
allows for better code generation.

The expression is then either y%16 or y%4 which are both powers of two
and so it can be rearranged to use simple bitmask operations.

Co-authored-by: Jonathan Wakely <jwakely@redhat.com>
Co-authored-by: Ulrich Drepper <drepper@redhat.com>
libstdc++-v3/ChangeLog:

* include/std/chrono (chrono::year::is_leap()): Optimize.

libstdc++-v3/include/std/chrono

index 4631a727d73f2500f61c30f929497954c8651b1d..863b6a27bdf0c1f35b7382027b3539a2c26eb558 100644 (file)
@@ -1606,13 +1606,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        // [1] https://github.com/cassioneri/calendar
        // [2] https://accu.org/journals/overload/28/155/overload155.pdf#page=16
 
+       // Furthermore, if y%100 != 0, then y%400==0 is equivalent to y%16==0,
+       // so we can rearrange the expression to (mult_100 ? y % 4 : y % 16)==0
+       // which is equivalent to (y & (mult_100 ? 15 : 3)) == 0.
+       // See https://gcc.gnu.org/pipermail/libstdc++/2021-June/052815.html
+
        constexpr uint32_t __multiplier   = 42949673;
        constexpr uint32_t __bound        = 42949669;
        constexpr uint32_t __max_dividend = 1073741799;
        constexpr uint32_t __offset       = __max_dividend / 2 / 100 * 100;
        const bool __is_multiple_of_100
          = __multiplier * (_M_y + __offset) < __bound;
-       return (!__is_multiple_of_100 || _M_y % 400 == 0) && _M_y % 4 == 0;
+       return (_M_y & (__is_multiple_of_100 ? 15 : 3)) == 0;
       }
 
       explicit constexpr