실수형의 종류

  범위 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

+ Recent posts