문제

다음 C 코드가 있습니다.

#define PRR_SCALE 255
...
uint8_t a = 3;
uint8_t b = 4;
uint8_t prr;
prr = (PRR_SCALE * a) / b;
printf("prr: %u\n", prr);

이것을 컴파일하면 (MSP430 플랫폼 컴파일러를 사용하여, 콘티키) 결과는 191을 예상하는 동안 0입니다.

변경하면 :

uint8_t a = 3;
uint8_t b = 4;
uint8_t c = 255;
uint8_t prr;
prr = (c * a) / b;
printf("prr: %u\n", prr);

올바르게 작동하고 191을 인쇄합니다.

우분투 박스에서 GCC를 사용 하여이 '정상적으로'의 간단한 버전을 컴파일하면 두 경우 모두 올바른 값을 인쇄합니다.

이것이 왜 그런지 정확히 잘 모르겠습니다. 정의 된 값을 변수에 미리 할당하여 우회 할 수는 있지만 오히려 그렇게하지는 않습니다.

이것이 왜 그런지 아는 사람이 있습니까? 아마도 이것에 대한 더 많은 정보에 대한 링크가 있습니까?

도움이 되었습니까?

해결책

짧은 답변 : 컴파일러는 버그가 있습니다. (다른 사람들이 제안한 것처럼 오버플로에는 문제가 없습니다.)

두 경우 모두 산술이 완료됩니다 int, 이는 길이가 16 비트 이상인 것으로 보장됩니다. 이전 스 니펫에서는 그 때문입니다 255 이다 int, 후자는 때문입니다 통합 홍보.

언급했듯이 GCC는 이것을 올바르게 처리합니다.

다른 팁

255는 정수 문자 그대로 처리되고 있으며 서명되지 않은 숯 기반이 아닌 전체 표현식이 int 기반으로 만듭니다. 두 번째 케이스는 유형을 정확하게 강요합니다. 다음과 같이 #define을 변경해보십시오.

 #define PRR_SCALE ((uint8_t) 255)

해당 컴파일러가 MSPGCC 인 경우 바이너리/HEX 파일과 함께 컴파일 된 프로그램의 어셈블러 목록을 배치해야합니다. 다른 컴파일러에는 추가 컴파일러 플래그가 필요할 수 있습니다. 또는 바이너리에서 별도의 분리기가 실행될 수도 있습니다.

이곳은 설명을 찾는 곳입니다. 컴파일러 최적화로 인해 프로세서에 제시된 실제 코드는 원래 C 코드와 크게 유사하지 않을 수 있지만 일반적으로 동일한 작업을 수행합니다).

결함이있는 코드를 나타내는 몇 가지 어셈블러 지침을 살펴보면 문제의 원인이 나타납니다.

내 생각에 컴파일러는 어떻게 든 전체 계산 사이스를 최적화하는 것이 정의 된 상수가 컴파일 시간에 알려진 부분입니다. 255*x는 x << 8-x (더 빠르고 작음)로 최적화 될 수 있습니다. 아마도 최적화 된 어셈블러 코드에서 무언가 잘못 될 수 있습니다.

내 시스템에서 두 버전을 컴파일하는 데 시간이 걸렸습니다. 활성 최적화를 통해 MSPGCC는 다음 코드를 생성합니다.

#define PRR_SCALE 255
uint8_t a = 3;
uint8_t b = 4;
uint8_t prr;
prr = (PRR_SCALE * a) / b;
    40ce:   3c 40 fd ff     mov #-3,    r12 ;#0xfffd
    40d2:   2a 42           mov #4, r10 ;r2 As==10
    40d4:   b0 12 fa 6f     call    __divmodhi4 ;#0x6ffa
    40d8:   0f 4c           mov r12,    r15 ;
printf("prr: %u\n", prr);
    40da:   7f f3           and.b   #-1,    r15 ;r3 As==11
    40dc:   0f 12           push    r15     ;
    40de:   30 12 c0 40     push    #16576      ;#0x40c0
    40e2:   b0 12 9c 67     call    printf      ;#0x679c
    40e6:   21 52           add #4, r1  ;r2 As==10

볼 수 있듯이 컴파일러는 255*3 ~ -3 (0xFFFD)의 결과를 직접 계산합니다. 그리고 여기에 문제가 있습니다. 어떻게 든 255는 255 개의 서명되지 않은 16 비트 대신 -1 서명 8 비트로 해석됩니다. 또는 먼저 8 비트로 구문 분석 한 다음 16 비트로 사인 확장됩니다. 또는 무엇이든.

이 주제에 대한 토론은 이미 MSPGCC 메일 링리스트에서 시작되었습니다.

왜 정의가 작동하지 않는지 잘 모르겠지만, 당신은 uint8_t 변수. 255는 최대 값입니다 uint8_t (2^8 - 1), 당신이 그것을 3만큼 곱하면, 당신은 약간의 미묘한 롤오버 문제에 빠질 것입니다.

컴파일러는 코드를 최적화하고 수학 표현식의 결과를 사전 계산하고 PRR에서 결과를 전달할 수 있습니다 (중간 값이 맞지 않더라도 적합하기 때문에).

이렇게 표현을 해치면 어떻게되는지 확인하십시오 (이것은 원하는 것과 같이 행동하지 않습니다) :

prr = c * a; // rollover!
prr = prr / b;

더 큰 데이터 유형 만 사용하면됩니다.

Case-1에서 생각할 수있는 한 가지 차이는

PRR_SCALE 리터럴 값은 ROM 또는 코드 영역으로 이동할 수 있습니다. 그리고 Mul opecode에는 몇 가지 차이가있을 수 있습니다.

case-1: [register], [rom]
case -2: [register], [register]

전혀 말이되지 않을 수 있습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top