부울과 비트 비교


12

uint16_t로 인코딩 된 플래그 세트가 있다고 가정 해보십시오 flags. 예를 들면 다음과 같습니다 AMAZING_FLAG = 0x02. 이제 기능이 있습니다. 이 기능을 사용하려면 플래그를 변경해야하는지 확인해야합니다. 변경하려면 플래시에 기록해야하기 때문입니다. 그리고 그것은 비싸다. 따라서 나는 flags & AMAZING_FLAG같은지 알려주는 검사를 원합니다.doSet . 이것이 첫 번째 아이디어입니다.

setAmazingFlag(bool doSet)
{
    if ((flags & AMAZING_FLAG) != (doSet ? AMAZING_FLAG : 0)) {
        // Really expensive thing
        // Update flags
    }
}

이것은 직관적 인 if 문이 아닙니다. 더 좋은 방법이 있어야한다고 생각합니다.

if ((flags & AMAZING_FLAG) != doSet){

}

그러나 이것은, 실제로 일을하지 않는 true동일하게 보인다 0x01.

그렇다면 부울과 비트를 비교할 수있는 깔끔한 방법이 있습니까?


2
논리가 무엇인지 분명하지 않습니다. 이것 (flags & AMAZING_FLAG) && doSet입니까?
kaylum

질문은 명확하지 않습니다. '플래그'가 0x01인지 아닌지를 확인하고 싶습니다. 당신이 원하는가요? 그렇다면 비트 연산자 '&'를 사용할 수 있습니다.
Vikas Vijayan

doSet가 true 인 경우에만 당신이 그것을 더 읽기 전화 setAmazingFlag을 확인하려면 다음 함수 이름 체크 아웃 더 나은 그렇지 않으면 당신은 나 이름이 말씀을하지 않을 수있는 기능이 나쁜 코드 판독있게
AndersK

당신이하려는 모든 것이 그것을 더 읽기 쉽게 만드는 것이라면,`flagsNotEqual`` 함수를 만드십시오.
Jeroen3

답변:


18

0이 아닌 숫자를 1 (true)로 변환하려면 오래된 트릭이 있습니다. !(not) 연산자를 두 번 적용하십시오 .

if (!!(flags & AMAZING_FLAG) != doSet){

4
또는 (bool)(flags & AMAZING_FLAG) != doSet-더 직접적입니다. ( 이것은 Microsoft가 이에 문제가 있음을 시사합니다.) 또한 ((flags & AMAZING_FLAG) != 0)아마도 그것이 정확히 컴파일되고 절대적으로 명시적인 것일 것입니다.
크리스 홀

"또한 ((flags & AMAZING_FLAG)! = 0)은 아마도 정확히 컴파일 된 것이며 절대적으로 명시 적입니다". 그럴까요? doSet이 true이면 어떻게됩니까?
Cheiron

@ChrisHall (bool) 2는 1이됩니까, 아니면 여전히 2입니까?
user253751

3
C11 표준, 6.3.1.2 부울 유형 : "스칼라 값이 _Bool로 변환되면 값이 0과 비교되면 결과는 0이고, 그렇지 않으면 결과는 1입니다." 6.5.4 캐스트 연산자 : "괄호로 묶은 형식 이름으로 식을 선행하면 식 값이 명명 된 형식으로 변환됩니다."
크리스 홀

@Cheiron, 더 명확하게 : ((bool)(flags & AMAZING_FLAG) != doSet)와 같은 효과 (((flags & AMAZING_FLAG) != 0) != doSet)가 있으며 둘 다 정확히 같은 것으로 컴파일됩니다. 제안 된 내용 (!!(flags & AMAZING_FLAG) != doSet)은 동일하며 동일한 내용으로 컴파일 할 것입니다. 그것은 전적으로 당신이 명확하다고 생각 하나 취향의 문제 다음 (bool)캐스트가 _Bool 작업에 얼마나 캐스트와 전환을 기억하도록 요구; !!다른 정신 체조 가 필요하거나 "트릭"을 알아야합니다.
크리스 홀

2

당신은 C의 값에 해당하는 부울 문에 비트 마스크를 변환해야 0하거나 1.

  • (flags & AMAZING_FLAG) != 0. 가장 일반적인 방법입니다.

  • !!(flags & AMAZING_FLAG). 다소 일반적이지만 사용하기는 쉽지만 약간 암호입니다.

  • (bool)(flags & AMAZING_FLAG). C99 이상의 최신 C 웨이.

다음 부울 사용과 비교, 위의 대안 중 하나를 가지고 !===.


1

논리적 관점에서 보면 flags & AMAZING_FLAG다른 모든 플래그를 마스킹하는 비트 작업 만 있습니다. 결과는 숫자 값입니다.

부울 값을 받으려면 비교를 사용하십시오.

(flags & AMAZING_FLAG) == AMAZING_FLAG

이제이 논리 값을와 비교할 수 있습니다 doSet.

if (((flags & AMAZING_FLAG) == AMAZING_FLAG) != doSet)

C에서는 숫자의 부울 값으로의 암시 적 변환 규칙으로 인해 약어가있을 수 있습니다. 그래서 당신은 또한 쓸 수 있습니다

if (!(flags & AMAZING_FLAG) == doSet)

더 간결하게 쓰려고 그러나 이전 버전은 가독성 측면에서 더 좋습니다.


1

doSet값을 기준으로 마스크를 만들 수 있습니다 .

#define AMAZING_FLAG_IDX 1
#define AMAZING_FLAG (1u << AMAZING_FLAG_IDX)
...

uint16_t set_mask = doSet << AMAZING_FLAG_IDX;

이제 수표는 다음과 같습니다.

setAmazingFlag(bool doSet)
{
    const uint16_t set_mask = doSet << AMAZING_FLAG_IDX;

    if (flags & set_mask) {
        // Really expensive thing
        // Update flags
    }
}

일부 아키텍처에서는 !!분기로 컴파일 될 수 있으며이 경우 두 개의 분기가있을 수 있습니다.

  1. 에 의한 정규화 !!(expr)
  2. 와 비교 doSet

내 제안의 장점은 보장 된 단일 지점입니다.

참고 : 30을 초과하여 왼쪽으로 시프트하여 정의되지 않은 동작을 도입하지 않도록하십시오 (정수는 32 비트라고 가정). 이것은 쉽게 달성 할 수 있습니다static_assert(AMAZING_FLAG_IDX < sizeof(int)*CHAR_BIT-1, "Invalid AMAZING_FLAG_IDX");


1
모든 방식의 부울 표현식은 분기를 일으킬 가능성이 있습니다. 이상한 수동 최적화 트릭을 적용하기 전에 먼저 분해합니다.
룬딘

1
@Lundin, 확실히, 내가 "일부 아키텍처에"라고 쓴 이유는 ...
Alex Lop.

1
컴파일러 자체의 문제는 대부분 ISA 자체가 아닙니다.
룬딘

1
@Lundin 동의하지 않습니다. 일부 아키텍처에는 조건부 비트 설정 / 재설정을위한 ISA가 있으며,이 경우 브 래치가 필요하지 않으며, 다른 아키텍처는 그렇지 않습니다. godbolt.org/z/4FDzvw
Alex Lop.

참고 :이 작업을 수행 할 경우, 정의하십시오 AMAZING_FLAG의 관점에서 AMAZING_FLAG_IDX(예를 들어 #define AMAZING_FLAG ((uint16_t)(1 << AMAZING_FLAG_IDX))당신이 두 장소 같은 일을 업데이트 할 수 있다는 것을 (의 말에 정의 된 동일한 데이터를 필요가 없습니다), 0x40x8) 다른 동안 ( IDX2) 변경되지 않습니다 .
ShadowRanger

-1

이 테스트를 수행하는 방법에는 여러 가지가 있습니다.

삼항 연산자는 값 비싼 점프를 생성 할 수 있습니다.

if ((flags & AMAZING_FLAG) != (doSet ? AMAZING_FLAG : 0))

부울 변환을 사용할 수도 있습니다.

if (!!(flags & AMAZING_FLAG) != doSet)

또는 동등한 대안 :

if (((flags & AMAZING_FLAG) != 0) != doSet)

곱셈이 저렴한 경우 다음을 사용하여 점프를 피할 수 있습니다.

if ((flags & AMAZING_FLAG) != doSet * AMAZING_FLAG)

경우 flags서명하고 컴파일러는 매우 똑똑 아래 부문은 간단한 변화로 컴파일 할 수 있습니다

if ((flags & AMAZING_FLAG) / AMAZING_FLAG != doSet)

아키텍처가 2의 보수 산술을 사용하는 경우 다른 대안이 있습니다.

if ((flags & AMAZING_FLAG) != (-doSet & AMAZING_FLAG))

또는 flags비트 필드가있는 구조로 정의 하고 훨씬 더 읽기 쉬운 구문을 사용할 수 있습니다.

if (flags.amazing_flag != doSet)

아아, 비트 필드의 사양은 비트 레벨 구현에 대한 정밀한 제어를 허용하지 않기 때문에이 접근법은 일반적으로 눈살을 찌푸립니다.


모든 코드의 주요 목표는 가독성이어야하며 대부분의 예제는 그렇지 않습니다. 실제로 컴파일러에 예제를로드 할 때 더 큰 문제가 발생합니다. 처음 두 예제 구현이 가장 좋은 성능을 갖습니다. 최소 점프 및로드 (ARM 8.2, -Os) (동일 함). 다른 모든 구현은 성능이 저하됩니다. 일반적으로 컴파일러가 진행중인 작업을 파악하기가 어려워 성능이 저하되기 때문에 멋진 작업 (미세 최적화)을 피해야합니다. 따라서 -1입니다.
Cheiron
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.