(% 256)이 (a & 0xFF)와 다른 이유는 무엇입니까?


145

나는 항상 (a % 256)옵티 마이저를 할 때 내가 쓴 것처럼 효율적인 비트 단위 연산을 자연스럽게 사용할 것이라고 가정했습니다 (a & 0xFF).

컴파일러 탐색기 gcc-6.2 (-O3)에서 테스트 할 때 :

// Type your code here, or load an example.
int mod(int num) {
    return num % 256;
}

mod(int):
    mov     edx, edi
    sar     edx, 31
    shr     edx, 24
    lea     eax, [rdi+rdx]
    movzx   eax, al
    sub     eax, edx
    ret

그리고 다른 코드를 시도 할 때 :

// Type your code here, or load an example.
int mod(int num) {
    return num & 0xFF;
}

mod(int):
    movzx   eax, dil
    ret

내가 완전히 빠진 것 같습니다. 어떤 아이디어?


64
0xFF는 256이 아닌 255입니다.
Rishikesh Raje

186
@RishikeshRaje : 그럼요? 둘 다 %아닙니다 &.
usr2564301

27
@RishikeshRaje : OP가이를 잘 알고 있다고 확신합니다. 그들은 다른 작업과 함께 사용됩니다.
건배와 hth. -Alf

28
경우 관심의 부족, 당신은 더 나은 결과를받을 수 있나요 num이다 unsigned?
Bathsheba

20
@RishikeshRaje 비트 단위 및 0xFF는 부호없는 정수의 경우 모듈로 2 ^ 8과 같습니다.
2501

답변:


230

이건 같은게 아니야. 를 시도 num = -79하면 두 작업에서 다른 결과를 얻을 수 있습니다. (-79) % 256 = -79, (-79) & 0xff양수입니다.

를 사용 unsigned int하면 작업이 동일하며 코드가 동일 할 것입니다.

PS- 누군가가 논평

그것들은 동일해서는 안되며로 a % b정의됩니다 a - b * floor (a / b).

그것이 C, C ++, Objective-C (즉, 질문의 코드가 컴파일되는 모든 언어)에서 정의되는 방식이 아닙니다.


의견은 긴 토론을위한 것이 아닙니다. 이 대화는 채팅 으로 이동 되었습니다 .
Martijn Pieters

52

짧은 답변

-1 % 256수득 -1되지 255이다 -1 & 0xFF. 따라서 최적화가 올바르지 않습니다.

긴 대답

C ++에는 (a/b)*b + a%b == a꽤 자연스러운 규칙이 있습니다. a/b항상 소수 부분이없는 산술 결과를 반환합니다 (0쪽으로 잘림). 결과적으로 a%b부호와 같 a거나 0입니다.

분할 -1/256수율 0따라서이 -1%256있어야 -1상기 조건을 만족시키기 위해 ( (-1%256)*256 + -1%256 == -1). 이것은에서 분명히 다릅니다 -1&0xFF이다 0xFF. 따라서 컴파일러는 원하는 방식으로 최적화 할 수 없습니다.

N4606 상태 기준 C ++ 표준 [expr.mul §4] 의 관련 섹션 :

적분 피연산자의 경우, /연산자는 일부 분수 부분이 폐기 된 대수 몫을 산출합니다. 몫이 경우 a/b결과의 타입에서 표현할 수이고, (a/b)*b + a%b동일하다 a[...].

최적화 활성화

그러나 유형을 사용unsigned 하면 위의 규칙을 충족하는 최적화가 완전히 정확합니다 .

unsigned(-1)%256 == 0xFF

참조 .

다른 언어

이것은 Wikipedia에서 찾아 볼 수 있듯이 프로그래밍 언어에 따라 매우 다르게 처리 됩니다.


50

C ++ 11부터는 음수 num % 256이면 양수가 아니 num어야합니다.

따라서 비트 패턴은 시스템에서 부호있는 유형의 구현에 따라 다릅니다. 음의 첫 번째 인수의 경우 결과는 최하위 8 비트의 추출이 아닙니다.

num귀하의 경우 에는 다른 문제가 될 것입니다 unsigned: 요즘 컴파일러가 인용하는 최적화를 거의 기대할 것 입니다.


6
거의 아니지만 꽤. 경우 num부정적이며, 다음 num % 256제로 또는 마이너스 (일명 비 양성)입니다.
Nayuki

5
어떤 IMO는 표준에서 실수입니다 : 수학적으로 모듈로 연산하면 제수 256의 부호를 사용해야합니다 (이 경우). 이해하기 위해서는 그 이유를 생각 (-250+256)%256==6하지만, (-250%256)+(256%256)해야한다, 표준, "비 양성"에 따라, 따라서 없습니다 6. 예를 들어 정수 좌표로 "축소"렌더링을 계산할 때 모든 좌표가 음이 아닌 값을 갖도록 이미지를 이동시켜야합니다.
Michael

2
@Michael Modulus는 수학 정의를 문자로 따르는 경우에도 덧셈에 대한 분배를 해 본 적이 없습니다. 예를 들어, (128+128)%256==0그러나 (128%256)+(128%256)==256. 아마도 지정된 행동에 대해 좋은 반대가 있을지 모르지만 그것이 당신이 한 말인지는 분명하지 않습니다.
Daniel Wagner

1
@DanielWagner, 당신은 맞습니다, 물론, 나는 "연관"을 잘못 생각합니다. 그러나 제수의 부호를 유지하고 모든 것을 모듈 식 산술로 계산하면 분포 특성이 유지됩니다. 귀하의 예에서는 256==0. 핵심은 N모듈로 N산술 에서 정확히 가능한 값 을 갖는 것입니다 . 이는 모든 결과가 0,...,(N-1)아닌 범위 안에있는 경우에만 가능합니다 -(N-1),...,(N-1).
Michael

6
@Michael : %는 모듈로 연산자가 아니라 나머지 연산자입니다.
Joren

11

컴파일러의 추론에 대한 텔레파시 통찰력은 없지만 %음수 값 (및 반올림을 0으로 반올림)을 처리해야 &하지만 결과는 항상 8 비트보다 낮습니다.

sar부호 비트의 값으로 물러 비트를 채우고, "산술 오른쪽으로 이동"과 같은 나에게 명령 소리.


0

수학적으로 말하면 모듈로는 다음과 같이 정의됩니다.

a % b = a-b * 바닥 (a / b)

바로 여기에 당신을 위해 그것을 정리해야합니다. 정수 나누기는 floor (a / b)와 동일하므로 정수의 바닥을 제거 할 수 있습니다. 그러나 컴파일러가 제안한대로 일반적인 트릭을 사용하려면 모든 a와 b에 대해 작동해야합니다. 불행히도, 이것은 사실이 아닙니다. 수학적으로 말하면, 당신의 트릭은 부호없는 정수에 대해 100 % 정확합니다 (답은 부호있는 정수가 깨져 있지만 -a % b가 양수이어야한다는 것을 확인하거나 거부 할 수 있습니다). 그러나 모든 b에 대해이 트릭을 수행 할 수 있습니까? 아마 아닙니다. 이것이 컴파일러가하지 않는 이유입니다. 결국, 모듈로가 하나의 비트 연산으로 쉽게 쓰여졌다면, 우리는 추가와 다른 연산과 같은 모듈로 회로를 추가 할 것입니다.


4
"바닥"을 "잘라 내기"와 혼동하고 있다고 생각합니다. 초기 컴퓨터는 절단 분할을 사용했습니다. 일이 균등하게 분할되는 경우에도 바닥 분할보다 계산하기가 더 쉽기 때문입니다. 잘린 구분이 플로어 분할보다 더 유용한 경우는 거의 없었지만 많은 언어가 잘린 구분을 사용하는 FORTRAN의 리드를 따릅니다.
supercat

@supercat 수학적으로 말하면, 바닥 잘립니다. 둘 다 같은 효과가 있습니다. 그들은 컴퓨터에서 동일하게 구현되지는 않았지만 같은 일을합니다.
user64742

5
@TheGreatDuck : 음수에 대해서는 동일하지 않습니다. 의 바닥은 -2.3이다 -3당신이자를 경우 동안, -2.3당신이 얻을 정수로 -2. en.wikipedia.org/wiki/ 잘림을 참조하십시오 . "음수 자르기는 바닥 함수와 같은 방향으로 반올림되지 않습니다." 그리고 %음수 의 동작 은 정확하게 OP가 설명 된 동작을 보는 이유입니다.
Mark Dickinson

@ MarkDickinson C ++의 모듈로가 양의 제수에 양수 값을 제공한다고 확신하지만 논쟁하지는 않을 것입니다.
user64742

1
@TheGreatDuck-예제 참조 : cpp.sh/3g7h (C ++ 98은 사용 된 두 가지 변형 중 어느 것을 사용했는지 정의하지 않았지만보다 최근의 표준에서는 그렇게하므로 C ++ 구현을 사용했을 가능성이 있습니다. 다르게 ...)을했던 과거
Periata Breatta
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.