재산 관리 서비스 프로젝트를 진행하면서 계산을 하는 로직이 매우 많았습니다.
예를들어 예산 금액을 추천하는 계산 메소드를 보면,
/**
* 계산기
* 예산 금액 추천을 계산
* 카테고리의 예산 금액 = 반올림((예산 금액) * (해당 카테고리의 퍼센테이지 / 100)) / 최소 단위 금액) * 최소 단위 금액
*
* @param cost 예산 총 금액
* @param percentageResult 카테고리 별 금액 퍼센트
* @return 카테고리별 예산 금액 리스트
*/
private List<Integer> calculatorRecommendResult(
int cost,
List<Double> percentageResult
) {
return percentageResult.stream()
.map(percent -> {
double categoryCost = cost * (percent / PERCENT_UNIT);
double roundedUnitCost = Math.round((categoryCost / MIN_COST_UNIT)) * MIN_COST_UNIT;
return (int) roundedUnitCost;
})
.toList();
}
다음과 같이 소수점을 포함한 계산을 합니다.
소수점을 계산할 때, 약간의 오차가 발생하는데 이는 부동소수점 때문입니다.
실수를 표현할 때 대부분의 시스템에서 사용하는 방식으로, 소수점을 고정시키 않고 가수부를 통해 자릿수를 구하기에 오차가 발할 수 있다.
이를 정확하게 계산하는 방법은 BigDecimal 를 사용하면, 내부 로직을 통해, 정확한 계산을 할 수있지만 속도에서 큰 차이가 납니다.
따라서 계산을 할 때 무분별한 BigDecimal 을 피하고, 도메인에 맞는 방식을 선택하는게 중요하다고 생각했습니다.
저는 추천 시스템에서 Double 방식을 택했습니다.
애초에 추천을 100원 단위로 반올림하기 때문에, 정확한 값을 주는게 아닐 뿐더러,
회원이 정확한 금액을 추천받기 보단 대략 추천 금액을 빠르게 받는게 더 중요하다고 생각했기 때문입니다.