Thanks to visit codestin.com
Credit goes to github.com

Skip to content

isGregorianLeap in RubyDate.java - two suggestions for minor changes #5965

@colinb2r

Description

@colinb2r

This isn't exactly a bug report because as written it works correctly.
But I think it is sub-optimal, and with minor changes it can be:
(i) a bit faster for 75% of years;
(ii) maybe somewhat faster for 25% of years.
I'm more than happy to answer questions.
Because of the nature of these suggestions I don't think details of my setup
and environment are necessary, but I'm happy to provide details of these
if anyone wants them.

These also apply to C Ruby time.c leap_year_p and date_core.c c_gregorian_leap_p
so I'm including the code for those so easy comparisons can be made.
I will also post this on a C Ruby forum.

Currently:

* C Ruby: time_c:  leap_year_p(long y):
    return ((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0);

* C Ruby: date_core.c:  c_gregorian_leap_p(int y):
    return (MOD(y, 4) == 0 && y % 100 != 0) || MOD(y, 400) == 0;

* JRuby: RubyDate.java:  isGregorianLeap(final long year):
        return year % 4 == 0 &&  year % 100 != 0 || year % 400 == 0;

Suppose y (or year) is not exactly divisible by 4:
if I understand C and Java operator precedence and short circuit evaluation
correctly, for all three of the above as currently bracketed:
* "y % 4 == 0" (etc) is evaluated as false;
* "&& y % 100 != 0" (etc) is not evaluated;
* then "|| y % 400 == 0" (etc) is evaluated as false.

But if we rebracket the return statements as, for example:
    return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0);

* "y % 4 == 0" is evaluated as false;
* " && (y % 100 != 0 || y % 400 == 0)" is not evaluated;

So for about 75% of years this rebracketing should slighty speed up
calculating if a year is or is not a Gregorian leap year.

Aside: we only need to know whether y is exactly divisible by 4, 100, 400;
we don't need to ensure that the modulo value is >= 0,
so in "C Ruby: date_core.c: c_gregorian_leap_p" we can use "%" instead of "MOD".
This also applies to "C Ruby: date_core.c: c_gregorian_leap_p(int y)":
    return MOD(y, 4) == 0;
For example, "JRuby: RubyDate.java:  isJulianLeap(final long year)" uses:
        return year % 4 == 0;

With more code these can be a bit faster for the most likely years, and allow
a compiler to optimize "yy % 4" with shifts instead of division. For example:

* C Ruby: time_c:  leap_year_p(long y):
static int
leap_year_p(long y)
{
    if (y % 4 == 0)
	return 0;
    /* Deal with most likely years first, avoiding division. */
    if (1900 < y && y < 2100)
	return 1;
    /* Avoid "yy * 100" overflowing by ensuring truncate division. */
    long yy = y >= 0 ? y / 100; y > -100 ? 0 : -((-(y + 100)) / 100)  - 1;
    return y != yy * 100 || yy % 4 == 0;
}

* C Ruby: date_core.c:  c_gregorian_leap_p(int y):
As just above, except instead of "long" use "int".

* JRuby: RubyDate.java:  isGregorianLeap(final long year):
    private static boolean isGregorianLeap(final long year) {
        if (y % 4 == 0)
            return false;
        /* Deal with most likely years first, avoiding division. */
        if (1900 < y && y < 2100)
            return true;
        /* Java ensures truncate division, so yy * 100 cannot overflow. */
        long yy = y / 100;
        return y != yy * 100 || yy % 4 == 0;
    }

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions