67

For example :

f(8)=8
f(9)=8

Can I do x = x/2*2; ? Is there a risk that the compiler will optimize away such expression ?

Rob
  • 26,989
  • 16
  • 82
  • 98
user2370139
  • 1,297
  • 1
  • 11
  • 13
  • Optimizations aren't allowed to change the results of a well-defined operation. – Barmar Oct 24 '17 at 18:44
  • 2
    Strongly suggest using: `x >>= 1; x <<= 1;` – user3629249 Oct 24 '17 at 22:12
  • 5
    @user3629249 Why strongly suggest it? It less clearly expresses the intent than the code already in the question, it compiles to identical machine code so it's not any faster, and there's no analogue if you want to round to the nearest multiple of a different number than 2 (unless that number also happens to be a power of 2). – Arthur Tacca Oct 25 '17 at 07:36
  • because it is faster, no multiply and divide. a MUCH easier way is to simply use: `x &= ~(1)' Where the `1` could be a string of 1s of the desired length. For instance: `x &= ~(0x07);` for three bits, etc – user3629249 Oct 26 '17 at 02:12
  • 1
    if both methods are resulting in the same machine instructions, they you must have the optimization set to the max. – user3629249 Oct 26 '17 at 02:13
  • @user3629249 "optimization set to the max" Nope, arithmetic optimisations like this are the most trivial possible. Besides, who even knows if performance is important in the use case of the questioner? Unless this function is going to be called quadrillions of times, the time to type one of these comments outweights the performance gains; see "premature optimisation". So it's best to use the representation that most clearly expresses intent. By the way, use @ name when replying to a comment, otherwise the other person doesn't get notified (I only found your comment by chance). – Arthur Tacca Oct 31 '17 at 12:38

7 Answers7

85

The compiler is allowed to make any optimisiations it likes so long as it does not introduce any side effects into the program. In your case it can't cancel the '2's as then the expression will have a different value for odd numbers.

x / 2 * 2 is evaluated strictly as (x / 2) * 2, and x / 2 is performed in integer arithmetic if x is an integral type.

This, in fact, is an idiomatic rounding technique.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
  • 4
    I like the note about this being idiomatic. It's also somewhat more intuitive than a mask IMO. – StoryTeller - Unslander Monica Oct 18 '17 at 07:54
  • 29
    Of course, the compiler _is_ allowed to properly optimize this, e.g. by replacing it with `(x>>1)<<1`. IIRC, there are compilers which will do this even if optimizations are technically turned off. – MSalters Oct 18 '17 at 08:05
  • 2
    @MSalters: Yes it was a silly thing to say. I've amended. – Bathsheba Oct 18 '17 at 08:07
  • 31
    @MSalters: Or, in the case of unsigned `(x & 0xfffffffe)` :) – Matthieu M. Oct 18 '17 at 12:41
  • 2
    @MSalters While the compiler can do that, us humans aren't allowed to do that, since it invokes undefined behavior. Probably worth a mention. – Voo Oct 19 '17 at 19:29
  • 3
    @Voo: Why? It's an unsigned integer. The behavior is identical by definition. – MSalters Oct 20 '17 at 07:06
  • @MSalters What if the type of `x` is wider than `0xfffffffe`? C++ standard guarantees minimun size for unsigned integer but not a maximun size. – Anonymous Coward Oct 20 '17 at 12:02
  • @JoseAntonioDuraOlmos: We're talking compiler optimizations, they know the size of their own integers. – MSalters Oct 20 '17 at 12:13
  • @MSalters Hence why the compiler can do it but a human cannot. A human does not know if his code will ever be compiled by someone with some compiler where `x` is wider than `0xfffffffe`. – Anonymous Coward Oct 20 '17 at 12:39
  • @MSalters It looks like clang and GCC like to optimize this with `and eax, -2`. It'll do this for `x/2*2`, `x & ~1`, `(x >> 1 ) << 1`, and `x ^ 1`. – Erroneous Oct 20 '17 at 15:26
  • @MSalters I missed the part where it said unsigned, you're right. – Voo Oct 20 '17 at 21:28
53

Since you specified the integers are unsigned, you can do it with a simple mask:

x & (~1u)

Which will set the LSB to zero, thus producing the immediate even number that is no larger than x. That is, if x has a type that is no wider than an unsigned int.

You can of course force the 1 to be of the same type as a wider x, like this:

x & ~((x & 1u) | 1u)

But at this point, you really ought to look at this approach as an exercise in obfuscation, and accept Bathsheba's answer.


I of course forgot about the standard library. If you include stdint.h (or cstdint, as you should in C++ code). You can let the implementation take care of the details:

uintmax_t const lsb = 1;
x & ~lsb;

or

x & ~UINTMAX_C(1)
StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • 3
    Don't you need `x & (~static_cast(1))` or do I need a coffee? – Bathsheba Oct 18 '17 at 07:54
  • @Bathsheba - Well, integral promotions will take care of that, unless *I* need a coffee. – StoryTeller - Unslander Monica Oct 18 '17 at 07:55
  • I *thought* the parentheses would mean that the *result* of `~1u` is promoted, thus potentially introducing lots of zeros. Someone needs a coffee - that much is clear. – Bathsheba Oct 18 '17 at 07:56
  • 2
    @Bathsheba - It's most definitely I who needs a coffee, than. Mmm... I didn't give it much though about working with any unsigned integer type. – StoryTeller - Unslander Monica Oct 18 '17 at 07:58
  • When you're back, feel free to nick the `static_cast` from the comment. – Bathsheba Oct 18 '17 at 07:59
  • 1
    @Bathsheba - I would, but that C tag... I kinda want to think about it, and be all cool by producing a solution that works in both C and C++. – StoryTeller - Unslander Monica Oct 18 '17 at 08:00
  • 1
    @Bathsheba - But only up to `UINT_MAX`, no? Wer'e still in the same boat if `x` is `unsigned long`, I *think*. – StoryTeller - Unslander Monica Oct 18 '17 at 08:03
  • @StoryTeller: Yup, that -1u idea was hogwash. It's me who needs the coffee now. Then I thought of `-(x/x)` but that breaks if x is 0. – Bathsheba Oct 18 '17 at 08:05
  • 4
    I'm afraid `x & (~1u)` does not work if the type of `x` is larger than `unsigned int`. Conversely, `x & ~1` would behave as expected for all types. This is a counter-intuitive pitfall. If you insist on using an unsigned constant, you must write `x & ~(uintmax_t)1` as even `x & ~1ULL` would fail if `x` has a larger type than `unsigned long long`. To make matters worse, many platforms now have larger integer types than `uintmax_t`, such as `__uint128_t`. – chqrlie Oct 18 '17 at 09:31
  • @chqrlie - Umm, I did cover all of this in my answer (other than `x & ~1`). – StoryTeller - Unslander Monica Oct 18 '17 at 09:32
  • 1
    Re-reading your answer, you did mention *if the type of `x` is no wider than `unsigned int`*, but why use the `u` suffix at all? It is just about as obscure why it does not work with `1u` and why it does with just `1`, even for overly large types. – chqrlie Oct 18 '17 at 09:46
  • @chqrlie - For the same reason that `~1` and `-2` would raise eyebrows, despite being correct. What *everyone* knows is that bitwise operations are most well defined for unsigned types. Understanding the minutia of standardese for signed types is less common. This is simply to reduce the rate of WTF per minute (a well known metric of code readability). – StoryTeller - Unslander Monica Oct 18 '17 at 09:48
  • I agree and this is an excellent example of *simplicity is better*, albeit for non obvious reasons, – chqrlie Oct 18 '17 at 09:56
  • @chqrlie - I must also say, that I think the bind I got into with widening the unsigned constant should serve as another valuable lesson to the OP (or I hope it would). Anyway, your answer gives a neat summary of the whole thing. So +1 to that. – StoryTeller - Unslander Monica Oct 18 '17 at 09:58
  • So much extra code just to avoid a feature of the language... And even with `uintmax_t` this may not work if `x` has a third-party extended integer type, when conversion of signed to unsigned would. – Ruslan Oct 18 '17 at 14:50
  • Actually see [this example](http://coliru.stacked-crooked.com/a/187ca1e3fc5e9205), which demonstrates that your attempts to avoid signed-to-unsigned conversion still don't work. – Ruslan Oct 18 '17 at 15:07
  • @chqrlie: If `x` is an unsigned type at least as large as `unsigned`, `x & ~1u` will yield `x & (UINT_MAX-1)` on any conforming implementation. On a ones'-complement C89 system (or a hypothetical ones'-complement C99 or C11 system), `x & ~1` would yield `x`. The form `x & -2` will yield `x | 1 ^ 1` on any conforming implementation regardless of how it represents signed values. – supercat Oct 18 '17 at 16:18
  • @Ruslan - My **original** attempt does work, albeit inelegantly. The fact `uintmax_t` and `__uint128_t` are not synonyms is a quality of implementation issue, [one that can be debated at length](https://stackoverflow.com/questions/21265462/why-in-g-stdintmax-t-is-not-a-int128-t). – StoryTeller - Unslander Monica Oct 18 '17 at 17:24
  • @supercat: You are correct about one's complement. The code fails too on conforming sign/magnitude architectures: `~1` has the value `-INT_MAX+1`. Converting this to type `unsigned int` by repeatedly adding `UINT_MAX+1` gives either `1` if `UINT_MAX==INT_MAX` or `INT_MAX+2` if `UINT_MAX == INT_MAX*2 + 1`. In the first case `x & ~1` evaluates to the same as `x & 1` and in the second case `x & ~1` evaluates to `x & 0x8001` (on 16 bits, adjust for the number of bits in the type). Neither is the expected value. – chqrlie Oct 18 '17 at 19:52
  • @supercat: operator precedence is such a nightmare: `x | 1 ^ 1` parses as `x | (1 ^ 1)`... With the appropriate parentheses, your proposal is a valid alternative, portable to all integer representations and for which `gcc -O1` produces the same code. – chqrlie Oct 18 '17 at 19:55
  • @chqrlie: Have any sign-magnitude implementations conforming to any standard been produced or even updated since the publication of C99? I've read documentation for a one's-complement implementation that was updated in 2005 but isn't fully C99 conforming. From what I can tell, sign-magnitude was obsolete even before C99 was published. – supercat Oct 18 '17 at 20:27
  • @supercat: I haven't any more than you have, and I would welcome both sign/magnitude and one's complement being removed from the C Standard. Keeping them there seems solely motivated by extreme conservatism and idolatry. – chqrlie Oct 18 '17 at 22:35
  • @chqrlie: I think one issue is that it would be sensible to have some actions (e.g. -1<<1) invoke Undefined Behavior in non-TC implementations, but there's no sensible reason not to define them on TC ones. Some people, however, are of the opinion that it's somehow "better" to leave such things as UB to facilitate "optimization". Removing any justification for such behaviors to remain undefined would remove any justification for such "optimizations". – supercat Oct 18 '17 at 22:43
  • 1
    @Mehrdad: I added your proposed alternative, same code generation. Thank you. – chqrlie Oct 19 '17 at 10:33
  • (x | 1) ^ 1 is less ugly than the other form of forcing the type. – Joshua Oct 19 '17 at 22:01
36

C and C++ generally use the "as if" rule in optimization. The computation result must be as if the compiler didn't optimize your code.

In this case, 9/2*2=8. The compiler may use any method to achieve the result 8. This includes bitmasks, bit shifts, or any CPU-specific hack that produces the same results (x86 has quite a few tricks that rely on the fact that it doesn't differentiate between pointers and integers, unlike C and C++).

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • 11
    [Case and point](https://godbolt.org/g/CR4q3v). This is what GCC produces with `-O1`. All the approaches are distilled to the same machine code. – StoryTeller - Unslander Monica Oct 18 '17 at 08:37
  • And as I suspected, for all 3 variants GCC will use the `and RAX, -2` method even on `-O0`. – MSalters Oct 18 '17 at 08:41
  • ... and GCC on ARM knows to use the `bic` (Bit Clear) instruction. Checking further, MSVC and ICC generate the same `and , -2` code as GCC. – MSalters Oct 18 '17 at 08:46
  • 7
    GCC is highly unusual in that it applies these sorts of mathematical/bit-twiddling peephole optimizations even when optimizations are disabled. That's kind of an interesting and notable design quirk. Other compilers perform a much more literal translation of the C code into machine instructions with optimization disabled, but once you enable the optimizer, the output is all the same, which is why you should write the code for readability unless you know for sure that your compiler is brain-dead. – Cody Gray - on strike Oct 18 '17 at 11:45
  • @CodyGray: Having `uintVal/=2` evaluated as equivalent to `uintVal>>=1` is really no different from having `x>>=2` be evaluated with code equivalent to `x>>=1; x>>=1;` or `x=((x>>1)>>1)` instead of code equivalent to `for(int i=0; i<2; i++) x>>=1;`. It's not really an "optimization"--it's simply a straightforward piece of code generation. – supercat Oct 18 '17 at 16:24
  • 1
    Transforming a division to a shift is a standard optimization technique known as "strength reduction". Yes, it's very standard, and extremely straightforward, but I disagree that makes it not an optimization. @supercat – Cody Gray - on strike Oct 18 '17 at 16:27
  • @CodyGray: If a code generator works by traversing an expression tree, having different expansions for different combinations of operands isn't usually called an "optimization". Processing "x=y+1;" in using a completely context-independent code generator would yield something like `mov ax,&x / push ax` `mov ax,&y / push ax` `pop bx / mov ax,[bx]` / `push ax` `mov ax,1 / push ax` `pop ax / pop bx / add ax,bx / push ax` `pop ax / pop bx / mov [bx],ax`. Would having a compiler produce `mov ax,[y] / inc ax / mov [x],ax` really be called "optimizing"? – supercat Oct 18 '17 at 17:43
  • @CodyGray the standard defines `x >> 1` as `x / 2` , for unsigned integers – M.M Oct 18 '17 at 20:22
  • 1
    @supercat: I mostly agree with Cody Gray here. It's not so much the strength reduction; it's the fact that GCC does not generate code for individual expressions. `x/2*2` compiles to a single statement. In general, compiling without optimizations is done for debugging purposes, and then you want a 1:N correspondence between source code and assembly. – MSalters Oct 19 '17 at 07:13
21

You can write x / 2 * 2 and the compiler will produce very efficient code to clear the least significant bit if x has an unsigned type.

Conversely, you could write:

x = x & ~1;

Or probably less readably:

x = x & -2;

Or even

x = (x >> 1) << 1;

Or this too:

x = x - (x & 1);

Or this last one, suggested by supercat, that works for positive values of all integer types and representations:

x = (x | 1) ^ 1;

All of the above proposals work correctly for all unsigned integer types on 2's complement architectures. Whether the compiler will produce optimal code is a question of configuration and quality of implementation.

Note however that x & (~1u) does not work if the type of x is larger than unsigned int. This is a counter-intuitive pitfall. If you insist on using an unsigned constant, you must write x & ~(uintmax_t)1 as even x & ~1ULL would fail if x has a larger type than unsigned long long. To make matters worse, many platforms now have integer types larger than uintmax_t, such as __uint128_t.

Here is a little benchmark:

typedef unsigned int T;

T test1(T x) {
    return x / 2 * 2;
}

T test2(T x) {
    return x & ~1;
}

T test3(T x) {
    return x & -2;
}

T test4(T x) {
    return (x >> 1) << 1;
}

T test5(T x) {
    return x - (x & 1);
}

T test6(T x) {  // suggested by supercat
    return (x | 1) ^ 1;
}

T test7(T x) {  // suggested by Mehrdad
    return ~(~x | 1);
}

T test1u(T x) {
    return x & ~1u;
}

As suggested by Ruslan, testing on Godbolt's Compiler Explorer shows that for all the above alternatives gcc -O1 produces the same exact code for unsigned int, but changing the type T to unsigned long long shows differing code for test1u.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • Here's an example of what GCC does with `x/2*2` for unsigned `x`: https://godbolt.org/g/Nee7cJ – Ruslan Oct 18 '17 at 12:49
  • @Ruslan: Thanks for your comment, gcc indeed produces the same code for all proposed alternatives. – chqrlie Oct 18 '17 at 14:05
  • @M.M: I believe it works for all integer types and representations, but only for positive values of `x`. Can you demonstrate for what value it would not work? – chqrlie Oct 18 '17 at 22:29
  • 1
    @M.M: How so? `1` is positive, and always is represented as `00...01`. `|1` sets the LSB. `^1` toggles that same bit. One's complement affects the value of the MSB c.q. the value of `INT_MIN` but we're not touching the MSB. IOW, number representations start to matter when you mix arithmetic and numerical operations, and we don't do that. (But the question leaves open how to round negative numbers, round down or round to zero) – MSalters Oct 19 '17 at 07:18
  • @chqrlie all positive numbers have the same representation; the only reason to mention "all representations" is if you're talking about negative numbers. (The question was originally about positive and negative but was later edited). All of the examples here work for positive numbers – M.M Oct 19 '17 at 07:32
  • @M.M: I'm afraid some of the examples produce negative numbers as intermediary results, such as `-2` and `~1`, which are converted to the type of `x` before evaluating the binary operators. The value of `~1` does depend on the representation and size of signed integers, and the value of `(unsigned int)~1` depends both on the representation of signed integers and the sizes of `int` and `unsigned int`. – chqrlie Oct 19 '17 at 10:42
  • While the OP's question is about unsigned, testing the code with signed will also be illuminating. `x - (x%2)?1:0` would be an example of a case that leads to a different result for signed and unsigned values and whose behavior is fully defined with signed `x`. An answer whose behavior is well specified for signed x is superior to one which isn't, all things being equal, as you never know when someone is going to refactor. As an aside, `x&~decltype(x)1` solves your large unsigned type problem. – Yakk - Adam Nevraumont Oct 19 '17 at 14:57
  • 1
    What good is a puzzle of bit manipulation without everyone's favorite, modulo! `x = x - (x%2)`? This has the advantage of scaling to any `n` where `roundToMultiple(int x, int n) { return x - (x % n); }` – corsiKa Oct 20 '17 at 03:44
7

If your values are of any unsigned type as you say, the easiest is

x & -2;

The wonders of unsigned arithmetic make it that -2 is converted to the type of x, and has a bit pattern that has all ones, but for the least significant bit which is 0.

In contrary to some of the other proposed solutions, this should work with any unsigned integer type that is at least as wide as unsigned. (And you shouldn't do arithmetic with narrower types, anyhow.)

Extra bonus, as remarked by supercat, this only uses conversion of a signed type to an unsigned type. This is well-defined by the standard as being modulo arithmetic. So the result is always UTYPE_MAX-1 for UTYPE the unsigned type of x. In particular, it is independent of the sign representation of the platform for signed types.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
  • 4
    It may be worth noting, just to preempt any confusion, that converting -2 to "unsigned" will yield the "unsigned value" which, when added to 2, will yield 0, *regardless of whether a system uses two's-complement signed math*. By contrast, if one had a ones`-complement system, ~1 would equal -1, which when converted to unsigned would yield a value with all bits set. – supercat Oct 18 '17 at 16:15
  • @supercat, thanks, yes exactly. I tried to integrate these ideas in my answer. – Jens Gustedt Oct 19 '17 at 08:28
  • To make this safe for unsigned types smaller than unsigned int you can use (1u * x) & -2 – plugwash Oct 19 '17 at 17:37
6

One option that I'm surprised hasn't been mentioned so far is to use the modulo operator. I would argue this represents your intent at least as well as your original snippet, and perhaps even better.

x = x - x % 2

As others have said, the compiler's optimiser will deal with any reasonable expression equivalently, so worry about what's clearer rather than what you think is fastest. All the bit-tweaking answers are interesting, but you should definitely not use any of them in place of arithmetic operators (assuming the motivation is arithmetic rather than bit tweaking).

Arthur Tacca
  • 8,833
  • 2
  • 31
  • 49
  • 1
    Yes, compiler's optimizers are smart enough: https://godbolt.org/g/U9zsiL – Bob__ Oct 19 '17 at 13:04
  • 1
    While the OP did specify unsigned, it should be noted that the above produces possibly surprising results when you feed it a signed integer. – Yakk - Adam Nevraumont Oct 19 '17 at 13:51
  • @Yakk In C, modulo is defined to be `x % d = x - x / d * d`, so my snippet is guaranteed to give identical results to the `x = x/2*2` mentioned in the question. Assuming it's `x` you're worried about being negative (rather than a negative replacement for `2`), this gives what OP probably wants: if `x=-7` then `x/2*2 == x - x%2 == -6`. – Arthur Tacca Oct 19 '17 at 14:44
  • But the fact that you had that worry, and I wasn't sure until I checked it, does throw out my argument about it being easier to read (in the case of negative numbers). – Arthur Tacca Oct 19 '17 at 14:45
  • @ArthurTacca I'd argue even the OP's `x/2*2` is also surprising with negative integers. So not sure how to avoid having code that behaves surprising to someone with negative integers! – Yakk - Adam Nevraumont Oct 19 '17 at 14:56
  • Why not make it even more obvious and instead write `const unsigned int floored = ((x % 2) == 0) ? x : x - 1;` – JonatanE Oct 19 '17 at 17:05
  • @JonatanE The way I wrote it seems more natural to me, and works for other dividends by just replacing the 2 with the new number. – Arthur Tacca Oct 19 '17 at 22:01
-2

just use following:

template<class T>
inline T f(T v)
{
    return v & (~static_cast<T>(1));
}

Do not afraid that this is function, compiler should finally optimize this into just v & (~1) with appropriate type of 1.

ivan.ukr
  • 2,853
  • 1
  • 23
  • 41