uinsigned int 의 차를 구했을 때의 결과는 정해져 있지 않나요?

32bit single core mcu를 가진 ESP-32를 사용중입니다.
uint끼리 뺄셈을 할 경우가 생겨서 코딩을 진행중에 이상한 오류를 발견했습니다.
uint_16t끼리 뺄셈을 할 경우 음수가 나오면, signed 32bit로 up casting(사실 32bit인지는 모르겠습니다)되는데, uint_32bit끼리 뺄셈을 할 경우 값이 알수가 없더라구요.
printf로 찍을 경우 잘 나오긴 했습니다. 그런데 그 뺼셈의 결과가 abs 매크로에 처리되고, 나눗셈의 분모로 작용하는 식인데,

1 / abs(current - target) //이런식

이러면 결과가 0이 나오는 현상이 발생합니다.
undefined behavior 일까요?

정수형끼리 나눗셈을 해서 그런게 아닐까요? 피연산자 하나를 float형으로 바꿔보세요.

int끼리 나눗셈은 int입니다. 분모나 분자를 float으로 캐스팅해보세요.

이래서 언어스펙을 잘 공부해야하는겁니다. ㅎㅎㅎㅎ
비직관적인 부분이 있을수 잇어요

나눗셈은 알고 있었는데 unsigned int끼리의 차를 구했을 경우가 궁금해서요…

오잉 제가 틀린게 있었나요?

아뇨아뇨 작성자분이 int 변수끼리의 나눗셈이 실수형이 나온다고 착각하신거 같아서

작성자분에게 드린말씀이었어요 ㅎㅎㅎ

지금보니까 제가 답글을 이상한데에 달았군요 ㅎㅎ
수정했습니다. 혼란을 드려 죄송합니다.

그냥 연산하면 자동으로 int 처리됩니다. ㅎㅎㅎ
이러한 행위를 integer promotion이라고 하죠.

int 끼리의 나눗셈의 결과도 int이므로 당연히 0이 나옵니다. ㅎㅎㅎ

abs가 뭘 말씀하시는지 모르겠지만 c 표준 함수 abs는 int형 변수를 매개변수로 받습니다.
따라서 unsigned int끼리의 차를 계산하여 언더플로우 된값을 넣으면 큰 값이 아니라 음수가 절댓값된 결과가 리턴됩니다.

printf 함수의 포멧 스트링을 무엇을 쓰느냐에 따라 출력되는 값이 다릅니다. (예, %d, %u)

왜 값을 알 수 없다고 하시는지 모르겠습니다. 값 알수 있읍니다. ^^

uint끼리의 차는 알 수 있습니다. 잘 정의되죠
오히려 int끼리의 차가 overflow나면 ub라서 알수가 없죠

uint32_t a = 1, b = 2;
uint32_t c = a - b; // 4294967295
int32_t d = 2'000'000'000, e = -d;
int32_t f = d - e; // ?????

아 이해했습니다 1/(1보다 큰 정수) 여서 1 미만값이어서 0 나오신거라고 생각하셨군요
죄송합니다 제대로 질문을 못했네요ㄷㄷ
실제 식에서는 분자가 분모보가 크게 정의가 됩니다

_update_interval = ((_ramp_time_sec*1000)/(abs(_current_duty_cycle-_target_duty_cycle)))*FADE_STEP_SIZE;

uint32 interval
#define step size = 1
uint32 ramp sec = 60
uint32 current = 0
uint32 target > current
일 때 interval이 0으로 나옵니다.

담부턴 원 코드 제대로 올릴게용…

흠 혹시 abs가 매크로라고 하셨는데 그거 때문에 그런거 아닐까요?
즉 분모가 아주 큰 값으로 잡혀서 그런거 아닐까 의심스럽네요.

예를 들면 이런것이죠.

unsigned int a = 0, b = 2;
printf("%d", 100 / (a - b));
printf("%d", 100 / (0 - 2));

실제로 위에 출력은 0이 나오고, 아래 출력은 -50이 나옵니다.

c 표준에 있는 abs함수를 사용하면

unsigned int a = 0, b = 2;
printf("%d", 100 / abs(a - b));
printf("%d", 100 / abs(0 - 2));

둘다 50으로 출력되죠.

그렇다면…

uint32_t _current_duty_cycle = 0, _target_duty_cycle = 100;
uint32_t _ramp_time_sec = 60;

int _update_interval = ((_ramp_time_sec * 1000) / 
                              (abs(_current_duty_cycle - _target_duty_cycle)));
printf("%d\n", _update_interval);

결과로 600이 나옵니다.

매크로 한번 확인해보세요.
더 좋은건 디버그 거셔서 분모의 값이 16진수로 어떻게 되어있는지 확인하시면 좋을거 같습니다.

아 이해했습니다 uint32가 underflow되서 매우 큰 값으로 설정되고, 그 값을 분모로 취하기 때문에 0 미만 값이 되어서 0이 된다는 뜻이군요
매크로는 이렇습니다

abs(x) (x) > 0 ? (x) : (-x)

매크로 때문에 그런거 맞네유 ㅎㅎㅎ

#include <stdio.h>
#define abs(x) (x) > 0 ? (x) : (-x)

int main()
{
    unsigned int a = 0, b = 3;
    printf("%u", abs(a - b));
    return 0;
}

잘생각해보시면 매크로이기 때문에.

(x) > 0 ? (x) : (-x)
(a-b) > 0 ? (a-b) (-a-b)

로 평가가 되겠지요??
매크로 함수내에 식을 넣을땐 조심해야합니다.
덧셈뺄셈보다 단항 연산자 - 가 더 우선순위가 높죠.

뿐만아니라,

#include <stdio.h>
#define abs(x) (x) > 0 ? (x) : (-x)

int main()
{
    unsigned int a = 0, b = 3;
    if ((a - b) > 0) printf("ok");
    return 0;
}

의 출력이 ok가 나오는것처럼,

이렇게 되버립니다.

앞으로는 math.h 헤더파일의 abs() 함수를 사용하세요 ㅎㅎㅎ

1 Like

저런식으로 적용될줄 몰랐네요 ㄷㄷ
감사합니다