[gcc r17-3387] libstdc++: Optimize chrono year::is_leap

Jonathan Wakely via Gcc-cvs <[email protected]>
Newsgroups gmane.comp.gcc.cvs
Message-ID <[email protected]>
https://gcc.gnu.org/g:28b1b55c60464da23c65c6a8a9515c29f8d67ad8

commit r17-3387-g28b1b55c60464da23c65c6a8a9515c29f8d67ad8
Author: Francisco Muniz <[email protected]>
Date:   Wed Jul 29 21:57:10 2026 -0300

    libstdc++: Optimize chrono year::is_leap
    
    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.
    
    Although the previous algorithm (by Cassio) has better throughput due to
    better vectorization, we use this one to optimize for latency instead.
    
    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]>

Diff:
---
 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 692fd6025e7d..4116f1c815eb 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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.