나는 항상 (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
내가 완전히 빠진 것 같습니다. 어떤 아이디어?
답변
이건 같은게 아니야. 를 시도 num = -79
하면 두 작업에서 다른 결과를 얻을 수 있습니다. (-79) % 256 = -79
, (-79) & 0xff
양수입니다.
를 사용 unsigned int
하면 작업이 동일하며 코드가 동일 할 것입니다.
PS- 누군가가 논평
그것들은 동일해서는 안되며로
a % b
정의됩니다a - b * floor (a / b)
.
그것이 C, C ++, Objective-C (즉, 질문의 코드가 컴파일되는 모든 언어)에서 정의되는 방식이 아닙니다.
답변
짧은 답변
-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에서 찾아 볼 수 있듯이 프로그래밍 언어에 따라 매우 다르게 처리 됩니다.
답변
C ++ 11부터는 음수 num % 256
이면 양수가 아니 num
어야합니다.
따라서 비트 패턴은 시스템에서 부호있는 유형의 구현에 따라 다릅니다. 음의 첫 번째 인수의 경우 결과는 최하위 8 비트의 추출이 아닙니다.
num
귀하의 경우 에는 다른 문제가 될 것입니다 unsigned
: 요즘 컴파일러가 인용하는 최적화를 거의 기대할 것 입니다.
답변
컴파일러의 추론에 대한 텔레파시 통찰력은 없지만 %
음수 값 (및 반올림을 0으로 반올림)을 처리해야 &
하지만 결과는 항상 8 비트보다 낮습니다.
sar
부호 비트의 값으로 물러 비트를 채우고, “산술 오른쪽으로 이동”과 같은 나에게 명령 소리.
답변
수학적으로 말하면 모듈로는 다음과 같이 정의됩니다.
a % b = a-b * 바닥 (a / b)
바로 여기에 당신을 위해 그것을 정리해야합니다. 정수 나누기는 floor (a / b)와 동일하므로 정수의 바닥을 제거 할 수 있습니다. 그러나 컴파일러가 제안한대로 일반적인 트릭을 사용하려면 모든 a와 b에 대해 작동해야합니다. 불행히도, 이것은 사실이 아닙니다. 수학적으로 말하면, 당신의 트릭은 부호없는 정수에 대해 100 % 정확합니다 (답은 부호있는 정수가 깨져 있지만 -a % b가 양수이어야한다는 것을 확인하거나 거부 할 수 있습니다). 그러나 모든 b에 대해이 트릭을 수행 할 수 있습니까? 아마 아닙니다. 이것이 컴파일러가하지 않는 이유입니다. 결국, 모듈로가 하나의 비트 연산으로 쉽게 쓰여졌다면, 우리는 추가와 다른 연산과 같은 모듈로 회로를 추가 할 것입니다.