나는 그것을 취하는 함수를 정의하고 싶다. unsigned int
인수로 인수에 int
합동 모듈로 UINT_MAX + 1을 반환하는 .
첫 번째 시도는 다음과 같습니다.
int unsigned_to_signed(unsigned n)
{
return static_cast<int>(n);
}
그러나 모든 언어 변호사가 알고 있듯이 INT_MAX보다 큰 값에 대해 unsigned에서 signed로 캐스팅하는 것은 구현에 따라 정의됩니다.
나는 이것을 구현하여 (a) 사양에서 요구하는 동작에만 의존합니다. (b) 최신 기계 및 최적화 컴파일러에서 no-op으로 컴파일됩니다.
기괴한 기계에 관해서는 ... unsigned int에 대해 unsigned int 합동 모듈로 UINT_MAX + 1이 없다면 예외를 던지고 싶다고합시다. 둘 이상이있는 경우 (가능한지 모르겠 음) 가장 큰 것을 원한다고 가정 해 봅시다.
좋아요, 두 번째 시도 :
int unsigned_to_signed(unsigned n)
{
int int_n = static_cast<int>(n);
if (n == static_cast<unsigned>(int_n))
return int_n;
// else do something long and complicated
}
내 겸손한 의견으로는 가능성이 낮기 때문에 일반적인 2 점 보완 시스템을 사용하지 않을 때 효율성에 대해별로 신경 쓰지 않습니다. 그리고 내 코드가 2050 년에 편재하는 부호 크기 시스템에서 병목 현상이 발생하면 누군가가이를 알아 내고 최적화 할 수있을 것입니다.
자,이 두 번째 시도는 내가 원하는 것에 매우 가깝습니다. 에 캐스팅 있지만 int
구현 정의 어떤 입력에 대해,에 캐스팅 돌아unsigned
표준에 의해 은 모듈로 UINT_MAX + 1 값을 유지하도록 보장됩니다. 따라서 조건문은 내가 원하는 것을 정확히 확인하고 내가 만날 가능성이있는 시스템에서 아무것도 컴파일하지 않습니다.
그러나 ... int
구현 정의 동작을 호출할지 여부를 먼저 확인하지 않고 여전히 캐스팅 중 입니다. 2050 년의 일부 가상 시스템에서는 누가 무엇을 알고 있는지 할 수 있습니다. 그래서 그것을 피하고 싶다고 가정 해 봅시다.
질문 : "세 번째 시도"는 어떤 모습이어야합니까?
요약하면 다음과 같습니다.
- unsigned int에서 signed int로 캐스트
- mod UINT_MAX + 1 값 유지
- 표준 필수 동작 만 호출
- 최적화 컴파일러를 사용하여 일반적인 2- 보완 머신에서 no-op으로 컴파일
[최신 정보]
이것이 사소한 질문이 아닌 이유를 보여주는 예를 들어 보겠습니다.
다음 속성을 사용하여 가상의 C ++ 구현을 고려하십시오.
sizeof(int)
4와 같음sizeof(unsigned)
4와 같음INT_MAX
32767과 같음INT_MIN
-2 32 + 32768과 같습니다.UINT_MAX
2와 동일 (32) - (1)- 산술
int
은 모듈로 2 32입니다 (부터 범위INT_MIN
까지INT_MAX
). std::numeric_limits<int>::is_modulo
사실이다- unsigned
n
를 int로 캐스팅 하면 0 <= n <= 32767의 값이 유지되고 그렇지 않으면 0이 생성 됩니다.
이 가상 구현에서는 int
각 unsigned
값에 합동하는 정확히 하나의 값 (mod UINT_MAX + 1)이 있습니다 . 그래서 내 질문은 잘 정의되어 있습니다.
이 가상의 C ++ 구현은 C ++ 98, C ++ 03 및 C ++ 11 사양을 완전히 준수한다고 주장합니다. 나는 그들 모두의 모든 단어를 암기하지 않았다는 것을 인정한다. 그러나 나는 관련 섹션을주의 깊게 읽었다 고 믿는다. 그래서 제가 당신의 대답을 받아들이 길 원한다면 (a)이 가상의 구현을 배제하는 사양을 인용하거나 (b) 올바르게 처리해야합니다.
실제로 정답은 표준에서 허용하는 모든 가상 구현을 처리해야합니다 . 이것이 정의상 "표준 필수 동작 만 호출"이라는 의미입니다.
덧붙여서, std::numeric_limits<int>::is_modulo
여기에서는 여러 가지 이유로 완전히 쓸모가 없습니다. 우선, true
서명되지 않은 캐스트가 큰 서명되지 않은 값에 대해 작동하지 않는 경우에도 가능합니다. 다른 true
경우에는 산술이 전체 정수 범위를 단순히 모듈로하는 경우 보완 또는 부호 크기 시스템에서도 가능합니다. 등등. 귀하의 답변이에 따라 다르다면 is_modulo
잘못된 것입니다.
[업데이트 2]
hvd의 대답 은 나에게 뭔가를 가르쳐주었습니다. 정수에 대한 나의 가상 C ++ 구현은 현대 C에서 허용 되지 않습니다 . C99 및 C11 표준은 부호있는 정수의 표현에 대해 매우 구체적입니다. 실제로, 그들은 2 개 보수, 1 개 보수 및 부호 크기 만 허용합니다 (섹션 6.2.6.2 단락 (2);).
그러나 C ++는 C가 아닙니다. 결과적으로이 사실은 제 질문의 핵심입니다.
원래 C ++ 98 표준은 훨씬 더 오래된 C89를 기반으로합니다 (섹션 3.1.2.5).
각 부호있는 정수 유형에 대해 동일한 양의 스토리지 (부호 정보 포함)를 사용하고 동일한 정렬 요구 사항을 갖는 해당 (그러나 다른) 부호없는 정수 유형 (부호없는 키워드로 지정됨)이 있습니다. 부호있는 정수 유형의 음이 아닌 값의 범위는 해당하는 부호없는 정수 유형의 하위 범위이며 각 유형에서 동일한 값의 표현은 동일합니다.
C89는 하나의 부호 비트 만 가지고 있거나 두 개의 보수 / 일-보완 / 부호 크기 만 허용하는 것에 대해 아무 말도하지 않습니다.
C ++ 98 표준은이 언어를 거의 그대로 채택했습니다 (섹션 3.9.1 단락 (3)) :
각각의 부호있는 정수 유형에 대해 해당하는 (그러나 다른) 부호없는 정수 유형 인 "
unsigned char
", "unsigned short int
", "unsigned int
"및 "unsigned long int
"이 있으며, 각 유형은 동일한 양의 스토리지를 차지하고 동일한 정렬 요구 사항을 갖습니다 (3.9 ) 해당 부호있는 정수 유형으로; 즉, 각 부호있는 정수 유형은 해당하는 부호없는 정수 유형 과 동일한 객체 표현을 갖습니다 . 부호있는 정수형의 음이 아닌 값의 범위는 해당하는 부호없는 정수형의 하위 범위이며, 각 해당 부호 / 부호없는 형식의 값 표현은 동일해야합니다.
C ++ 03 표준은 C ++ 11과 동일한 언어를 사용합니다.
내가 말할 수있는 한 표준 C ++ 사양은 부호있는 정수 표현을 C 사양으로 제한하지 않습니다. 그리고 싱글 사인 비트나 그 어떤 종류의 것을 요구하는 것은 없습니다. 음 이 아닌 부호있는 정수는 해당하는 부호없는 범위의 하위 범위 여야한다는 것입니다.
그래서 다시 INT_MIN = -2 32 +32768의 INT_MAX = 32767 이 허용된다고 주장합니다 . 귀하의 답변이 그렇지 않다고 가정하면 C ++ 표준 을 인용하지 않는 한 잘못된 것입니다.