소수 계산 어떻게 해결해야할까?

2023. 9. 16. 11:20·프로그래밍/Java

만약 글을 읽는 분께서, 돈과 관련된 업무 또는 정확한 수치를 기반으로 진행해야 하는 업무를 맡게 된다면 어떻게 하시겠습니까?

 

float이나 double을 해답으로 생각하셨다면 한번 이 글을 통해 더 나은 방법에 대해 같이 고민해보는게 좋을 것 같습니다.

 

float과 double의 문제점

float과 double은 연산했을 때, 나오는 가장 유명한 문제 입니다.

코드는 java를 기준으로 작성되었습니다.

public class FloatTest {

	public static void main(String[] args) {
		float a = 0.1f;
		float b = 0.2f;

		if(a + b == 0.3){
			System.out.println("a + b == 0.3");
		}

		System.out.println("a + b != 0.3");

	}
}

이 코드를 실행하게 되면 a+b != 0.3 이라는 결과를 출력하게 됩니다.

만약 a,b 변수를 double로 변경해도 똑같은 결과를 출력하게 됩니다.

왜 이런걸까요?

  • float과 double이 정확한 값(precise value)가 아닌 근삿값(approximate value)을 담고 있어서 발생하는, 부동소수 타입의 대표적인 문제입니다. 그렇다면 문제에 대한 근본적인 원인을 파악하기 위해 고정소수점과 부동소수점에 대해 간략하게 알아보겠습니다.

고정소수점과 부동소수점

  1. 고정 소수점 표현방식(fixed point number representation)

고정소수점 표현방식은 실수를 부호 비트(signed bit), 정수부(integer part)와 소수부(fractional part)로 나누고,자릿수를 고정하여 실수를 표현하는 방식입니다.

  • 예를 들어 7.75라는 실수를 2진수로 변환하면 111.11이 됩니다. 이를 각각 지수부와 소수부에 담아 표현합니다( 그림은 32비트 기준입니다)

고정소수점 표현방식은 구현 방법이 간단하다는 장점이 있지만, 자릿수가 제한되어 있으므로 표현할 수 있는 수의 범위가 한정적이라는 단점이 있습니다. 이에 따라 더 넓은 범위의 실수를 표현하기 위해 부동소수점이라는 개념이 등장했습니다.

 

2. 부동소수점 표현방식(floating point number representation)

 

부동소수점 표현방식은 실수를 부호부(sign), 지수부(exponent), 가수부(mantissa)로 나누고, 정규화된값을 각 비트에 나눠 담아 실수를 표현하는 방식입니다. 쉽게 말해 12.3456을 저장한다면 표현식을 0.123456 * 10^2로 변경한 다음, 가수부에는 0.123456을 담고 지수부에는 2를 저장하는 방식입니다.

(실제로는 IEEE 754 표준에 따라서 지수부에 bias라는 값을 더해주는 과정을 거치게 됩니다.)

부동소수점 표현방식은 고정소수점 표현방식에 비해 표현 범위가 더 넓지만, 근본적으로 2진수를 사용하므로 여전히 소수를 표현할 때 오차가 발생하게 됩니다. 예를 들어 0.3을 2진수로 변환하면 0.0100100100…처럼 특정 수가 무한적으로 반복됩니다. float과 double이 이와 같은 부동소수점 표현방식으로 구현되었기 때문에 앞선 문제들이 발생한 것 입니다.

이런 문제를 해결하기 위해 자바에서는 BigDecimal 클래스를 제공합니다.

 

BigDecimal이란?

공식문서에서 BigDecimal은 불변의 성질을 띠며, 임의 정밀도와 부호를 지니는 10진수라고 표현합니다. 임의 정밀도(arbitary-precision)란 쉽게 말하자면 아무리 큰 숫자라도 표현할 수 있는 것을 의미합니다.(실제로는 무한에 가까울 뿐 무한은 아닙니다.)임의 정밀도 연산은 플랫폼마다 구현에 차이는 있지만 기본적으로 큰 숫자를 배열에 나눠 담는 방식으로 구현됩니다.

1231235451235 = [1]+[2]+[3]+[1]+.....

BigDecimal은 내부적으로 임의 정밀도 연산을 활용하는 동시에 불변이므로 BigDecimal 객체 간의 연산마다 새로운 객체를 생성합니다. 이에 따라 float나 double 과 같은 기본 타입에 비해 사용하기 훨씬 느리며, 사용방법도 어렵습니다. 물론 int나 long이라는 대체제가 있지만, 실수를 표현할 수 없고 값의 범위가 비교적 제한된다는 점 때문에 BigDecimal은 금융 관련 계산에서 필수적으로 사용됩니다.

BigDecimal의 구성

BigDecimal은 개념적으로 임의 정밀도 정수형인 unscaled value와 소수점 오른쪽의 자릿수를 나타내는 32비트 정수인 scale로 구성됩니다.

  • 예를 들어 BigDecimal 3.14의 경우 unscaled value는 314이고, scale은 2가 됩니다.

내부적으로 살펴보면 intVal, scale , precision , intCompact 로 구성되어 있습니다.

  • intVal은 BigInteger 타입으로서 unscaled value를 저장하는 변수입니다.
  • scale은 소수점 오른쪽의 자릿수
  • precision은 총 자릿수를 나타냅니다.

또한 특이한 점으로는 intCompact가 long 타입으로 선언되어 있다는 것인데, 만약 값의 크기가 작아서 유효숫자의 절댓값이 long타입으로 표현될 수 있다면 BigDecimal은 unscaled value를 intVal 대신 intCompact에 저장함으로써 메모리를 최적화합니다.

package java.math;

public class BigDecimal extends Number implements Comparable<BigDecimal> {
    
    private final BigInteger intVal; // = unscaled value

    private final int scale;

    private transient int precision;

    private final transient long intCompact;
    
    ...

이외에도 BigDecimal클래스 사용법에 대해 알고 싶은 분은 참고 자료를 참고해주세요

참고 자료

https://dev.gmarket.com/75

'프로그래밍 > Java' 카테고리의 다른 글

Java volatile이란?  (0) 2023.09.08
함수형 프로그래밍  (0) 2023.04.11
G1 GC에 대해  (0) 2023.03.31
Singleton 패턴과 스프링에서는 Singleton 패턴을 어떻게 사용하고 있을까?  (1) 2023.01.27
Long 과 AtomicLong은 어떤 차이가 있을까?  (0) 2023.01.24
'프로그래밍/Java' 카테고리의 다른 글
  • Java volatile이란?
  • 함수형 프로그래밍
  • G1 GC에 대해
  • Singleton 패턴과 스프링에서는 Singleton 패턴을 어떻게 사용하고 있을까?
황심지
황심지
  • 황심지
    꾸준함이 진리다
    황심지
  • 전체
    오늘
    어제
    • 분류 전체보기 (51)
      • 프로그래밍 (12)
        • 운영체제 (0)
        • Spring (4)
        • Java (10)
        • SQL (0)
        • HTTP (2)
        • 회고 (2)
        • Network (0)
        • 프로젝트 (12)
        • Infra (2)
        • 데이터베이스 (3)
        • TIL (1)
        • 파이썬 (2)
      • 운동 (0)
        • 거인화 루틴 일지 (0)
        • 과부하 훈련 일지 (0)
        • 운동 관련 이모저모 (0)
  • 블로그 메뉴

    • 홈
    • 방명록
    • 글쓰기
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    패스트캠퍼스
    F-Lab 후기
    그런 RESTAPI로 괜찮은가?
    CAP Theorem
    python
    django orm
    배포 방식
    2023년회고
    패캠챌린지
    개인성장
    Java
    position argument
    1년회고록
    대용량 트래픽
    CAP 이론
    에프랩 후기 자바 백엔드
    에프랩 후기 자바 백엔드 부트캠프
    chatops
    페이징 최적화
    webflux
    패스트캠퍼스후기
    세션
    에프랩 후기
    쿼리 성능
    개인회고록
    한번에끝내는코딩테스트369Java편초격차패키지Online
    직장인자기계발
    레디스 세션
    spring
    직장인인강
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
황심지
소수 계산 어떻게 해결해야할까?
상단으로

티스토리툴바