469,950 Members | 2,343 Online

# A better algorithm to calculate a leap year?

I'm new here, so excuse me if my style is incorrect. Can anyone come
up with a better method for this calculation?

Code:
int is_leap(int year)
{
switch (year % 19) {
case 0: case 3: case 6: case 8:
case 11: case 14: case 17: return 1;
default: return 0;
}
}

This is part of a calendar program.

Nov 5 '07 #1
37 14127
<ma******@gmail.comwrote:
>I'm new here, so excuse me if my style is incorrect. Can anyone come
up with a better method for this calculation?
>Code:
int is_leap(int year)
{
switch (year % 19) {
case 0: case 3: case 6: case 8:
case 11: case 14: case 17: return 1;
default: return 0;
}
}
1900 % 19 == 0 but 1900 was not a leap year (special case)
1903 % 19 == 3 but 1903 was not a leap year.
1906 % 19 == 6 but 1906 was not a leap year.
1911 % 19 == 11 but 1911 was not a leap year.
1914 % 19 == 14 but 1914 was not a leap year.
1917 % 19 == 17 but 1917 was not a leap year.
2000 % 19 == 5 but 2000 *was* a leap year.
2004 % 19 == 9 but 2004 *was* a leap year.

It isn't a simple case of having to subtract an base year
to get to the start of the cycle:
you have a leap year at year 0 of the cycle, and another at
year +3 (not year +4), another at year +6, then
one just 2 years later at year +8. Clearly this is wrong.
--
"Any sufficiently advanced bug is indistinguishable from a feature."
-- Rich Kulawiec
Nov 5 '07 #2
ma******@gmail.com said:
I'm new here, so excuse me if my style is incorrect. Can anyone come
up with a better method for this calculation?

Code:
int is_leap(int year)
{
switch (year % 19) {
case 0: case 3: case 6: case 8:
case 11: case 14: case 17: return 1;
default: return 0;
}
}

This is part of a calendar program.
Here's a better method:

int really_is_leap(int year)
{
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
}

This one has the merit of actually giving the right results.

Test your function, and see how it works on years that you know to be leap
years (eg 1976, 2000, 2004, 2008) and years you know not to be leap years
(2001, 2002, 2003, 2005).

Then switch to a working algorithm.

--
Richard Heathfield <http://www.cpax.org.uk>
Email: -http://www. +rjh@
"Usenet is a strange place" - dmr 29 July 1999
Nov 5 '07 #3
On Nov 5, 12:30 pm, mazwo...@gmail.com wrote:
I'm new here, so excuse me if my style is incorrect. Can anyone come
up with a better method for this calculation?

Code:
int is_leap(int year)
{
switch (year % 19) {
case 0: case 3: case 6: case 8:
case 11: case 14: case 17: return 1;
default: return 0;
}

}

This is part of a calendar program.
Try this:

return (year % 400 == 0) || ((year % 4 == 0) && (year % 100 != 0));

I think that's the correct formula (evenly divisible by 400, or evenly
divisible by 4 and not evenly divisible by 100).

Nov 5 '07 #4
On Nov 5, 12:30 pm, mazwo...@gmail.com wrote:
I'm new here, so excuse me if my style is incorrect. Can anyone come
up with a better method for this calculation?

Code:
int is_leap(int year)
{
switch (year % 19) {
case 0: case 3: case 6: case 8:
case 11: case 14: case 17: return 1;
default: return 0;
}

}

This is part of a calendar program.
Try this:

return (year % 400 == 0) || ((year % 4 == 0) && (year % 100 != 0));

I think that's the correct formula (evenly divisible by 400, or evenly
divisible by 4 and not evenly divisible by 100).

Nov 5 '07 #5
On Nov 5, 1:30 pm, mazwo...@gmail.com wrote:
I'm new here, so excuse me if my style is incorrect. Can anyone come
up with a better method for this calculation?
Sorry, should have noted. This is to generate a Jewish calendar (This
is the year 5768, and therefore a leap year with an extra month thrown
in mid-March to mid-April, which is why Easter is 3 weeks later this
year than it was last year). The numbers in the code are correct. I
was wondering if there was any better algorithm. Thanks to all who
responded, and apologies for the misunderstanding.

-- Marty (a newbie, starting off on the wrong foot)
Nov 5 '07 #6
ma******@gmail.com wrote:
) On Nov 5, 1:30 pm, mazwo...@gmail.com wrote:
)I'm new here, so excuse me if my style is incorrect. Can anyone come
)up with a better method for this calculation?
)
) Sorry, should have noted. This is to generate a Jewish calendar (This
) is the year 5768, and therefore a leap year with an extra month thrown
) in mid-March to mid-April, which is why Easter is 3 weeks later this
) year than it was last year). The numbers in the code are correct. I
) was wondering if there was any better algorithm. Thanks to all who
) responded, and apologies for the misunderstanding.

Nice one ;)

In any case, what would you feel is a 'better' algorithm ?
Faster ? Less code ?
What you wrote seems pretty clear and robust to me.
SaSW, Willem
--
Disclaimer: I am in no way responsible for any of the statements
made in the above text. For all I know I might be
drugged or something..
No I'm not paranoid. You all think I'm paranoid, don't you !
#EOT
Nov 5 '07 #7
On Nov 5, 10:54 am, John Bode <john_b...@my-deja.comwrote:
On Nov 5, 12:30 pm, mazwo...@gmail.com wrote:
I'm new here, so excuse me if my style is incorrect. Can anyone come
up with a better method for this calculation?
Code:
int is_leap(int year)
{
switch (year % 19) {
case 0: case 3: case 6: case 8:
case 11: case 14: case 17: return 1;
default: return 0;
}
}
This is part of a calendar program.

Try this:

return (year % 400 == 0) || ((year % 4 == 0) && (year % 100 != 0));

I think that's the correct formula (evenly divisible by 400, or evenly
divisible by 4 and not evenly divisible by 100).

/***
Using Mike Lee's Driver (modified a bit by DRC):
------------------------------------------------
OK, if people want to test this for themselves it looks like the code
should be more robust in terms of "clever" optimisers. Try the
following
which sums and prints the number of leap years found:
***/
#include <stdio.h>
#include <time.h>
#define START_YEAR 1582
#define END_YEAR 4000
#define ITERATIONS 100000

typedef unsigned (*leap_func)(unsigned);

static unsigned is_a_leap_year1(unsigned y)
{
return (y % 400u == 0u) ? 1 : (y % 100u == 0u) ? 0u : (y % 4u ==
0u);
}

static unsigned is_a_leap_year2(unsigned y)
{
return !(y % 4u) && ((y % 100u) || !(y % 400u));
}

static unsigned is_a_leap_year3(unsigned y)
{
return !(y & 3u) && ((y % 100u) || !(y % 400u));
}

// Kirby
static unsigned is_a_leap_year4(unsigned y)
{
return y & 3u ? 0u : y % 25u ? 1u : y / 25u & 12u ? 0u : 1u;
}
// Hu
static unsigned is_a_leap_year5(unsigned y)
{
return (y & 3u) ? 0u : (y % 25u) ? 1u : (y & 15u) ? 0u : 1u;
}
static void test_leap(const char *name, leap_func f)
{
unsigned i,
year;
clock_t start,
end;
unsigned long leap_count = 0;
start = clock();
for (i = 0; i < ITERATIONS; i++) {
for (year = START_YEAR; year <= END_YEAR; year++)
leap_count += f(year);
}
end = clock();
leap_count /= ITERATIONS;
printf("%s leap_count=%lu %.2f seconds\n", name, leap_count,
(double) (end - start) / (double) CLOCKS_PER_SEC);
}
int main(void)
{
printf("START_YEAR=%d END_YEAR=%d ITERATIONS=%d\n", START_YEAR,
END_YEAR,
ITERATIONS);
test_leap("is_a_leap_year1", is_a_leap_year1);
test_leap("is_a_leap_year2", is_a_leap_year2);
test_leap("is_a_leap_year3", is_a_leap_year3);
test_leap("is_a_leap_year4", is_a_leap_year4);
test_leap("is_a_leap_year5", is_a_leap_year5);
return 0;
}
/*
After profile guided optimization, methods 2-5
are all about the same speed, and the "standard"
method is shown to be slower than the others.

Hardware 2.2GHz AMD, compiler MSVC++ 2005 with PGO.

C:\tmp\isleap\Release>isleap
START_YEAR=1582 END_YEAR=4000 ITERATIONS=100000
is_a_leap_year1 leap_count=587 1.92 seconds
is_a_leap_year2 leap_count=587 1.11 seconds
is_a_leap_year3 leap_count=587 1.09 seconds
is_a_leap_year4 leap_count=587 1.11 seconds
is_a_leap_year5 leap_count=587 1.09 seconds

C:\tmp\isleap\Release>isleap
START_YEAR=1582 END_YEAR=4000 ITERATIONS=100000
is_a_leap_year1 leap_count=587 1.92 seconds
is_a_leap_year2 leap_count=587 1.09 seconds
is_a_leap_year3 leap_count=587 1.13 seconds
is_a_leap_year4 leap_count=587 1.09 seconds
is_a_leap_year5 leap_count=587 1.11 seconds

C:\tmp\isleap\Release>isleap
START_YEAR=1582 END_YEAR=4000 ITERATIONS=100000
is_a_leap_year1 leap_count=587 1.92 seconds
is_a_leap_year2 leap_count=587 1.11 seconds
is_a_leap_year3 leap_count=587 1.09 seconds
is_a_leap_year4 leap_count=587 1.11 seconds
is_a_leap_year5 leap_count=587 1.09 seconds

C:\tmp\isleap\Release>isleap
START_YEAR=1582 END_YEAR=4000 ITERATIONS=100000
is_a_leap_year1 leap_count=587 1.92 seconds
is_a_leap_year2 leap_count=587 1.11 seconds
is_a_leap_year3 leap_count=587 1.11 seconds
is_a_leap_year4 leap_count=587 1.09 seconds
is_a_leap_year5 leap_count=587 1.09 seconds

C:\tmp\isleap\Release>isleap
START_YEAR=1582 END_YEAR=4000 ITERATIONS=100000
is_a_leap_year1 leap_count=587 1.92 seconds
is_a_leap_year2 leap_count=587 1.09 seconds
is_a_leap_year3 leap_count=587 1.11 seconds
is_a_leap_year4 leap_count=587 1.11 seconds
is_a_leap_year5 leap_count=587 1.09 seconds
*/

Nov 5 '07 #8
On Nov 5, 11:10 am, mazwo...@gmail.com wrote:
On Nov 5, 1:30 pm, mazwo...@gmail.com wrote:
I'm new here, so excuse me if my style is incorrect. Can
anyone come up with a better method for this calculation?
Sorry, should have noted. This is to generate a Jewish calendar
(This is the year 5768, and therefore a leap year with an extra
month thrown in mid-March to mid-April, which is why Easter is
3 weeks later this year than it was last year). The numbers in
the code are correct. I was wondering if there was any better
algorithm. Thanks to all who responded, and apologies for the
misunderstanding.
int is_leap(int year)
{
return (0x00024949U & (1U << year%19)) != 0;
}

Although, I would recommend renaming the function to something that
won't cause confusion if the application is ever combined with code
that also deals with Gregorian.

-- James
Nov 5 '07 #9
John Bode wrote:
>
On Nov 5, 12:30 pm, mazwo...@gmail.com wrote:
I'm new here, so excuse me if my style is incorrect. Can anyone come
up with a better method for this calculation?

Code:
int is_leap(int year)
{
switch (year % 19) {
case 0: case 3: case 6: case 8:
case 11: case 14: case 17: return 1;
default: return 0;
}

}

This is part of a calendar program.

Try this:

return (year % 400 == 0) || ((year % 4 == 0) && (year % 100 != 0));

I think that's the correct formula (evenly divisible by 400, or evenly
divisible by 4 and not evenly divisible by 100).
K&R2 has an equivalent expression on page 111.

--
pete
Nov 5 '07 #10
ma******@gmail.com wrote On 11/05/07 13:30,:
I'm new here, so excuse me if my style is incorrect. Can anyone come
up with a better method for this calculation?

Code:
int is_leap(int year)
{
switch (year % 19) {
case 0: case 3: case 6: case 8:
case 11: case 14: case 17: return 1;
default: return 0;
}
}
The principal alternative (which I haven't seen
anyone mention yet) would be to use a table:

int is_leap(int year)
{
static const int answer[19] = {
1, 0, 0, 1, 0, 0, 1, 0, 1, 0,
0, 1, 0, 0, 1, 0, 0, 1, 0 };
return answer[ year % 19 ];
}

Whether this is better depends on how you define
"better." The tabular version may be more compact;
the switched version is easier to read.

Both versions can misbehave if `year' is negative:
The remainder `-3 % 19' can be either -3 or 16 under
C89 rules; under C99 rules the result is -3. Negative
years may be nonsensical given the application, but the
principle of coding defensively suggests you should be
wary. Three possibilities occur to me:

1) Make the `year' argument an `unsigned int', so
negative values can never appear.

2) Insert an explicit test for negative `year', and
take some appropriate action (error message?) if
you get one.

3) Replace `year % 19' with `(year % 19 + 19) % 19',
or with `(year %= 19 < 0) ? year + 19 : year'.

(There are other ways, but these seem to cover the major
themes.)

--
Er*********@sun.com
Nov 5 '07 #11
On Nov 5, 1:15 pm, jxh <j...@despammed.comwrote:
On Nov 5, 11:10 am, mazwo...@gmail.com wrote:
On Nov 5, 1:30 pm, mazwo...@gmail.com wrote:
I'm new here, so excuse me if my style is incorrect. Can
anyone come up with a better method for this calculation?
Sorry, should have noted. This is to generate a Jewish calendar
(This is the year 5768, and therefore a leap year with an extra
month thrown in mid-March to mid-April, which is why Easter is
3 weeks later this year than it was last year). The numbers in
the code are correct. I was wondering if there was any better
algorithm. Thanks to all who responded, and apologies for the
misunderstanding.

int is_leap(int year)
{
return (0x00024949U & (1U << year%19)) != 0;

}

Although, I would recommend renaming the function to something that
won't cause confusion if the application is ever combined with code
that also deals with Gregorian.

-- James
which aligns with GMT perfectly):
http://individual.utoronto.ca/kalend.../rect.htm#leap

Nov 5 '07 #12
Richard Heathfield wrote:
>
ma******@gmail.com said:
I'm new here, so excuse me if my style is incorrect. Can anyone come
up with a better method for this calculation?

Code:
int is_leap(int year)
{
switch (year % 19) {
case 0: case 3: case 6: case 8:
case 11: case 14: case 17: return 1;
default: return 0;
}
}

This is part of a calendar program.

Here's a better method:

int really_is_leap(int year)
{
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
}

This one has the merit of actually giving the right results.
The parentheses in the return statement
make no difference in the results.

--
pete
Nov 5 '07 #13
On Nov 5, 1:40 pm, pete <pfil...@mindspring.comwrote:
Richard Heathfield wrote:
mazwo...@gmail.com said:
I'm new here, so excuse me if my style is incorrect. Can anyone come
up with a better method for this calculation?
Code:
int is_leap(int year)
{
switch (year % 19) {
case 0: case 3: case 6: case 8:
case 11: case 14: case 17: return 1;
default: return 0;
}
}
This is part of a calendar program.
Here's a better method:
int really_is_leap(int year)
{
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
}
This one has the merit of actually giving the right results.

The parentheses in the return statement
make no difference in the results.
True, but they are useful as a sort of comment for people who are
unsure about precedence. For sure, the code generated will not be any
worse.

P.S.
There are gobs of Hebrew calendars on Sourceforge such as:
http://sourceforge.net/project/showf...group_id=63109

Nov 5 '07 #14
[comp.lang.c] Richard Heathfield <rj*@see.sig.invalidwrote:
int really_is_leap(int year)
{
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
}
This one has the merit of actually giving the right results.
How about for year 0? :-)

--
C. Benson Manica | I appreciate all corrections, polite or otherwise.
cbmanica(at)gmail.com |
----------------------| I do not currently read any posts posted through
sdf.lonestar.org | Google groups, due to rampant unchecked spam.
Nov 5 '07 #15
user923005 <dc*****@connx.comwrites:
On Nov 5, 1:40 pm, pete <pfil...@mindspring.comwrote:
>Richard Heathfield wrote:
int really_is_leap(int year)
{
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
}
This one has the merit of actually giving the right results.

The parentheses in the return statement
make no difference in the results.

True, but they are useful as a sort of comment for people who are
The parentheses are not redundant--deleting them changes how the
expression parses. The precedence of && and || simply doesn't
matter in this case.
--
"I don't have C&V for that handy, but I've got Dan Pop."
--E. Gibbons
Nov 5 '07 #16
On Mon, 05 Nov 2007 21:55:58 +0000, Christopher Benson-Manica wrote:
[comp.lang.c] Richard Heathfield <rj*@see.sig.invalidwrote:
>int really_is_leap(int year)
{
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
}
>This one has the merit of actually giving the right results.

How about for year 0? :-)
There wasn't a year 0. The year after 1 BC was 1 AD.

B.
Nov 5 '07 #17
user923005 wrote:
>
On Nov 5, 1:40 pm, pete <pfil...@mindspring.comwrote:
Richard Heathfield wrote:
mazwo...@gmail.com said:
I'm new here, so excuse me if my style is incorrect.
Can anyone come
up with a better method for this calculation?
Code:
int is_leap(int year)
{
switch (year % 19) {
case 0: case 3: case 6: case 8:
case 11: case 14: case 17: return 1;
default: return 0;
}
}
This is part of a calendar program.
Here's a better method:
int really_is_leap(int year)
{
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
}
This one has the merit of actually giving the right results.
The parentheses in the return statement
make no difference in the results.

True, but they are useful as a sort of comment for people who are
unsure about precedence. For sure, the code generated will not be any
worse.
Those parentheses impose new precedence,
rather than emphasize the natural precedence of && and ||.

Part of what I was getting at, was that these expressions

year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)
(year % 4 == 0 && year % 100 != 0) || year % 400 == 0

have the same value.

--
pete
Nov 5 '07 #18
ma******@gmail.com writes:
I'm new here, so excuse me if my style is incorrect. Can anyone come
up with a better method for this calculation?

Code:
int is_leap(int year)
{
switch (year % 19) {
case 0: case 3: case 6: case 8:
case 11: case 14: case 17: return 1;
default: return 0;
}
}

This is part of a calendar program.
You mentioned later that this is for a Jewish calendar. I'm not
familiar with the rules; does it really have 7 leap years every 19
years?

Assuming the algorithm is correct, the code looks decent. You should
think about the behavior for negative years; it may not be an issue,
but you should think about it.

Another possibility would be a table lookup, something like:

int is_leap(int year)
{
static const int leap_table[19]
= { 1, 0, 0,
1, 0, 0,
1, 0,
1, 0, 0,
1, 0, 0,
1, 0, 0,
1, 0 };
return leap_table[year % 19];
}

Or you could use a bit array.

Either looks about equally good to me. In your code, I'd probably
line up the "case" keywords:

case 0: case 3: case 6: case 8:
case 11: case 14: case 17:
return 1;
default:
return 0;

or perhaps put one "case" on each line.

Given the regularity of the pattern, I suspect there's a fairly simple
integer arithmetic expression that avoids enumerating all 19 cases, or
all 7 cases, but I'm too lazy to figure it out, and for something this
small it wouldn't be a great improvement.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
Looking for software development work in the San Diego area.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
Nov 5 '07 #19
Keith Thompson wrote:
ma******@gmail.com writes:
I'm new here, so excuse me if my style is incorrect. Can anyone come
up with a better method for this calculation?

Code:
int is_leap(int year)
{
switch (year % 19) {
case 0: case 3: case 6: case 8:
case 11: case 14: case 17: return 1;
default: return 0;
}
}

This is part of a calendar program.

You mentioned later that this is for a Jewish calendar. I'm not
familiar with the rules; does it really have 7 leap years every 19
years?
The months of the Hebrew calendar are lunar months, of either 29 or 30
days. Originally the length was determined by observation of the moon,
but eventually the average length was set to 29 days 12 hours 44
minutes and 3+1/3 seconds, the value that was published by Ptolemy in
the Almagest as the period of the Lunar month. It's about 3/5 second
longer than the current astronomical month. The solar year contains
more than 12 and less than 13 lunar months. The Hebrew calendar deals
with this by inserting an extra intercalary month in the years
identified by the above algorithm. These aren't really leap years as
the term is understood in the Gregorian calendar system. The Hebrew
calendar's year comes out to about 6 minutes and 25+25/57 seconds
longer than the current astronomical solar year.

All of this is from Wikipedia, with all of the usual caveats. However,
it is consistent with what I remember from more traditionally reliable
sources.
Assuming the algorithm is correct, the code looks decent. You should
think about the behavior for negative years; it may not be an issue,
but you should think about it.
The world was supposedly created at or shortly before the start of the
first year of the Jewish calendar; it's possible that the people who
actually use it have little or no use for negative year values.

Nov 5 '07 #20
Richard Heathfield wrote:

[Q: To leap or not to leap?]
Here's a better method:

int really_is_leap(int year)
{
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
}

This one has the merit of actually giving the right results.
The Gregorian Calendar is not used everywhere in the world, even in the
Catholic countries, it was not used before

assert(year 1582);

The Protestantic countries was far more skeptical, and didn't like to be
dictated by the Pope. Anyway, Germany, Switzerland and Denmark-Norway
finally followed in

assert(year 1700);

As usual, the Brits was slow (btw they might switch to euro some day!),
so for the British Empire we have

assert(year 1752);

at least you beat Sweden/Finland to it by a year. ;-) However, the
Swedes did this their own way, and instead of loosing many days at once,
they dropped 10+ leap years afterwards.

etc. etc.
Test your function, and see how it works on years that you know to be leap
years (eg 1976, 2000, 2004, 2008) and years you know not to be leap years
(2001, 2002, 2003, 2005).

Then switch to a working algorithm.
As an example to us all, in 1983, Stanley Rabinowitz made this
*masterpiece* of a maintainer response to a VAX/VMS leap-year bug report:

http://rudy.ca/lycomplaint.html

:-)

--
Tor <bw****@wvtqvm.vw | tr i-za-h a-z>
Nov 6 '07 #21
Rob Kendrick <nn**@rjek.comwrites:
On Mon, 05 Nov 2007 21:55:58 +0000, Christopher Benson-Manica wrote:
[comp.lang.c] Richard Heathfield <rj*@see.sig.invalidwrote:
int really_is_leap(int year)
{
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
}
This one has the merit of actually giving the right results.
How about for year 0? :-)

There wasn't a year 0. The year after 1 BC was 1 AD.
There is a year 0 in the Gregorian calendar, but few people use it for
dates before its first adoption. There is no year 0 in the "Julian"
calendar which is more commonly used for giving date at this period even if
it wasn't in use with that origin. I seem to remember that taking into
account leap years as observed in that period is funny, you need to get a
table because the rules where not followed.

See http://emr.cs.iit.edu/home/reingold/...econd-edition/.
This reference could also of use for the OP.

A+

--
Jean-Marc
Nov 6 '07 #22
In article <11*********************@q5g2000prf.googlegroups.c omJohn Bode <jo*******@my-deja.comwrites:
On Nov 5, 12:30 pm, mazwo...@gmail.com wrote:
Code:
int is_leap(int year)
{
switch (year % 19) {
case 0: case 3: case 6: case 8:
case 11: case 14: case 17: return 1;
default: return 0;
}

}

This is part of a calendar program.

Try this:

return (year % 400 == 0) || ((year % 4 == 0) && (year % 100 != 0));

I think that's the correct formula (evenly divisible by 400, or evenly
divisible by 4 and not evenly divisible by 100).
Depends on the calendar you are using. The %4 rule etc. is valid for the
Gregorian calendar. I think the %19 rule above is valid for the Muslim
calendar.
--
dik t. winter, cwi, kruislaan 413, 1098 sj amsterdam, nederland, +31205924131
home: bovenover 215, 1025 jn amsterdam, nederland; http://www.cwi.nl/~dik/
Nov 6 '07 #23
On 5 Nov, 21:55, Christopher Benson-Manica
<at...@faeroes.freeshell.orgwrote:
[comp.lang.c] Richard Heathfield <r...@see.sig.invalidwrote:
int really_is_leap(int year)
{
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
}
This one has the merit of actually giving the right results.

How about for year 0? :-)
Strangely enough, a couple of my programs use 0 as a wildcard, ie
meaning any year, and in those cases I want the number of days in
February to be returned as 29, because some years do have 29 days in
February. So the above formula can be used without a special
modification.

Paul.

Nov 6 '07 #24
On 6 Nov, 00:58, Tor Rustad <tor_rus...@hotmail.comwrote:
The Gregorian Calendar is not used everywhere in the world, even in the
Catholic countries, it was not used before

assert(year 1582);

The Protestantic countries was far more skeptical, and didn't like to be
dictated by the Pope. Anyway, Germany, Switzerland and Denmark-Norway
finally followed in

assert(year 1700);

As usual, the Brits was slow (btw they might switch to euro some day!),
so for the British Empire we have

assert(year 1752);

at least you beat Sweden/Finland to it by a year. ;-) However, the
Swedes did this their own way, and instead of loosing many days at once,
they dropped 10+ leap years afterwards.

etc. etc.
I believe the Russians didn't change until 1917
--
Nick Keighley

Nov 6 '07 #25
In article <11**********************@y42g2000hsy.googlegroups .comNick Keighley <ni******************@hotmail.comwrites:
On 6 Nov, 00:58, Tor Rustad <tor_rus...@hotmail.comwrote:
....
at least you beat Sweden/Finland to it by a year. ;-) However, the
Swedes did this their own way, and instead of loosing many days at once,
they dropped 10+ leap years afterwards.
This is not entirely correct. They wanted to do that but failed to do it
after the first dropping. This was corrected in 1712 by adding a 30rd of
February, when they came back again to the Julian calendar.
I believe the Russians didn't change until 1917
And in Greece it was only adopted in 1923.
--
dik t. winter, cwi, kruislaan 413, 1098 sj amsterdam, nederland, +31205924131
home: bovenover 215, 1025 jn amsterdam, nederland; http://www.cwi.nl/~dik/
Nov 6 '07 #26
On Mon, 05 Nov 2007 11:10:45 -0800, ma******@gmail.com wrote:
>On Nov 5, 1:30 pm, mazwo...@gmail.com wrote:
>I'm new here, so excuse me if my style is incorrect. Can anyone come
up with a better method for this calculation?

Sorry, should have noted. This is to generate a Jewish calendar (This
is the year 5768, and therefore a leap year with an extra month thrown
in mid-March to mid-April, which is why Easter is 3 weeks later this
It may explain why Passover is three weeks later (actually less than
2.5) but it has nothing to do with Easter.
>year than it was last year). The numbers in the code are correct. I
was wondering if there was any better algorithm. Thanks to all who
responded, and apologies for the misunderstanding.

-- Marty (a newbie, starting off on the wrong foot)

Remove del for email
Nov 6 '07 #27
On Mon, 05 Nov 2007 12:17:33 -0800, user923005 <dc*****@connx.com>
wrote:
>OK, if people want to test this for themselves it looks like the code
should be more robust in terms of "clever" optimisers. Try the
following
which sums and prints the number of leap years found:
***/
#include <stdio.h>
#include <time.h>
#define START_YEAR 1582
#define END_YEAR 4000
#define ITERATIONS 100000

typedef unsigned (*leap_func)(unsigned);

static unsigned is_a_leap_year1(unsigned y)
{
return (y % 400u == 0u) ? 1 : (y % 100u == 0u) ? 0u : (y % 4u ==
0u);
Two of the three possible return values are signed and 1 is unsigned.
Did you have something special in mind when you specified the u after
the second "?" and not for any of the other return values?
>}

Remove del for email
Nov 7 '07 #28
On Nov 6, 4:21 pm, Barry Schwarz <schwa...@doezl.netwrote:
On Mon, 05 Nov 2007 12:17:33 -0800, user923005 <dcor...@connx.com>
wrote:

OK, if people want to test this for themselves it looks like the code
should be more robust in terms of "clever" optimisers. Try the
following
which sums and prints the number of leap years found:
***/
#include <stdio.h>
#include <time.h>
#define START_YEAR 1582
#define END_YEAR 4000
#define ITERATIONS 100000
typedef unsigned (*leap_func)(unsigned);
static unsigned is_a_leap_year1(unsigned y)
{
return (y % 400u == 0u) ? 1 : (y % 100u == 0u) ? 0u : (y % 4u ==
0u);

Two of the three possible return values are signed and 1 is unsigned.
Did you have something special in mind when you specified the u after
the second "?" and not for any of the other return values?
}
Error on my part. I converted some old code that had signed int for
all data types.
Since Gregorian dates never have negative years, I changed to
unsigned.
My intention was to change all elements of the calculations to
unsigned type to avoid any conversion costs.
I forgot a couple in that function.

Nov 7 '07 #29
user923005 wrote:
....
Since Gregorian dates never have negative years, I changed to
unsigned.
The traditional proleptic Gregorian calendar uses positive year numbers
and "BC", but the proleptic Gregorian calendar used in ISO 8601 assigns
a year number of 0 to 1BC, and negative year numbers to earlier dates.
It's up to you whether you want to support this, but ISO 8601 is, as
it's name implies, an International Standard. If you want to support the
traditional proleptic calendar, you should provide some way of
indicating BC dates.

Nov 7 '07 #30
....
Correction noted. Passover started last year on April 3, this year it
starts on April 20. My comment about Easter was so that those who
couldn't care less about Passover might also respond. However, I do
believe that Easter is (nearly) always the Sunday after Passover, and
therefore is the same 2.5 - 3 weeks later in Jewish leap years.
This is only approximately. Jewish passover is always on Nissan 14.
Christian Easter is on the first sunday *after* the first full moon
on or after the vernal equinox, where both the full moon and the vernal
equinox are based on calculations. But the calculations are different
between the Western Christian branches and the Eastern Christian branches,
so they agree only in about one third of the cases. The rule you observed
that Western Easter is almost always on the Sunday after Passover is right
(the rules are basically the same with a nineteen-year cycle), but your
statement that so it is 2.5 to 3 weeks later in Jewish leap years is
wrong. In the period 2000-2020 (5760-5780) there is only one year when
that is not the case: 2005 (5765), when Passover falls four weeks after
Easter (Passover 24th April, Easter 27th March). The reason for that small
discrepancy is that in the Easter calculations almost the same nineteen year
cycle is used as is used in the Jewish calendar. So there occurs a
difference when the Jewish calendar does have a leap year (with an added
month) while the Christian Easter calendar does *not* have a leap year (so
has no added month). The discrepance between Western and Eastern Easter
is because the Easter (lunar) calendar is calculated differently. For
this reason in 2002 Western Easter was on 31th March and Eastern Easter on
5th May the other huge differences until 2020 occur in 2005, 2008, 2013 and
2016; I suspect a discrepancy in the Metonic cycles of nineteen years used,
in other years they are either on the same day or the Eastern Easter is one
week later than the Western one.

(Yes, by now I know how to type Easter blind ;-). And by now you do not
want information about calendars in this newsgroup anymore...)
int is_leap(int year)
{
return (0x00024949U & (1U << year%19)) != 0;

}

Thanks. From reading the assembly output of GCC I saw the same
optimization, but could not understand it. I now do. Also I think this
is the better of the two suggestions that I received.
If the compiler can do the optimisation, why should you do it? When you
do it in your code you only obfuscate your code, and make debuggin a
problem.
--
dik t. winter, cwi, kruislaan 413, 1098 sj amsterdam, nederland, +31205924131
home: bovenover 215, 1025 jn amsterdam, nederland; http://www.cwi.nl/~dik/
Nov 8 '07 #31
Amandil <ma******@gmail.comwrites:
[...]
>>From Eric Sosman, I saw:
The principal alternative (which I haven't seen
anyone mention yet) would be to use a table:

int is_leap(int year)
{
static const int answer[19] = {
1, 0, 0, 1, 0, 0, 1, 0, 1, 0,
0, 1, 0, 0, 1, 0, 0, 1, 0 };
return answer[ year % 19 ];
}

I actually did think of that. This method is, I believe, the fastest
and the easiest to implement in a macro. However, I personal felt that
a 76-byte table was a waste of space.
Making the table an array of char rather than of int would reduce its
size to 19 bytes.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
Looking for software development work in the San Diego area.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
Nov 8 '07 #32
Keith Thompson wrote On 11/07/07 22:09,:
Amandil <ma******@gmail.comwrites:
[...]
>>>From Eric Sosman, I saw:
>> The principal alternative (which I haven't seen
anyone mention yet) would be to use a table:

int is_leap(int year)
{
static const int answer[19] = {
1, 0, 0, 1, 0, 0, 1, 0, 1, 0,
0, 1, 0, 0, 1, 0, 0, 1, 0 };
return answer[ year % 19 ];
}

I actually did think of that. This method is, I believe, the fastest
and the easiest to implement in a macro. However, I personal felt that
a 76-byte table was a waste of space.

Making the table an array of char rather than of int would reduce its
size to 19 bytes.
... or for even greater compaction (and obfuscation),
make it an integer constant:

int is_leap(int year) {
return (149833 >(year % 19)) & 1;
}

--
Er*********@sun.com
Nov 8 '07 #33
On Nov 8, 8:05 am, Eric Sosman <Eric.Sos...@sun.comwrote:
Keith Thompson wrote On 11/07/07 22:09,:

Amandil <mazwo...@gmail.comwrites:
[...]
>>From Eric Sosman, I saw:
> The principal alternative (which I haven't seen
anyone mention yet) would be to use a table:
> int is_leap(int year)
{
static const int answer[19] = {
1, 0, 0, 1, 0, 0, 1, 0, 1, 0,
0, 1, 0, 0, 1, 0, 0, 1, 0 };
return answer[ year % 19 ];
}
>I actually did think of that. This method is, I believe, the fastest
and the easiest to implement in a macro. However, I personal felt that
a 76-byte table was a waste of space.
Making the table an array of char rather than of int would reduce its
size to 19 bytes.

... or for even greater compaction (and obfuscation),
make it an integer constant:

int is_leap(int year) {
return (149833 >(year % 19)) & 1;
}
If there were a way to note numbers as binary in C, then there would
be no obfuscation at all since:
100100100101001001 (Base 2) = 149833 (Base 10)

Maybe something like:
0b
as a prefix would be nice.

Nov 8 '07 #34
On Wed, 07 Nov 2007 20:52:02 -0000, Amandil <ma******@gmail.com>
wrote:
>On Nov 6, 8:50 am, Barry Schwarz <schwa...@doezl.netwrote:
>On Mon, 05 Nov 2007 11:10:45 -0800, mazwo...@gmail.com wrote:
>On Nov 5, 1:30 pm, mazwo...@gmail.com wrote:
I'm new here, so excuse me if my style is incorrect. Can anyone come
up with a better method for this calculation?
>Sorry, should have noted. This is to generate a Jewish calendar (This
is the year 5768, and therefore a leap year with an extra month thrown
in mid-March to mid-April, which is why Easter is 3 weeks later this

It may explain why Passover is three weeks later (actually less than
2.5) but it has nothing to do with Easter.
>year than it was last year). The numbers in the code are correct. I
was wondering if there was any better algorithm. Thanks to all who
responded, and apologies for the misunderstanding.
-- Marty (a newbie, starting off on the wrong foot)

Correction noted. Passover started last year on April 3, this year it
starts on April 20. My comment about Easter was so that those who
couldn't care less about Passover might also respond. However, I do
believe that Easter is (nearly) always the Sunday after Passover, and
therefore is the same 2.5 - 3 weeks later in Jewish leap years.
In 2006 Easter was April 16. In 2007 it was April 8. In 2008 it will
be March 23.

I'm just wondering what "Easter is 3 weeks later this year than it was
last year" in your explanation refers to.

While Easter should follow Passover, since the Last Supper was
reportedly a Seder, in 2008 it is almost a month before Passover which
starts April 19.
Remove del for email
Nov 9 '07 #35
On Nov 7, 6:23 pm, "Dik T. Winter" <Dik.Win...@cwi.nlwrote:

int is_leap(int year)
{
return (0x00024949U & (1U << year%19)) != 0;
}
>
Thanks. From reading the assembly output of GCC I saw the
same optimization, but could not understand it. I now
do. Also I think this is the better of the two suggestions

If the compiler can do the optimisation, why should you do it?
When you do it in your code you only obfuscate your code, and
make debuggin a problem.
I agree with Dik (that's pronounced "deek" right?), if the
compiler is really converting your case statement into this
code for you, you should leave the code as a case statement.

Also, Eric's formulation is slightly more compact and in my
opinion a little clearer, except for the use of decimal to
express the constant (I find it easier to convert hex to
binary by hand when the need arises). In proper code, the
function should contain a comment explaining where the
constant came from.

-- James

Nov 9 '07 #36
jxh wrote On 11/08/07 20:38,:
[...]
Also, Eric's formulation is slightly more compact and in my
opinion a little clearer, except for the use of decimal to
express the constant (I find it easier to convert hex to
binary by hand when the need arises). [...]
Can't a guy have a little fun now and then? I was
planning to write 'MII', but decided there were too many
implementation-defined aspects to risk on c.l.c. ;-)

--
Er*********@sun.com
Nov 9 '07 #37
"Eric Sosman" <Er*********@sun.coma écrit dans le message de news:
1194537913.869007@news1nwk...
Keith Thompson wrote On 11/07/07 22:09,:
>Amandil <ma******@gmail.comwrites:
[...]
>>>>From Eric Sosman, I saw:

The principal alternative (which I haven't seen
anyone mention yet) would be to use a table:

int is_leap(int year)
{
static const int answer[19] = {
1, 0, 0, 1, 0, 0, 1, 0, 1, 0,
0, 1, 0, 0, 1, 0, 0, 1, 0 };
return answer[ year % 19 ];
}

I actually did think of that. This method is, I believe, the fastest
and the easiest to implement in a macro. However, I personal felt that
a 76-byte table was a waste of space.

Making the table an array of char rather than of int would reduce its
size to 19 bytes.

... or for even greater compaction (and obfuscation),
make it an integer constant:

int is_leap(int year) {
return (149833 >(year % 19)) & 1;
}
Very elegant! But such conciseness requires an explanatory comment for the
magic value.

--
Chqrlie.
Nov 12 '07 #38

### This discussion thread is closed

Replies have been disabled for this discussion.