programing

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

firstcheck 2022. 8. 1. 21:25
반응형

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

같은 타입의 다른 정수에서 부호 없는 정수를 빼는 데 문제가 있다고 생각되는 사람으로부터 코드가 발견되었는데, 결과는 음수입니다.따라서 이러한 코드는 대부분의 아키텍처에서 작동하더라도 정확하지 않습니다.

unsigned int To, Tf;

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

C 기준에서 모호하게 관련된 인용구는 이것밖에 없습니다.

부호 없는 피연산자를 포함한 계산은 절대 오버할 수 없습니다.이는 결과 부호 없는 정수형으로 나타낼 수 없는 결과가 결과 유형으로 나타낼 수 있는 최대값보다 1 큰 수치로 감소하기 때문입니다.

이 인용문은 오른쪽 피연산자가 더 클 때 연산이 모듈로 잘린 숫자의 맥락에서 의미 있게 조정된다는 의미로 해석할 수 있을 것 같습니다.

예.

0x0000 - 0x0001 == 0x1 0000 - 0x0001 == 0xFFFF

구현에 의존한 서명된 의미론 사용과는 달리 다음과 같습니다.

0x0000 - 0x0001 == (부호 없음) (0 + - 1) == (0xFFFFF, 0xFFE 또는 0x8001)

어떤 해석이 옳은가?정의되어 있나요?

부호 없는 타입으로 작업할 때는 모듈러 연산(「감기」동작이라고도 불립니다)이 실행됩니다.이 모듈식 계산을 이해하려면 다음 클럭을 참조하십시오.

여기에 이미지 설명 입력

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숫자의 차이를 구하고 모듈식 산술이 적용되지 않도록 하려면 에서 정의한 함수를 사용하는 것을 고려해야 합니다.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)
    // ...

부호 없는 유형의 음수를 생성하는 감산 결과는 명확하게 정의됩니다.

  1. [...] 부호 없는 피연산자를 포함한 계산은 결코 오버플로우 할 수 없다. 왜냐하면 부호 없는 정수형으로는 나타낼 수 없는 결과는 결과형으로는 나타낼 수 있는 가장 큰 값보다 1 큰 수치로 감소하기 때문이다.(ISO/IEC 9899:1999 (E) § 6.2.5/9)

바와 같이, '우리'는 '우리'입니다.(unsigned)0 - (unsigned)1또는1 modulo UINT_MAX+1, "UINT_MAX"와 .

"부호되지 않은 피연산자를 포함하는 계산은 절대 오버플로할 수 없습니다"라고 되어 있는데, 이는 상한 초과에만 적용되는 것으로 생각될 수 있지만, 이는 문장의 실제 바인딩 부분에 대한 동기 부여로 제시됩니다: "결과적인 부호되지 않은 정수 유형으로 나타낼 수 없는 결과는 감소된 모듈입니다."o 결과 유형으로 나타낼 수 있는 최대값보다 1 큰 숫자입니다."이 문구는 유형의 상한 오버플로로 제한되지 않으며 너무 낮아 나타낼 수 없는 값에도 동일하게 적용됩니다.

부호 없는 정수 빼기는 동작을 정의해 왔습니다.또한 어려운 일입니다.부호 없는 두 정수를 빼면 결과(lvalue) 유형이 명시적으로 지정되지 않은 경우 결과가 더 높은 유형 int로 승격됩니다.예를 들어 후자의 경우 int8_t result = a - b; (a 및 b의 int8_t 타입) 매우 이상한 동작을 얻을 수 있습니다., 트랜시비티 속성을 잃을 수 있습니다(즉, a > b 및 b > c의 경우 a > c의 경우).이동성이 상실되면 트리형 데이터 구조 작업이 파괴될 수 있습니다.어떤 키가 더 높거나 더 낮은지 추론하기 위해 부호 없는 정수 감산을 사용하는 정렬, 검색, 트리 구축을 위한 비교 기능을 제공하지 않도록 주의해야 합니다.

아래의 예를 참조해 주세요.

#include <stdint.h>
#include <stdio.h>

void main()
{
    uint8_t a = 255;
    uint8_t b = 100;
    uint8_t c = 150;

    printf("uint8_t a = %+d, b = %+d, c = %+d\n\n", a, b, c);

    printf("          b - a  = %+d\tpromotion to int type\n"
           " (int8_t)(b - a) = %+d\n\n"
           "          b + a  = %+d\tpromotion to int type\n"
           "(uint8_t)(b + a) = %+d\tmodular arithmetic\n"
           "     b + a %% %d = %+d\n\n", 
           b - a,  (int8_t)(b - a), 
           b + a, (uint8_t)(b + a),
           UINT8_MAX + 1,
           (b + a) % (UINT8_MAX + 1));

    printf("c %s b (b - c = %d), b %s a (b - a = %d), AND c %s a (c - a = %d)\n",
           (int8_t)(c - b) < 0 ? "<" : ">", (int8_t)(c - b),
           (int8_t)(b - a) < 0 ? "<" : ">", (int8_t)(b - a),
           (int8_t)(c - a) < 0 ? "<" : ">", (int8_t)(c - a));
}
$ ./a.out 
uint8_t a = +255, b = +100, c = +150

          b - a  = -155 promotion to int type
 (int8_t)(b - a) = +101

          b + a  = +355 promotion to int type
(uint8_t)(b + a) = +99  modular arithmetic
     b + a % 256 = +99

c > b (b - c = 50), b > a (b - a = 101), AND c < a (c - a = -105)

부호가 타입의 」unsigned int유형 변환이 없는 경우, 또는 그 이상입니다.a-b는 부호 되어 있습니다.이 숫자는, 「부호 없는 번호」에 됩니다.b,가 산출됩니다.a음수를 부호 없이 변환하면 부호 반전된 원래 숫자에 더하면 0이 된다(따라서 -5를 부호 없음으로 변환하면 5에 더하면 0이 된다).

는 보다 주의하세요.unsigned int으로 될 수 .inta-bint.

음, 첫 번째 해석은 맞습니다.그러나 이 맥락에서 "서명된 의미론"에 대한 당신의 추론은 틀렸습니다.

다시 한 번, 당신의 첫 번째 해석은 옳습니다.부호 없는 산수는 모듈로 산술의 규칙을 따르는데, 이것은 다음을 의미한다.0x0000 - 0x0001까지 평가하다.0xFFFF32비트 부호 없는 타입의 경우.

그러나 두 번째 해석('서명된 의미론'에 기초한 해석)도 동일한 결과를 얻기 위해 필요합니다.예를 들어, 당신이 평가한다고 해도0 - 1서명된 활자의 영역에서, 그리고 얻습니다.-1중간 결과로서 이것은-1여전히 생산해야 합니다.0xFFFF나중에 부호 없는 유형으로 변환됩니다.일부 플랫폼이 부호 있는 정수(1의 보완, 부호 있는 크기)에 대해 이국적인 표현을 사용하더라도 이 플랫폼은 부호 없는 정수 값을 변환할 때 모듈로 산술 규칙을 적용해야 합니다.

예를 들어 이 평가는

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

생산은 여전히 보장된다.UINT_MAXc플랫폼이 부호 있는 정수에 대해 이국적인 표현을 사용하고 있는 경우에도 마찬가지입니다.

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

std::abs는 부호 없는 정수의 경우 "signed"가 아닙니다.깁스가 필요하긴 한데.

언급URL : https://stackoverflow.com/questions/7221409/is-unsigned-integer-subtraction-defined-behavior

반응형