52

While looking through some example C code, I came across this:

y -= m < 3;

What does this do? It it some kind of condensed for loop or something? It's impossible to google for as far as I know.

Nakilon
  • 34,866
  • 14
  • 107
  • 142
TheTedinator
  • 745
  • 1
  • 8
  • 16
  • 11
    It's important to note that `<` has higher precedence (binds more tightly) than `-=`, so `y -= m < 3` means `y -= (m < 3)`. – Keith Thompson Oct 23 '11 at 00:03
  • 20
    It's basically the equivalent of `if(m<3) y=y-1;` – mpen Oct 23 '11 at 00:11
  • Did you just ask this to prove that your coworker should not have written this because it's too confusing? The competing answers are telling. – Chris Moschini Oct 23 '11 at 05:44
  • 2
    @TheTedinator When you have a question about C expressions, don't google it. Instead, just open the C standard http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf and read the corresponding section. That's faster and more informative. – Yuji Oct 23 '11 at 07:49
  • 2
    @Chris: This code is likely the best way to handle this if you've got a crappy compiler (e.g. many microcontrollers or you need the code to be fast even at -O0) and you need to keep branches and code size down (e.g. many microcontrollers or you need the code to be fast even at -O0). –  Oct 23 '11 at 10:49
  • I see. This isn't an answer, but if this were a codebase I was working with, I'd like to see a comment that spells it out the conventional way (if (m < 3) etc). – Chris Moschini Oct 24 '11 at 01:51
  • This is a part of Tomohiko Sakamoto's algorithm to calculate day of the week. – user2260040 Jan 29 '17 at 21:31

8 Answers8

75

m < 3 is either 1 or 0, depending on the truth value.

So y=y-1 when m<3 is true, or y=y-0 when m>=3

Nakilon
  • 34,866
  • 14
  • 107
  • 142
Wei Shi
  • 4,945
  • 8
  • 49
  • 73
  • 1
    Ok thanks, I didn't realize that you could use boolean statements as a value like that. – TheTedinator Oct 24 '11 at 19:26
  • @TheTedinator: This is possible in any language which will type-convert boolean "false" to the integer "0" and boolean "true" to the integer "1". – Chris Tonkinson Oct 25 '11 at 18:35
  • Java got mad at me when I tried it, but I was able to just use an if statement. – TheTedinator Oct 27 '11 at 03:11
  • 2
    C doesn't have Booleans, so Boolean expressions evaluate to integers. You cannot do the same in java because there are boolean and Boolean. – Dave Oct 27 '11 at 16:45
25

If you break it down by order of precedence for each operation, you get:

y = (y - (m < 3));

m < 3 gets evaluated and returns a boolean result 1 or 0, so the expression can be simplified as

y = y - 1; // if m < 3 is true

or

y = y - 0; // if m < 3 is false

The purpose for doing this is to avoid an if clause.

Jasarien
  • 58,279
  • 31
  • 157
  • 188
14

I means if (m < 3) { y -=1; }, since (m < 3) is 1 if m is less than 3, 0 otherwise.

The code appears in some hoary old reference implementation of something to do with either leap years or Easter, or possibly both: the first two months January and February are special because they occur before the leap day. There isn't really any excuse for writing code like that, unless you actually like the look of it. Most people don't.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
10

m < 3 evaluates to 1 if m is less than 3. Hence, y is decreased by one in this case. Thus, an if statement avoided.

AttishOculus
  • 1,439
  • 1
  • 11
  • 18
  • If you want to keep it readable and still avoid the `if`, use y -= (m < 3 ? 1 : 0); The parentheses are optional, but they improve the readability a bit. – AttishOculus Nov 05 '11 at 19:44
2

Most interesting is the amount of code it creates. On a simple Arduino system (compiler version 1.8.10) both the original and the

y -= (m < 3 ? 1 : 0); 

create the same code size. However, the 'if' version:

if(m<3) {y -= 1;} 

actually creates 8 bytes less code (4 instructions less). A good example of how creating clever code doesn't necessarily result in smaller code, especially with a good compiler.

Au Nguyen
  • 655
  • 4
  • 12
2

I can't tell you want it's for, but I can tell you what it does:

m < 3 returns an int of 0 or 1 representing a boolean value.

if m is less than 3, the statement evalutates as: y -= 1 or y = y - 1;

if m is greater than or equal to 3, the statement evalutates as y -= 0 or y = y - 0. Overall in this case, the statement does nothing.

James Webster
  • 31,873
  • 11
  • 70
  • 114
  • 6
    In C, `m < 3` doesn't yield a boolean (meaning a value of type `bool` or `_Bool`). It yields a value of type `int`, with the value 0 or 1. That value can be used as a condition or, as in this case, as an arithmetic value. – Keith Thompson Oct 23 '11 at 00:02
0

< has a higher priority to the sign of equal = so m<3 returns 1 if m is less than 3, and else is 0

then y = y - 1 or y = y - 0

log0
  • 10,489
  • 4
  • 28
  • 62
Dan
  • 950
  • 8
  • 13
-1

I agree that the code is equivalent to if(m<3) y=y-1;

However it is unsafe code because while FALSE is always 0, TRUE has had a couple of definitions. Nowadays TRUE is equal to 1 in most programming systems, but in older systems it was equal to -1 (where all bits where set to on, not just the last one). So if this code is running on an older platform it could translate to if(m<3) y=y-(-1); or if(m<3) y=y+1; Cross system compatibility is a big reason to avoid 'clever' code like this.

Mark Brown
  • 92
  • 1
  • 9
    -1 In The C99 Standard (Looking at N1256) guarantees in 6.5.8.6 that: "Each of the operators <, >, <=, and >= shall yield 1 if the specified relation is true and 0 if it is false.". This behaviour was also guaranteed by C89 and in fact goes all the way back to K&R... So it should be considered sufficiently portable :) – James Greenhalgh Oct 23 '11 at 08:15