1

On codecademy there exists a course on C, which includes a project on how to make a calendar. This project includes a boolean function which decides if a given year is a leap year or not. Code:

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

Given my beginner understanding of operators and return statements, my reading of this code would be: "A given year will be a leap year if it is divisible by 4 AND 100 OR 400." But this would mean that 1992 wouldn't be a leap year, and 1900 would be, which is plainly wrong.

How come then, that when I run the code and input these years, it does return a correct answer?

klutt
  • 30,332
  • 17
  • 55
  • 95
A. Dean
  • 13
  • 2
  • 2
    This is a prime example of how NOT write code – klutt Apr 19 '22 at 22:35
  • @klutt disagree. If you know what `&&` and `||` mean then it's readable and concise – M.M Apr 19 '22 at 22:53
  • @M.M disagree. Omitting the comparison in `year % 100 != 0` makes the code shorter, but reduces readability. – user3386109 Apr 19 '22 at 23:01
  • @user3386109 why not `year % 100 != 0 != 0` then, if you think adding `!= 0` helps ?! it's a basic part of C that `foo` in a condition means `foo != 0` – M.M Apr 19 '22 at 23:56
  • 1
    @M.M I'd rather say that it's understandable than readable. The expression `year % x` is used trice, and in two of these it's interpreted as an integer and the last one as a boolean. I'd accept `!(year % 4) && (year % 100 || !(year % 400))` or `year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)` – klutt Apr 20 '22 at 00:07
  • @M.M *"why not year % 100 != 0 != 0 then, if you think adding != 0 helps ?"* - Are you seriously implying that if it is a good thing to add `!= 0` once, then it logically follows that it's even better to add it twice. No offense, but you claim that, then your credibility about logic operators kind of drops... – klutt Apr 20 '22 at 00:11

2 Answers2

2

You appear to think

x || y == 0

means

x == 0 || y == 0

But it doesn't.

x || y == 0 doesn't mean "x or y is equal to zero".

x || y == 0 means "x, or y is equal to zero".

Put more clearly,

x || y == 0 means "(x) is true or (y is equal to zero) is true".

Since true simply means non-zero in C,

x || y == 0

is equivalent to

x != 0 || ( y == 0 ) != 0

That means the formula checks if the year isn't divisible by 100.

year % 4 == 0 Year is divisible by 4.

year % 100 Year isn't divisible by 100.

year % 400 == 0 Year is divisible by 400.

(Year is divisible by 4) and ( (Year isn't divisible by 100) or (Year is divisible by 400) )

How this would normally be stated in English:

It's a leap year if it's divisible by 4, but not by 100. Except years divisible by 400 are leap years.

And here's how things are calculated:

year % 4 == 0 && (year % 100 || year % 400 == 0)
1992 % 4 == 0 && (year % 100 || year % 400 == 0)
       0 == 0 && (year % 100 || year % 400 == 0)
            1 && (year % 100 || year % 400 == 0)
            1 && (year % 100 || year % 400 == 0)
            1 && (1992 % 100 || year % 400 == 0)
            1 && (        92 || year % 400 == 0)
            1 &&                               1 
                                               1

The right-hand side of || isn't evaluated because its left-hand is true.

ikegami
  • 367,544
  • 15
  • 269
  • 518
  • Oops, thought you had used 1982. Fixed. – ikegami Apr 19 '22 at 22:41
  • I appreciate the answer. So if I understand correctly, this has to do with bits, and the fact that any remainder in "x != 0" will show up as bigger than 0? – A. Dean Apr 19 '22 at 22:54
  • No. Nothing to do with bits, and there are no checks to see if anything is bigger than zero. `1992 % 100` returns `92`, and `||` checks if that's zero (false) or not (true). It's not zero, so it's true. And true or anything is true. For example, "the sky is blue or the water is red" is a true statement if the sky is blue. You only need to check the colour of the water if the sky isn't blue. – ikegami Apr 19 '22 at 22:58
  • I think I am beginning to understand. The differences between standard math and programming are beginning to wear on my migrained out nerves, and I forgot about the right-hand side rules. Thanks for the answers. – A. Dean Apr 19 '22 at 23:06
  • This is identical to [standard math](https://en.wikipedia.org/wiki/Boolean_algebra). `||` in C or `∨` in Math checks the truth of its operands, and T∨x = T – ikegami Apr 19 '22 at 23:06
  • Only if you are working on the premises of C. – A. Dean Apr 19 '22 at 23:09
  • No. "Or" in the same in both math and C. (Well, there's no concept of short-circuiting in math, but that's because it's moot because math doesn't have functions with side-effects. That's not relevant for this Q&A, though.) – ikegami Apr 19 '22 at 23:09
  • I didn't say there are no differences between C and Math. That would be silly. They're completely different things. It's like comparing cars and an Oral-B electric toothbrush. You're very limited in what you could compare. Same here. You can compare the operators they have in common, and that's about it. And for the operators in the expression in question, there are no relevant differences. The modulus operator works the same (for positive numbers), the and and or operators work the same, and the comparison operator works the same. – ikegami Apr 19 '22 at 23:17
  • Okay. In this case, it was rather the syntax which was confusing to me. Have a good day – A. Dean Apr 19 '22 at 23:23
1

This return statement

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

can be equivalently rewritten like

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

or like

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

The condition means that a leap year is divisible by 4 and either not divisible by 100 or divisible by 400. So 1992 is a leap year because it is divisible by 4 but not divisible by 100. And 1900 is not a leap year because though it is divisible by 4 but it also divisible by 100 and not divisible by 400. That is neither this condition (year % 4 == 0 && year % 100 != 0 ) nor this condition (year % 4 == 0 && year % 400 == 0)) is satisfied.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335