Search code examples
c++expressionoperator-precedencearity

Precedence & Arity in strange statement


What is the execution order and possible results of this statement: leap = year%4 == 0;

(The left part is assignment and the right assertion?) Which is excerpt from the following algorithm used to calculate day of the week of any date in Gregorian calendar:

static char daytab[2][13] =
{
                {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
                {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};
for (year = 1; year <= y; year++)
{
        leap  = year%4 == 0;
        if (year%100 == 0 && year%400 != 0)
                leap = 0;
        for (month = 1; month <= m; month++)
        {
                d += daytab[leap][month - 1];
                daystotal = 365*(year - 1) + (int)floor((year-1)/4)
                - (int)floor((year - 1)/100) + (int)floor((year - 1)/400) + d;
        }
}

Solution

  • year % 4 == 0 is equivalent to (year % 4) == 0, which takes the remainder after dividing year by 4, then compares that to 0 and produces a Boolean result--true or false. You don't show a declaration/definition of leap, so we can only guess at its type. If it's a bool, that result will be assigned directly to it. Based on the later code:

    if (year%100 == 0 && year%400 != 0)
        leap = 0;
    

    It appears that leap is probably some other arithmetic type (probably int). This is quite common, especially in code that was originally written in C90, which has no Boolean type.

    In this case, the bool will be converted before the assignment. When being converted to arithmetic types, false converts to 0 and true converts to 1 (going in the other direction, 0 converts to false, and any other value converts to true).

    So, the code has basically the same effect as something like:

    if ((year % 4) == 0)
        leap = 1;
    else
        leap = 0;
    

    ...or:

    leap = ((year % 4) == 0) ? 1 : 0;
    

    If I were writing it, I think I'd probably write the code more like this:

    if ((year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0) )
        leap = 1;
    else
        leap = 0;
    

    ...or (more likely) just:

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

    At least to me, this more directly states the conditions of "what is a leap year" in the Gregorian calendar1. I suppose you could view the original code as a historical artifact through: it first computes whether it's a leap year under the rules of the older Julian calendar, then separately adds on the rules that were added in the Gregorian calendar. If somebody really wanted to reflect that history, however, they should probably include a comment explaining that that's why the code is written as it is. As it stands right now, rather than just computing the correct value, it starts by computing a value that may be incorrect, then checks for more conditions, and patches the value up afterward.


    1. Which are, of course, something like: A year is a leap year if it is divisible by 4, and either not divisible by 100, or also divisible by 400. So, for example, 2000 was a leap year (divisible by 4 and also divisible by 400) but 1900 was not (divisible 4, but also divisible by 100 and not divisible by 400).