double 타입 사용시 주의할 점
오늘 삽질했던 코드. 평소에는 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.rate는 int, 100도 정수 리터럴이다. C++은 양쪽 피연산자가 모두 정수면 정수 나눗셈을 먼저 수행하고, 그 결과를 나중에 double에 대입한다. 950 / 100이 정수 나눗셈으로 9가 되고, 이 9가 double로 변환되어 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 등 대부분의 정적 타입 언어에서 공통으로 나타나는 함정이다. “나눗셈의 두 피연산자 타입이 결과를 결정하고, 대입받는 변수의 타입은 그 이후에나 영향을 준다”는 원칙만 기억해두면 된다.
댓글 남기기