leap year - by only using arithmetical operators in C

Viewed 101

I have the weirdest exercise I've ever seen: I have to find a leap year by scanning a year in the console and to control if that is a leap year.

I can only use + - / * % as arithmetical operators; I am not allowed to use any other operators or functions.

Here is what I have so far:

   int year = 0;
   bool b = false;
    
   printf ("Type in a year: ");
    
   int helpVar = 1000;
   for (int i = 0; i < 4; i++) {
       year += (getchar() - '0') * helpVar;
       helpVar = helpVar / 10;
   }
    
   b = (((year % 4) + (year % 100) + (year % 400)) + 1) % 2;

So I don't understand what I am doing wrong here. It works so far, the only case that's freaking me out is for year "1900". It shouldn't be a leap year, but appears to, by my code.

What am I missing here?

3 Answers

Here is one possibility (perhaps not the shortest -- obviously only works for the Gregorian calendar):

b = (((year-1)%4)+1)/4 - (((year-1)%100)+1)/100 + (((year-1)%400)+1)/400;

The idea is that ((year-1) % n) + 1 equals n only if year is a multiple of n (for positive year), and is smaller than n otherwise. Thus, if you divide that by n, you get 1 if and only if (year % n == 0).

Since year%100==0 cannot be true if year%4==0 is not, you can subtract that from each other, but add the year%400==0 term at the end.

The problem is that your % operations are done in integer arithmetic and, as such, their results may well be values other than 0 or 1. However, if you cast each of those results to the bool type (assuming that is as defined in the <stdbool.h> header), then your formula will work:

    b = (((bool)(year % 4) + (bool)(year % 100) + (bool)(year % 400)) + 1) % 2;

The only modifications I've made to your code is to add the (bool) casts on the results of each of the % operations.


EDIT: As pointed out in the comments, the above solution 'breaks the rules' by using the cast operator. The following modification also works (in a similar way), but it uses the ! operator (twice) on each % result; this also breaks the rules, but it may be nice for posterity:

    b = ((!!(year % 4) + !!(year % 100) + !!(year % 400)) + 1) % 2;

A modification of this solution using only implicit type conversions to bool (thus not using cast operators) would appear to be within the rules. This can be done using temporary/intermediate variables to hold the results of each % operation:

    bool four = year % 4, hundred = year % 100, fourhund = year % 400;
    b = (four + hundred + fourhund + 1) % 2;

Or, you could pre-declare the three intermediate bool variables and then do those implicit type conversions 'inline', like this:

    bool m4, m100, m400; // You could also move this to where you declare "b"
    b = (((m4 = year % 4) + (m100 = year % 100) + (m400 = year % 400)) + 1) % 2;

From the post above, the definition of a leap year is that: for year n

  • n should be a multiple of 4, which can be presented as n % 4 == 0
  • n is a multiple of 400 or n is not a multiple of 100

The problem is that you shouldn't write all those three conditions as a addition operation. You should write them as logical expression.

In your code:

b = (((year % 4) + (year % 100) + (year % 400)) + 1) % 2;

there are 3 conditions, year % 4, year % 100 and year % 400, there are two ways to make b = true after the evaluation:

  • all three conditions are false
  • there are one and only one case is false

Okey, let's make it clear

The definition of leap year:

  • n is multiple of 4 and not a multiple of 100
  • n is multiple of 400

So you code can be like this:

int year;
if (year % 400) {
    printf("Fine it's a leap year\n");
} else if (year % 100 == 0) {
    printf("Oh no it's not a leap year\n");
} else if (yaer % 4 == 0) {
    printf("It's a leap year\n");
} else {
    printf("Not a leap year\n");
}
Related