1 분 소요

오늘 삽질했던 코드. 평소에는 double을 잘 안 쓰다보니 헷갈렸다.

문제가 되었던 코드.

for (auto iter = range.first; iter != range.second; iter++)
{
    int reward_id = (*iter).second.reward_id;
    int funcsrl = (*iter).second.funcsrl;
    int reward_type = (*iter).second.reward_type;
    std::string reward_itemcode = (*iter).second.itemcode;
    double rate = (*iter).second.rate / 100;	// DB에는 만분률로 되어있으므로 백분율로 환산해야함.

    lottery_info.Add(std::make_tuple(reward_id, funcsrl, reward_type, reward_itemcode), rate);

#ifdef _DEBUG
    ServerStateLog(g_DebugTracerServer, "RewardID:[%d] FuncSRL:[%d] Rate:[%f]", reward_id, funcsrl, rate);
#endif
}

확률 계산처리를 해야하는데 DB에는 데이터가 만분율 기준(ex : 9.5% = 950)으로 들어가 있고 확률 계산처리 함수에는 백분율로 넣어줘야했다. 그래서 데이터를 가져오면서 나누기 100을 했던 것인데…

이터레이터 안의 (*iter).second.rate 값은 int 값. 950을 100으로 나누어 그 결과인 9.5를 double 형으로 얻으려는 것이었다.

확률 계산에서 문제가 계속 일어나 찾아보니 저 값이 9.5가 아닌 9.0이 들어가고 있었다.

원인

(*iter).second.rateint, 100도 정수 리터럴이다. C++은 양쪽 피연산자가 모두 정수면 정수 나눗셈을 먼저 수행하고, 그 결과를 나중에 double에 대입한다. 950 / 100이 정수 나눗셈으로 9가 되고, 이 9double로 변환되어 9.0이 저장된 것이다. 대입할 변수의 타입이 double이라고 해서 나눗셈 자체가 실수 연산이 되는 것은 아니다.

2026년 수정 안내 원래 이 글은 / 100/ 100.0f로 고쳐 해결했다고 적었는데, 그 수정 자체는 문제를 없애지만 의도와 정확히 맞는 수정은 아니었다. f 접미사는 float 리터럴이라, 결과는 int / float → float로 계산된 뒤 double 변수에 대입되면서 다시 넓혀진다. 최종 결과값은 맞게 나오지만 중간에 float의 정밀도(유효숫자 약 7자리)를 한 번 거치게 된다. 최종 타입이 double이라면 처음부터 double 리터럴을 쓰는 것이 정확하다.

올바른 수정

double rate = (*iter).second.rate / 100.0;   // 100.0 은 double 리터럴

100.0(접미사 없음)은 double 리터럴이다. 피연산자 중 하나가 double이면 다른 하나(int)도 double로 변환된 뒤 나눗셈이 일어나므로, 950 / 100.0은 실수 나눗셈으로 계산되어 9.5가 그대로 나온다.

정리

리터럴 접미사 타입 이 경우 적합성
100 없음 int 정수 나눗셈이 되어버려 원인이 된 표기
100.0f f float 결과는 맞지만 float을 거쳐 정밀도가 좁아짐
100.0 없음 double 최종 타입이 double이라면 이것이 정확

정수 나눗셈은 C++뿐 아니라 C, Java, C#, Go 등 대부분의 정적 타입 언어에서 공통으로 나타나는 함정이다. “나눗셈의 두 피연산자 타입이 결과를 결정하고, 대입받는 변수의 타입은 그 이후에나 영향을 준다”는 원칙만 기억해두면 된다.

댓글 남기기