[c] 부호없는 정수 빼기가 정의 된 동작입니까?

결과가 음수 일 때 동일한 유형의 다른 정수에서 부호없는 정수를 빼는 데 문제가 있다고 생각하는 사람의 코드를 보았습니다. 따라서 이와 같은 코드는 대부분의 아키텍처에서 작동하더라도 올바르지 않습니다.

unsigned int To, Tf;

To = getcounter();
while (1) {
    Tf = getcounter();
    if ((Tf-To) >= TIME_LIMIT) {
        break;
    }
}

이것은 내가 찾을 수있는 C 표준에서 모호하게 관련된 유일한 인용문입니다.

부호없는 피연산자를 포함하는 계산은 결과로 나타나는 부호없는 정수 형식으로 나타낼 수없는 결과가 결과 형식으로 나타낼 수있는 가장 큰 값보다 하나 더 큰 숫자로 축소되기 때문에 절대로 과잉 흐름이 발생하지 않습니다.

나는 오른쪽 피연산자가 더 클 때 연산이 모듈로 잘린 숫자의 맥락에서 의미가 있도록 조정된다는 것을 의미하는 따옴표를 취할 수 있다고 가정합니다.

0x0000-0x0001 == 0x 1 0000-0x0001 == 0xFFFF

구현 종속 서명 의미를 사용하는 것과 반대 :

0x0000-0x0001 == (부호 없음) (0 + -1) == (0xFFFF뿐만 아니라 0xFFFE 또는 0x8001)

어떤 해석이 맞습니까? 그것은 전혀 정의되어 있습니까?



답변

부호없는 유형에서 음수를 생성하는 빼기 결과는 잘 정의되어 있습니다.

  1. […] 부호없는 피연산자를 포함하는 계산은 결과 형식으로 나타낼 수있는 가장 큰 값보다 하나 더 큰 수만큼 부호없는 정수 형식으로 표시 할 수없는 결과가 축소되므로 절대 오버플로 할 수 없습니다. (ISO / IEC 9899 : 1999 (E) §6.2.5 / 9)

보시다시피, (unsigned)0 - (unsigned)1모듈로 UINT_MAX + 1, 즉 UINT_MAX는 -1 과 같습니다.

“부호없는 피연산자를 포함하는 계산은 절대 오버플로 할 수 없습니다.”라고 표시되어있어 상한을 초과하는 경우에만 적용된다고 믿게 만들 수 있지만 이는 문장의 실제 바인딩 부분에 대한 동기 로 제시됩니다 . “a 결과로 나타나는 부호없는 정수 유형으로 표현할 수없는 결과는 결과 유형으로 표현할 수있는 가장 큰 값보다 하나 더 큰 수의 모듈로 축소됩니다. ” 이 문구는 유형의 상한의 오버플로에 제한되지 않으며 표현하기에는 너무 낮은 값에도 동일하게 적용됩니다.


답변

부호없는 유형으로 작업 할 때 모듈 식 산술 ( “둘러싸 기” 동작 이라고도 함 )이 발생합니다. 이 모듈 식 산술 을 이해하려면 다음 시계를 살펴보십시오.

여기에 이미지 설명 입력

9 + 4 = 1 ( 13 mod 12 )이므로 다른 방향으로는 1-4 = 9 ( -3 mod 12 )입니다. 서명되지 않은 유형으로 작업하는 동안에도 동일한 원칙이 적용됩니다. 경우] 결과 유형unsigned후 모듈러 산술 일어난다.


이제 결과를 저장하는 다음 작업을 살펴보십시오. unsigned int .

unsigned int five = 5, seven = 7;
unsigned int a = five - seven;      // a = (-2 % 2^32) = 4294967294 

int one = 1, six = 6;
unsigned int b = one - six;         // b = (-5 % 2^32) = 4294967291

결과가인지 확인하려면 변수에 signed저장 signed하거나 signed. 숫자의 차이를 확인하고 모듈 식 산술이 적용되지 않도록하려면 다음에 abs()정의 된 함수 사용을 고려해야 합니다.stdlib.h .

int c = five - seven;       // c = -2
int d = abs(five - seven);  // d =  2

특히 조건을 작성할 때 다음과 같은 이유로 매우주의하십시오.

if (abs(five - seven) < seven)  // = if (2 < 7)
    // ...

if (five - seven < -1)          // = if (-2 < -1)
    // ...

if (one - six < 1)              // = if (-5 < 1)
    // ...

if ((int)(five - seven) < 1)    // = if (-2 < 1)
    // ...

그러나

if (five - seven < 1)   // = if ((unsigned int)-2 < 1) = if (4294967294 < 1)
    // ...

if (one - six < five)   // = if ((unsigned int)-5 < 5) = if (4294967291 < 5)
    // ...


답변

음, 첫 번째 해석이 맞습니다. 그러나이 문맥에서 “서명 된 의미론”에 대한 귀하의 추론은 잘못되었습니다.

다시 말하지만, 첫 번째 해석이 맞습니다. 부호없는 산술은 모듈로 산술의 규칙을 따릅니다. 즉, 부호없는 32 비트 유형 0x0000 - 0x00010xFFFF대해 평가됩니다 .

그러나 동일한 결과를 생성하려면 두 번째 해석 ( “서명 된 의미”에 기반한 해석)도 필요합니다. 즉 0 - 1, 서명 된 유형의 도메인에서 평가 -1하고 중간 결과로 얻는 경우에도 나중에 서명되지 않은 유형으로 변환 될 때 -1생성해야 0xFFFF합니다. 일부 플랫폼에서 부호있는 정수 (1의 보수, 부호있는 크기)에 대해 이국적인 표현을 사용하더라도이 플랫폼은 부호있는 정수 값을 부호없는 값으로 변환 할 때 모듈로 산술 규칙을 적용해야합니다.

예를 들어,이 평가

signed int a = 0, b = 1;
unsigned int c = a - b;

플랫폼이 부호있는 정수에 대해 이국적인 표현을 사용하는 경우에도 UINT_MAX에서 생성되도록 보장됩니다 c.


답변

유형의 부호 숫자 unsigned int이상, 형식 변환의 부재에 a-b첨가 할 때, 부호 번호 수득 같이 정의된다 b,을 산출 할 것이다 a. 음수를 부호없는 숫자로 변환하는 것은 부호가 반전 된 원래 숫자에 더 해지면 0이되는 숫자를 산출하는 것으로 정의됩니다 (따라서 -5를 부호없는 숫자로 변환하면 5에 더 해지면 0이되는 값이 산출됩니다). .

보다 작은 부호없는 숫자 는 빼기 전에 unsigned int입력하도록 승격 될 수 int있으며의 동작은 a-b의 크기에 따라 달라집니다 int.


답변