If I want to round an unsigned integer to the closest smaller or equal even integer, can I divide by 2 then multiply by 2?

C++C

C++ Problem Overview


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 ?

C++ Solutions


Solution 1 - C++

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.

Solution 2 - C++

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)

Solution 3 - C++

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++).

Solution 4 - C++

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.

Solution 5 - C++

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.

Solution 6 - C++

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).

Solution 7 - C++

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser2370139View Question on Stackoverflow
Solution 1 - C++BathshebaView Answer on Stackoverflow
Solution 2 - C++StoryTeller - Unslander MonicaView Answer on Stackoverflow
Solution 3 - C++MSaltersView Answer on Stackoverflow
Solution 4 - C++chqrlieView Answer on Stackoverflow
Solution 5 - C++Jens GustedtView Answer on Stackoverflow
Solution 6 - C++Arthur TaccaView Answer on Stackoverflow
Solution 7 - C++ivan.ukrView Answer on Stackoverflow