developer tip

부호없는 정수를 가장 가까운 더 작거나 같은 짝수 정수로 반올림하려면 2로 나눈 다음 2를 곱할 수 있습니까?

copycodes 2020. 11. 6. 18:56
반응형

부호없는 정수를 가장 가까운 더 작거나 같은 짝수 정수로 반올림하려면 2로 나눈 다음 2를 곱할 수 있습니까?


예 :

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

할 수 있습니까 x = x/2*2;? 컴파일러가 이러한 표현을 최적화 할 위험이 있습니까?


컴파일러는 프로그램에 부작용을 일으키지 않는 한 원하는 최적화를 할 수 있습니다. 귀하의 경우에는 '2'를 취소 할 수 없으므로식이 홀수에 대해 다른 값을 갖게됩니다.

x / 2 * 2평가 엄격히 같은 (x / 2) * 2x / 2경우 정수 연산으로 수행되는 x일체형이다.

사실 이것은 관용적 반올림 기법입니다.


정수가 부호없는 것으로 지정 했으므로 간단한 마스크로 수행 할 수 있습니다.

x & (~1u)

이는 LSB를 0으로 설정하여보다 크지 않은 즉각적인 짝수를 생성합니다 x. 경우 즉, x보다 넓은하지 않는 유형이 있습니다 unsigned int.

물론 1다음과 같이 더 넓은 유형과 동일한 유형이되도록 강제 할 수 있습니다 x.

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

그러나이 시점에서 여러분은이 접근 방식을 난독 화 연습으로보고 밧세바의 대답을 받아 들여야합니다.


물론 표준 라이브러리는 잊어 버렸습니다. 포함하는 경우 stdint.h(또는 cstdint, C ++ 코드에서해야 함). 구현에서 세부 사항을 처리하도록 할 수 있습니다.

uintmax_t const lsb = 1;
x & ~lsb;

또는

x & ~UINTMAX_C(1)

C 및 C ++는 일반적으로 최적화에서 "as if"규칙을 사용합니다. 계산 결과는 컴파일러가 코드를 최적화하지 않은 것과 같아야합니다 .

이 경우 9/2*2=8. 컴파일러는 결과를 얻기 위해 모든 방법을 사용할 수 있습니다. 8. 여기에는 비트 마스크, 비트 시프트 또는 동일한 결과를 생성하는 CPU 특정 해킹이 포함됩니다 (x86에는 포인터를 구분하지 않는다는 사실에 의존하는 몇 가지 트릭이 있습니다. C 및 C ++와 달리 정수).


작성할 수 있으며 x / 2 * 2컴파일러는 x부호없는 유형이있는 경우 최하위 비트를 지우는 매우 효율적인 코드를 생성 합니다.

반대로 다음과 같이 작성할 수 있습니다.

x = x & ~1;

또는 가독성이 떨어질 수 있습니다.

x = x & -2;

또는

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

또는 이것도 :

x = x - (x & 1);

또는 supercat이 제안한이 마지막 항목은 모든 정수 유형 및 표현의 양수 값에 대해 작동합니다.

x = (x | 1) ^ 1;

위의 모든 제안은 2의 보수 아키텍처에서 모든 부호없는 정수 유형에 대해 올바르게 작동합니다. 컴파일러가 최적의 코드를 생성할지 여부는 구성 및 구현 품질의 문제입니다.

그러나 x & (~1u)유형 x이보다 크면 작동하지 않습니다 unsigned int. 이것은 직관에 반하는 함정입니다. 당신이 부호없는 정수를 사용하여 주장하는 경우에, 당신은 작성해야 x & ~(uintmax_t)1조차도 x & ~1ULL경우 실패 x보다 더 큰 유형이 있습니다 unsigned long long. 메이크업 악화 문제에 많은 플랫폼은 지금 형태의보다 큰 정수가 uintmax_t같은, __uint128_t.

다음은 약간의 벤치 마크입니다.

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.


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.


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


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.

참고URL : https://stackoverflow.com/questions/46805181/if-i-want-to-round-an-unsigned-integer-to-the-closest-smaller-or-equal-even-inte

반응형