Personally, I would unit test all preprocessor procedures.

On Tue, Apr 4, 2017 at 10:14 AM, Rob Biedenharn
<rob.biedenharn / gmail.com> wrote:
>
>> On 2017-Apr-3, at 16:18 , Colin Bartlett <colinb2r / googlemail.com> wrote:
>>
>> 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;
>> }
>
> I think that this is accounting for the fact that the Gregorian calendar has no year "0", after  1 B.C. comes A.D. 1
> See https://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar for too much detail. ;-/
>
> You seem to be correct in that the grouping of terms could be changed to:
>
>    return MOD(y, 4) == 0 && (y % 100 != 0 || MOD(y, 400) == 0);
>
> to avoid the needless check against 400 most of the time.
>
> -Rob
>
>>
>> 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>
>
>
> Unsubscribe: <mailto:ruby-talk-request / ruby-lang.org?subject=unsubscribe>
> <http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>

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