I've been looking at the C code for the Date class (and at some of the
C code for the Time class) and it seems to be possible to make some of
this quite a bit faster (from 5 to 70 times faster, depending on the
function, computer, compiler options, etc), partly by replacing some
floating point calculations by integer calculations, and partly by
using some different algorithms.

While doing that I found these leap year functions in
ruby-2.4.0/ext/date/date_core.c

#define NMOD(x,y) ((y)-(-((x)+1)%(y))-1)
#define MOD(n,d) ((n)<0 ? NMOD((n),(d)) : (n)%(d))

inline static int
c_gregorian_leap_p(int y)
{
    return (MOD(y, 4) == 0 && y % 100 != 0) || MOD(y, 400) == 0;
}

and in ruby-2.4.0/time.c

static int
leap_year_p(long y)
{
    return ((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0);
}

Unless I'm missing something (which is not impossible):

1. Instead of using "MOD", the date_core.c code could just use the
time.c code, of course with int y instead of long y. I don't see that
there is any need to use "MOD" instead of "%": all we are interested
in here is whether y is or is not exactly divisible by 4, 100 or 400,
and it doesn't matter whether, for example,
    "y % 4" is implemented as "y - 4 * trunc(y / 4)" or "y - 4 * floor(y / 4)"

2. In the time.c code, suppose y is not exactly divisible by 4:

the first equality in ((y % 4 == 0) && (y % 100 != 0)) evaluates as false,
and, as I understand it (and unless the compiler notices this)
(y % 400 == 0) is then also evaluated as false.

Compare with this:
    return (y % 4 == 0) && ((y % 100 != 0) || (y % 400 == 0));
Here (y % 4 == 0) evaluates as false, and that's the end of it.

Using gcc to compile and test functions using both formats, the
re-factored return expression seems a little faster, suggesting that
the time.c code is unecessarily evaluating "(y % 400 == 0)" for the
three-quarters of years which are not exactly divisible by 4.

So as I see it, the time.c (and date_core.c) code isn't wrong, because
it gives the correct result but it is a little slower than it could
be, and this could be remedied by simply moving two brackets.

Unless I'm missing something, hence this post to Ruby-Talk before I
try to report this.

Unsubscribe: <mailto:ruby-talk-request / ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>