다음과 같은 질문이 있습니다. 포함 된 어셈블리없이 C에서 회전 교대를 수행하는 방법. 더 구체적으로, 어떻게 시프트 회전 32 비트 int
.C에서 회전 교대를 수행하는 방법
이제이 문제를 long long int
유형의 도움으로 해결할 예정입니다.하지만 좀 더보기 싫어서 더 우아한 방법이 있는지 알고 싶습니다.
친절하게 제공합니다.
다음과 같은 질문이 있습니다. 포함 된 어셈블리없이 C에서 회전 교대를 수행하는 방법. 더 구체적으로, 어떻게 시프트 회전 32 비트 int
.C에서 회전 교대를 수행하는 방법
이제이 문제를 long long int
유형의 도움으로 해결할 예정입니다.하지만 좀 더보기 싫어서 더 우아한 방법이 있는지 알고 싶습니다.
친절하게 제공합니다.
(향후 독자들에게 경고) : 위키 백과의 코드는 비정상 asm (gcc에 분기 또는 cmov 포함)을 생성합니다. 효율적인 UB 프리 회전을 위해서는 Best practices for circular shift (rotate) operations in C++을 참조하십시오. Wikipedia에서
:
unsigned int _rotl(unsigned int value, int shift) {
if ((shift &= 31) == 0)
return value;
return (value << shift) | (value >> (32 - shift));
}
unsigned int _rotr(unsigned int value, int shift) {
if ((shift &= 31) == 0)
return value;
return (value >> shift) | (value << (32 - shift));
}
이 대답은 내가 Best-practices for compiler-friendly rotates에 게시 무엇의 중복입니다.
자세한 내용은 my answer on another question을 참조하십시오. 당신이 할 수 있도록, 임의의 정수 유형에 대한
uint32_t rotl32 (uint32_t x, unsigned int n)
{
const unsigned int mask = (CHAR_BIT*sizeof(x)-1);
assert ((n<=mask) &&"rotate by type width or more");
n &= mask; // avoid undef behaviour with NDEBUG. 0 overhead for most types/compilers
return (x<<n) | (x>>((-n)&mask));
}
작품뿐만 아니라 uint32_t
:
어떤 정의되지 않은 동작을 방지 C의 회전을 표현하는 가장 컴파일러 친화적 인 방법은 John Regehr의 구현 것 같다 여러 버전. 이 버전 inlines to a single rol %cl, reg
(또는 rol $imm8, reg
)은 컴파일러가 명령에 이미 마스크 작업이 내장되어 있음을 알고 있기 때문에 x86에 있습니다.
임시적으로 int
에 저장된 16 비트 값을 가지고있을 때 실수로 잘못된 너비의 회전을하고 싶지 않기 때문에 피연산자 유형에 템플릿을 지정하지 않는 것이 좋습니다. 특히 정수 승격 규칙은 부호가없는 유형이 포함 된 표현식의 결과를 int
으로 바꿀 수 있기 때문에 특히 그렇습니다.
x
에 부호없는 유형을 사용하고 반환 값이 맞는지 확인하십시오. 그렇지 않으면 회전되지 않습니다. (gcc는 0보다 오히려 부호 비트의 복사본을 이동 시키므로, 두 개의 쉬프트 된 값이 합쳐져서 OR
일 때 문제가 발생합니다.)
@chux : 회전 수의 유형을 완화 할 수 있습니다 ... 'gcc'는 'n'이 16이나 8이 아닌 32 또는 64b 유형이지만 'int'또는 'unsigned'를 사용하는 경우 x86에서 더 나은 코드를 생성합니다. 거기에서 ok 일 수 있었다. 그래도 내 어설트() 논리 오류 잡기. –
@chux : 아, 어리석은 나를. 네가 'n'과 '마스크'가 아니라 'n'과 'x'라고 생각했다. 64 비트, 32 비트, 16 비트, 8 비트 타입을 실험 한 결과'uint32_t'를'n'으로 골랐고'uint32_t n '을 사용하여 gcc가 중복 AND 명령을 내놓은 것을 발견했습니다. –
@chux : 동의 함. 'unsigned '은 이미 컴파일러/플랫폼에서 32 비트입니다. asm 출력을 보았습니다. 내 답변을 완성했습니다. 도움을 주셔서 감사합니다. –
쓰레드가 오래되었지만, 나는 두 센트를 토론하고 문제의 해결책을 제안하십시오. 그것이 가치가 있기를 바라지 만, 내가 틀렸다면 제발 저를 교정하십시오.
나는 효율적이고 안전한 회전 방법을 찾고있을 때 실제로 그 해결책이 없다는 것에 놀랐습니다. 여기 몇 가지 관련 스레드를 발견 https://blog.regehr.org/archives/1063 (안전, 효율적인, 그리고 C/C++에서 휴대용 회전) (분기하지만 안전 포함) Best practices for circular shift (rotate) operations in C++ 및 위키 피 디아 스타일 :
uint32_t wikipedia_rotl(uint32_t value, int shift) {
if ((shift &= 31) == 0)
return value;
return (value << shift) | (value >> (32 - shift));
}
묵상 조금 후 결과적으로 알리미가 분기없이 항상 < 시프트 조건에 맞는 약수보다 항상 낮기 때문에 모듈로 나누기가 기준에 맞을 것으로 나타났습니다.도면의 수학적 관점에서 :
∀ x> = 0, Y (X 개조 Y) < Y 우리의 경우 매에서
정확히이다 (X의 32 %) < 32 우리 성취하고 싶다. (그리고 그래, 내가 경험적 것을 확인하고 항상 < 32)
#include <stdint.h>
uint32_t rotl32b_i1m (uint32_t x, uint32_t shift)
{
shift %= 32;
return (x<<n) | (x>>(32-shift));
}
또한 모드()의 100 개 비트를 가정 해 봅시다의 실제 회전이 3 번 전체 32 개 비트를 회전하고 있기 때문에, 과정을하는 본질적으로 단순화 아무것도 변경하지 않고 4 비트를 변경합니다. 그래서 100 % 32 == 4를 계산하고 4 비트를 회전하는 것이 더 나은가요? 그것은 어쨌든 단일 프로세서 연산을 필요로하고 상수 값 + 하나의 명령을 더해서 인자를 스택에서 가져와야합니다. 그러나 위키피디아처럼 if()를 사용하여 분기하는 것보다 여전히 좋습니다.
그럼, 어떻게 생각하니?
스택 오버플로에 오신 것을 환영합니다! 이것은 실제로 질문에 대답하지 않습니다. 피드백을 찾고 있다면,보다 완벽한 방식으로 샘플을 작성하고 [codereview.se]에서 비평을 요청할 수 있습니다. 다른 것들이 다르게 수행되기 때문에 [Stack Overflow 사용자를위한 코드 검토 가이드] (// codereview.meta.stackexchange.com/a/5778)를 먼저 읽어보십시오! –
'shift % = 32'는 John Regehr의 'shift & = 31'와 정확히 같습니다. base-2 modulo는 상위 비트를 마스킹하여 처리 할 수 있습니다. (컴파일러는 이것을 알고 있으므로 적어도 최적화가 가능할 때는 버전을 동일하게 컴파일 할 것입니다.)'(32-shift)'에 그것을 쓰는 것을 잊었을 때를 제외하고, 여러분의 버전은'shift = 0'에 대한 C의 정의되지 않은 동작을 갖습니다 (비록 대부분의 컴파일러에서 회전시키기만하면 컴파일됩니다.) –
네, 맞습니다. shift = 32에서는 어떤 일이 일어날 지 생각했지만 shift = 0이면 분석하지 않았습니다.같은 시간에 C/C++에서 형식적으로 두 버전을 모두 사용할 수 없으며 강제로 "wikipedia"버전 (조건을 확인하고 분기를 생성 함)을 사용하는 'shift & = 31'과 동일한 정의되지 않은 동작이 발생합니다. 제가 사용 매우 안전한 코드를 생성 GCC 확인할 롤 : #의 GCC -g -O2 -S rot321m1.c \t _rotl32b_i1m : \t퍼센트 ESP \t pushl \t%의 EBP \t의 movl %의 EBP \t의 movl \t 8 (%의 EBP) %의 EAX \t \t \t의 movl 12 (%의 EBP), ECX % \t \t \t andl $ 31 % ECX \t \t 롤 \t퍼센트 카스티 % EAX \t \t leave \t \t ret // John Regehr의 shift & = 31 버전의 경우 동일합니다. – tansy
대상에'rotl' 명령이있는 경우 최종 어셈블리에서 사용하겠습니까? 그렇지 않다면 (나는 의심 스럽다), 나는 인라인 어셈블리보다 다른 방법이 없다고 생각한다. – Gauthier
위키피디아가 맞는지는 모르겠지만 회전 연산을 수행 할 때 간단히 수행하면 몇 가지 결함이있는 것으로 보입니다 (값 >> 시프트). (값 << (32- 시프트)). 주의 사항 : C의 오른쪽 시프트 연산은 부호 확장됩니다. –
오른쪽 시프트는 변수가 시프트 된 경우에만 부호 확장되어야합니다. 'value'변수가 서명 된 경우 올바른 전환을 위해 부호없는 변수로 변환해야합니다. –