✅ float와 double은 근사치 계산에 적합하며 정확한 결과가 필요할 때는 적합하지 않음
✅ 금융 계산 같은 정확한 계산에는 BigDecimal, int 또는 long을 사용해야 함
✅ BigDecimal은 정확하지만 사용이 불편하고 느림
✅ 성능이 중요하고 소수점을 직접 관리할 수 있다면 int나 long을 권장
📚 필수 개념 정리
🧮 부동소수점 vs 고정소수점 vs BigDecimal 비교
특성
부동소수점(float/double)
고정소수점(int/long)
BigDecimal
정밀도
제한적 (IEEE 754 표준)
소수점 직접 관리 (정수)
임의 정밀도 (무제한)
표현 범위
매우 넓음 (±1.7E±308)
제한적 (int: ~21억, long: ~922경)
사실상 무제한
정확성
부정확 (근사값)
정확 (범위 내에서)
정확 (십진수 표현)
성능
매우 빠름
빠름
느림 (~10배)
메모리
적음 (4/8바이트)
적음 (4/8바이트)
많음 (가변)
사용 편의성
쉬움
소수점 관리 필요
편리하지만 코드 복잡성 증가
적합한 용도
과학/공학, 센서값 계산
임베디드, 간단한 금융 계산(소규모)
금융/정밀 수치 계산
오차 발생
필연적
없음 (적절히 구현 시)
발생하지 않음
반올림 제어
제한적
직접 구현 필요
세밀한 제어 가능
❓ 이 코드의 결과는?
→ 결과는 0.9999999999999999 \n false
👉 왜 이런 결과가 나올까?
부동소수점의 한계: 0.1과 같은 일부 십진수는 컴퓨터에서 정확한 이진수로 표현되지 못해 작은 오차가 발생
오차 누적: 아무리 오차가 작더라도 10번의 덧셈 과정에서 누적되어 최종 결과가 1.0이 아닌 0.999999...로 계산됨
public class FloatingPointError {
public static void main(String[] args) {
double funds = 0;
for (int i = 0; i < 10; i++)
funds += 0.1;
System.out.println(funds);
System.out.println(funds == 1.0);
}
}
/**
* double을 사용한 금융 계산의 문제점
* 정확한 값이 계산되지 않아 오류 발생
*/
public class BrokenCashRegister {
public static void main(String[] args) {
double price = 2.10; // 상품 가격
double payment = 1.05; // 지불한 금액
// 거스름돈 계산 - 문제 발생!
System.out.println("받아야 할 금액: " + (price - payment)); // 1.0499999999999998
// 1.05가 나와야 하지만 부동소수점 오차로 인해 다른 값이 출력됨
}
}
import java.math.BigDecimal;
/**
* BigDecimal을 사용한 정확한 금융 계산
* 정확한 값을 계산할 수 있으나 코드가 장황해짐
*/
public class AccurateCashRegister {
public static void main(String[] args) {
// String 생성자를 사용해 정확한 값 표현
BigDecimal price = new BigDecimal("2.10");
BigDecimal payment = new BigDecimal("1.05");
// 정확한 거스름돈 계산
System.out.println("받아야 할 금액: " + price.subtract(payment)); // 정확히 1.05 출력
}
}
// 잘못된 방법 - 이미 오차가 있는 double 값으로 생성
BigDecimal wrongWay = new BigDecimal(0.1); // 0.1000000000000000055511151231257827021181583404541015625
// 올바른 방법 - 문자열로 생성하여 정확한 값 표현
BigDecimal rightWay = new BigDecimal("0.1"); // 정확히 0.1
import java.math.BigDecimal;
import java.math.RoundingMode;
/**
* BigDecimal을 활용한 실전 할인율 계산 예시
*/
public class DiscountCalculator {
public static void main(String[] args) {
// 상품 가격과 할인율
BigDecimal price = new BigDecimal("19.99");
BigDecimal discountRate = new BigDecimal("0.15"); // 15% 할인
// 할인액 계산 (가격 × 할인율)
BigDecimal discountAmount = price.multiply(discountRate);
// 최종 가격 계산 (가격 - 할인액)
BigDecimal finalPrice = price.subtract(discountAmount);
// 소수점 둘째 자리까지 반올림 (HALF_UP: 5 이상이면 올림)
finalPrice = finalPrice.setScale(2, RoundingMode.HALF_UP);
System.out.println("상품 원가: $" + price);
System.out.println("할인액: $" + discountAmount);
System.out.println("최종 가격: $" + finalPrice);
}
}
/**
* 주식 거래 시뮬레이션
* 주식 가격을 센트 단위로 계산
* 출력 시에만 실수로 변환, 계산은 모두 정수로 수행
*/
public class StockTrader {
public static void main(String[] args) {
// 센트 단위로 계산
long stockPriceInCents = 12345; // $123.45
int sharesCount = 100;
// 총 비용 계산
long totalCostInCents = stockPriceInCents * sharesCount;
// 결과 출력 (센트 → 달러 변환)
System.out.println("주당 가격: $" + stockPriceInCents / 100.0);
System.out.println("총 주식 수: " + sharesCount + "주");
System.out.println("총 비용: $" + totalCostInCents / 100.0);
// int 사용 시 주의: 오버플로우 가능성 검사
int maxIntValue = Integer.MAX_VALUE; // 2,147,483,647
System.out.println("int 최대값: " + maxIntValue);
System.out.println("큰 금액은 int 범위를 초과할 수 있어 long 사용 권장");
}
}
/**
* BigDecimal과 long의 성능 비교
*/
import java.math.BigDecimal;
import java.time.Duration;
import java.time.Instant;
public class PerformanceComparison {
public static void main(String[] args) {
int iterations = 10_000_000;
// long을 사용한 계산 시간 측정
Instant start1 = Instant.now();
long sum1 = 0;
for (int i = 0; i < iterations; i++) {
long value = i * 100; // 달러 → 센트 변환
sum1 += value;
}
Instant end1 = Instant.now();
// BigDecimal을 사용한 계산 시간 측정
Instant start2 = Instant.now();
BigDecimal sum2 = BigDecimal.ZERO;
BigDecimal multiplier = BigDecimal.valueOf(100);
for (int i = 0; i < iterations; i++) {
BigDecimal value = BigDecimal.valueOf(i).multiply(multiplier);
sum2 = sum2.add(value);
}
Instant end2 = Instant.now();
// 소요 시간 비교
long longDuration = Duration.between(start1, end1).toMillis();
long bigDecimalDuration = Duration.between(start2, end2).toMillis();
System.out.println("long 연산 소요 시간: " + longDuration + "ms");
System.out.println("BigDecimal 연산 소요 시간: " + bigDecimalDuration + "ms");
System.out.println("BigDecimal은 long보다 약 " +
(bigDecimalDuration / longDuration) + "배 느림");
}
}
long 연산 소요 시간: 18ms
BigDecimal 연산 소요 시간: 423ms
BigDecimal은 long보다 약 23배 느림
[Execution complete with exit code 0]