실수형의 종류
범위 | bit | byte | |
float | 1.4E-45 ~ 3.4E38 | 32 | 4 |
double | 4.9E-324 ~ 1.8E308 | 64 | 8 |
실수형의 저장 형식
부호 (Sign) | 지수 (Exponent) | 가수 (Mantissa) | |
float | 1 | 8 | 23 |
double | 1 | 11 | 52 |
소수 점의 표현 방식
- 소수 점의 표현 방식은 두 가지가 있으며 두 가지 방식은 고정 소수점 방식과 부동 소수점 방식이다.
- 컴퓨터는 정수와 마찬가지로 실수를 2진수로 표현해야 하기 때문에 실수를 표현하는 것이 정수를 표현하는 것보다 많이 복잡하다.
- 10진수의 유한 소수가 2진수로 무한 소수가 될수 있어 2진수로 10진수를 정확하게 표현하기 어렵고 버려지는 값들로 인해 오차가 발생한다.
고정 소수점 방식
- 부호 : 0이면 양수, 1이면 음수
각 위치마다 2진수가 저장되기 때문에 표현할수 있는 범위가 제한적이다.
총 32bit로 표현 가능한 실수의 범위와 정밀도가 제한적이어서 잘 사용하지 않는다.
부동 소수점 방식
- 부호 (Sign) : 0이면 양수, 1이면 음수
- 지수부 (Exponent) : 소수점의 위치를 나타낸다.
- 가수부 (Mantissa) : 양의 정수를 표현한다.
부동 소수점은 IEE 754 표준 방식을 따르고 있다.
부동 소수점 방식의 각 위치의 bit는 타입에 맞는 실수형 bit와 동일하다.
고정 소수점 방식보다 넓은 범위와 높은 정밀도를 가지고 있기 때문에 많은 시스템에서 부동 소수점 방식을 사용한다.
부동 소수점 방식의 오차
double num = 0.1;
for(int i = 0; i < 1000; i++) {
num += 0.1;
}
System.out.println(num);
위의 예상 결과가 100으로 예상이 되지만 실제로는 100.09999999999859가 출력된다.
이처럼 컴퓨터가 연산하는 모든 실수형 연산은 작은 오차가 있다.이런 경우 작은 오차에도 민감한 금융 업계에서는 이 오차가 큰 영향을 미칠 수 있다.
부동 소수점 방식의 오차 해결 방법
해결 방법은 BigDecimal 클래스를 사용하는 것이다.
BigDecimal의 특성
- BigDecimal은 실수형과 달리 정수를 이용하여 실수를 표현한다.
- 오차가 없는 2진수 정수로 변환하여 다룬다.
- 실수를 정수와 10의 제곱의곱으로 표현한다. ex) 정수 * 10^-scale
- 정수를 저장하기 위해 BigInteger를 사용한다.
BigDecimal의 생성
BigDecimal bd = new BigDecimal("123.456789"); // 문자열 리터럴
BigDecimal bd = new BigDecimal(123.456789); // 실수형 리터럴
BigDecimal bd = new BigDecimal(123456789); // 정수형 리터럴
BigDecimal bd = BigDecimal.valueOf(123.456789); // 실수형 리터럴
BigDecimal bd = BigDecimal.valueOf(123456789); // 정수형 리터럴
생성의 예제에서 실수형 타입의 값을 매개변수로 사용하면 오차가 발생한다.
BigDecimal bd = new BigDecimal(0.1); // 0.100000....5511...
BigDecimal bd = new BigDecimal("0.1"); // 0.1
BigDecimal의 연산
BigDecimal b = new BigDecimal("123");
// 더하기
BigDecimal b1 = b.add(BigDecimal.ONE); // 124
// 빼기
BigDecimal b2 = b.subtract(BigDecimal.TEN); // 113
// 나누기
BigDecimal b3 = b.divide(new BigDecimal("10")); // 12
// 나머지
BigDecimal b4 = b.remainder(new BigDecimal("10")); // 3;
BigDecimal의 반올림
BigDecimal b1 = new BigDecimal("12.34").setScale(0, RoundingMode.UP); // 13 UP
BigDecimal b2 = new BigDecimal("-12.34").setScale(0, RoundingMode.UP); // -13 UP
BigDecimal b3 = new BigDecimal("12.34").setScale(0, RoundingMode.DOWN); // 12 DOWN
BigDecimal b4 = new BigDecimal("-12.34").setScale(0, RoundingMode.DOWN); // -12 DOWN
BigDecimal b5 = new BigDecimal("12.34").setScale(0, RoundingMode.CEILING); // 13 CEILING
BigDecimal b6 = new BigDecimal("-12.34").setScale(0, RoundingMode.CEILING); // -12 CEILING
BigDecimal b7 = new BigDecimal("12.34").setScale(0, RoundingMode.FLOOR); // 12 FLOOR
BigDecimal b8 = new BigDecimal("-12.34").setScale(0, RoundingMode.FLOOR); // -13 FLOOR
BigDecimal b9 = new BigDecimal("12.44").setScale(0, RoundingMode.HALF_UP); // 12 HALF_UP
BigDecimal b10 = new BigDecimal("12.54").setScale(0, RoundingMode.HALF_UP); // 13 HALF_UP
BigDecimal b11 = new BigDecimal("12.50").setScale(0, RoundingMode.HALF_DOWN); // 12 HALF_DOWN
BigDecimal b12 = new BigDecimal("12.61").setScale(0, RoundingMode.HALF_DOWN); // 13 HALF_DOWN
BigDecimal b13 = new BigDecimal("12.50").setScale(0, RoundingMode.HALF_EVEN); // 12 HALF_EVEN
BigDecimal b14 = new BigDecimal("12.61").setScale(0, RoundingMode.HALF_EVEN); // 13 HALF_EVEN
BigDecimal b15 = new BigDecimal("12").setScale(0, RoundingMode.UNNECESSARY); // 12 UNNECESSARY
BigDecimal b16 = new BigDecimal("12.1").setScale(0, RoundingMode.UNNECESSARY); // ArithemeticException UNNECESSARY
- setScale의 RoundingMode를 설정하지 않으면 기본으로 UNNECESSARY가 설정된다.
- setScale의 첫번째 인자 값은 소수점 자릿수이다. 0번부터 시작하며 몇 번째 소수점을 올림 또는 내림할 것인지 지정하는 것이다.
- setScale의 두번째 인자 값은 올림, 반올림, 내림, 반내림 등 어떤 모드로 설정할 것인지에 대한 값이다.
BigDecimal의 반올림 정리표
아래의 표는 RoundingMode enum을 참고하였다.
UP | DOWN | CEILING | FLOOR | HALF_UP | HALF_DOWN | HALF_EVEN | UNNECESSARY | |
5.5 | 6 | 5 | 6 | 5 | 6 | 5 | 6 | throw ArithmeE.. |
2.5 | 3 | 2 | 3 | 2 | 3 | 2 | 2 | throw ArithmeE.. |
1.6 | 2 | 1 | 2 | 1 | 2 | 2 | 2 | throw ArithmeE.. |
1.1 | 2 | 1 | 2 | 1 | 1 | 1 | 1 | throw ArithmeE.. |
1.0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
-1.0 | -1 | -1 | -1 | -1 | -1 | -1 | -1 | -1 |
-1.1 | -2 | -1 | -1 | -2 | -1 | -1 | -1 | throw ArithmeE.. |
-1.6 | -2 | -1 | -1 | -2 | -2 | -2 | -2 | throw ArithmeE.. |
-2.5 | -3 | -2 | -2 | -3 | -3 | -2 | -2 | throw ArithmeE.. |
-5.5 | -6 | -5 | -5 | -6 | -6 | -5 | -6 | throw ArithmeE.. |
- UP : 양수 -> 올림, 음수 -> 내림
- DOWN : 양수 -> 내림, 음수 -> 올림
- CEILING : 올림
- FLOOR : 내림
- HALF_UP : 5이상 올림, 5 미만 버림 (반올림)
- HALF_DOWN : 6이상 올림, 6 미만 버림 (반올림)
- HALF_EVEN : 자리의 값이 짝수 -> HALF_DOWN, 홀수 -> HALF_UP
- UNNECESSARY : 나눗셈 결과가 딱 떨어지는 수가 아니면 throw ArithemeticException 발생
참고
* http://tcpschool.com/java/java_datatype_floatingPointNumber
코딩교육 티씨피스쿨
4차산업혁명, 코딩교육, 소프트웨어교육, 코딩기초, SW코딩, 기초코딩부터 자바 파이썬 등
tcpschool.com
* https://madplay.github.io/post/the-need-for-bigdecimal-in-java
자바 BigDecimal: 정확한 실수의 표현과 부동 소수점
자바에서 정확하게 실수를 표현하려면 어떻게 해야 할까? 그리고 부동 소수점 방식이란 무엇일까?
madplay.github.io
'개발 언어 > Java' 카테고리의 다른 글
[Java] 연산자 (0) | 2021.08.18 |
---|---|
[Java] 형변환 (0) | 2021.08.18 |
[Java] 오버 플로우, 언더 플로우 (0) | 2021.08.12 |
[Java] 상수와 리터럴 (0) | 2021.08.11 |
[Java] 기본형과 참조형 (0) | 2021.08.11 |