부동 소수점 방식을 사용하면 고정 소수점 방식보다 훨씬 더 많은 범위까지 표현할 수 있다.
하지만 부동 소수점 방식에 의한 실수의 표현은 항상 오차가 존재한다는 단점을 가지고 있다.

 


실수의 표현 방식

 

컴퓨터에서 실수를 표햔하는 방법은 정수에 비해 훨씬 복잡하다.

왜냐하면, 컴퓨터에서는 실수를 정수와 마찬가지로 2진수로만 표현해야 하기 때문이다.

따라서, 실수를 표현하기 위한 다양한 방법들이 연구되었으며, 현재에는 다음과 같은 방식이 사용되고 있다.

 

1. 고정 소수점(Fixed Point) 방식

2. 부동 소수점(Floating Point) 방식

 


고정 소수점(Fixed Point) 방식

 

실수는 보통 정수부와 소수부로 나눌 수 있다.

따라서, 실수를 표현하는 가장 간단한 방식은 소수부의 자릿수를 미리 정하여, 고정된 자릿수의 소수를 표현하는 것이다.

 

32비트 실수를 고정 소수점 방식으로 표현하면 다음과 같다.

 

 

 

하지만 이 방식은 정수부와 소수부의 자릿수가 크지 않으므로, 표현할 수 있는 범위가 매우 적은 단점이 있다.

 


부동 소수점(Floating Point) 방식

 

실수는 보통 정수부와 소수부로 나누지만, 가수부와 지수부로 나누어 표현할 수도 있다.

부동 소수점 방식은 이렇게 하나의 실수를 가수부와 지수부로 나누어 표현하는 방식이다.

 

앞서 살펴본 고정 소수점 방식은 제한된 자릿수로 인해 표현할 수 있는 범위가 매우 작지만,  부동 소수점 방식은 다음 수식을 이용하여 매우 큰 실수까지도 표현할 수 있다.

 

 

 

현재 대부분의 시스템에서는 부동 소수점 방식으로 실수를 표현하고 있다.


부동 소수점 방식의 오차

 

컴퓨터에서 실수를 표현하는 방법은 정확한 표현이 아닌 언제나 근사치를 표현할 뿐임을 항상 명심해야 한다.

 

다음 예제는 부동 소수점 방식으로 실수를 표현할 때 발생할 수 있는 오차를 보여주는 예제이다.

 

int i;

float sum = 0;

 

for (i = 0; i < 1000; i++)

{

    sum += 0.1;

}

 

cout << "0.1을 1000번 더한 합계는 " << sum <<"입니다.";

 

실행결과

 

 

위의 예제에서 0.1을 1000번 더한 합계는 원래 100이 되어야 하지만, 실제로는 99.999가 출력된다.

이처럼 컴퓨터에서 실수를 가지고 수행하는 모든 연산에는 언제나 작은 오차가 존재하게 되는데, 모든 프로그래밍 언어에서 발생하는 기본적인 문제이다.

 


해결방법


JavaScript

 

. toFixed() 메서드

 

toFixed()는 입력받은 숫자를, 매개변수만큼 자리수를 반올림해 String으로 반환해주는 함수이다. 다시말해 매개변수는 소수점 몇 번째 자리까지 나타낼 지를 나타낸다. 참고로 0부터 20까지 입력할 수 있다. 숫자예를 들어 아래처럼 매개변수를 2로 지정해주면, 소수점 둘째자리까지 반올림해서 나타낸다. default값은 0이기 때문에 toFixed()만 입력하면 소수를 정수로 만들 수 있다.

 

그리고 String으로 반환한다는 점을 주의하자! 'number '타입으로 사용하고 싶다면 Number() 메서드를 한번 더 사용해서 숫자로 변환해줘야 한다.

 

let result = (0.1 + 0.2).toFixed(2); 
// '0.30' 
Number(result); 
// 0.3

 

 

2. Math.round() 메서드

 

Math 객체에 있는 Math.floor, Math.ceil, Math.trunc, Math.round 등의 메서드를 통해 소수 연산을 다룰 수도 있다. 이 중 Math.round()에 대해 살펴보자. Math.round()는 이름처럼 '반올림'을 해주는 함수이다. 매개변수로 들어온 값을 반올림한 후, 가장 가까운 정수 값을 반환한다. toFixed()의 매개변수를 0으로 지정해줬을 때처럼 정수값을 반환해준다는 점에 주의하자.

 

Math.round(20.49); 
// 20 
Math.round((0.1 + 0.2) * 10) / 10; 
// 0.3

 

 

3. 기타 라이브러리

 

Javascript의 수학 라이브러리를 이용하면 좀 더 쉽게 계산을 할 수도 있다. Big.js BigNumber.js,  Decimal.js,  mathjs 등의 라이브러리들이 있어서 찾아보고 취향에 따라(?) 사용하면 된다.

 

 


JAVA

 

자바에서는 BigDecimal 클래스를 제공하고 있다. 소수점을 다루는 연산을 한다면 BigDecimal 클래스의 사용은 필수적이다.

 

BigDecimal 클래스는 java.math 패키지 안에 포함되어 있다. 보통 생성자와 파라미터로 문자열을 넘겨 생성하는 것이 기본적이지만, 정적 팩토리 메서드도 제공한다. 생성자를 사용할 때 주의할 점은, 문자열이 아닌 double 타입 값을 넘기면 안 된다.

// 생성자 + 문자열로 초기화하는 방법
BigDecimal value1 = new BigDecimal("12.23");

// double 타입으로 초기화하는 방법
// 내부적으로 생성자 + 문자열을 사용한다.
BigDecimal value2 = BigDecimal.valueOf(34.45);

// 아래와 같이 사용하면 안 된다.
// 12.230000000000000426325641456060111522674560546875
BigDecimal dontDoThis = new BigDecimal(12.23);

 

더하기(add)

add 메서드로 덧셈 연산을 할 수 있다. 예제에서 사용한 BigDecimal.ONE은 BigDecimal 클래스에서 제공하는 전역 상수다. ZERO, TEN 도 있다.

BigDecimal value = new BigDecimal("12.23");

// 13.23
value.add(BigDecimal.ONE);

 

빼기(subtract)

뺄셈 연산은 subtract 메서드를 사용하면 된다.

BigDecimal value = new BigDecimal("12.23");

// 2.23
value.subtract(BigDecimal.TEN);

 

곱하기(multiply)

곱하기 연산은 multiply 메서드를 사용하면 된다.

BigDecimal value = new BigDecimal("1");

// 10
value.multiply(BigDecimal.TEN);

 

나누기(divide)

divide 메서드를 사용하면 나눗셈 연산이 가능하다. 하지만 정확하게 나누어 몫이 떨어지지 않는 수의 경우 ArithmeticException 예외를 던진다. 아래 예제를 확인해보자.

BigDecimal value1 = new BigDecimal("11");
BigDecimal value2 = BigDecimal.valueOf(3);

// Exception in thread "main" java.lang.ArithmeticException:
// Non-terminating decimal expansion; no exact representable decimal result.
value1.divide(value2);

 

아무리 BigDecimal 클래스라도 나누어떨어지지 않는 수는 정확하게 표현할 수 없다. 따라서 divide 메서드를 사용할 때는 소수점 몇 번째짜리까지, 어떻게 처리할 것인지 지정을 해줘야 한다.

예제에서 2번째 파라미터는 N 번째 자리까지 표현할 것인가를 뜻하고, 3번째 파라미터는 처리 방식이다. 즉, 아래 예제의 경우 소수점 3번째 자리에서 반올림하여 2번째 자리까지 표기한다.

참고로 BigDecimal.ROUND_HALF_UP와 같은 상수는 자바 9에서 Deprecated 되었다.

BigDecimal value1 = new BigDecimal("11");
BigDecimal value2 = BigDecimal.valueOf(3);

// 3.67
value1.divide(value2, 2, RoundingMode.HALF_UP);

 

그 밖의 메서드들

 

나머지 계산(reminder)

reminder 메서드로 나눗셈 결과의 나머지도 구할 수 있다.

BigDecimal value = new BigDecimal("10");

// 2
value.remainder(BigDecimal.valueOf(4));

값 비교(compareTo)

BigDecimal 인스턴스는 compareTo 메서드를 사용하여 서로 비교할 수 있다. 파라미터로 전달되는 값보다 작은 경우 -1, 큰 경우 1 그리고 같은 경우 0을 반환한다.

BigDecimal value = new BigDecimal("10");

// 0
value.compareTo(BigDecimal.TEN);

// 1
value.compareTo(BigDecimal.ONE);

// -1
BigDecimal.ONE.compareTo(value);

최대, 최소(max, min)

최댓값은 max, 최솟값은 min 메서드를 사용하면 전달되는 파라미터와 비교하여 구할 수 있다.

BigDecimal value = BigDecimal.valueOf(10);

// 10
value.max(BigDecimal.ONE);

// 1
value.min(BigDecimal.ONE);

소수점 처리 방식

자바에서는 BigDecimal 클래스에 다양한 소수점 처리 방식을 제공하고 있다. 몇 가지 예시로 살펴보면 아래와 같다.

// 소수점 첫 번째까지 표현, 두번째 자리에서 반올림
// 12.4
BigDecimal.valueOf(12.35).setScale(1, RoundingMode.HALF_UP);

// 소수점 이하 모두 제거하고 올림
// 13
BigDecimal.valueOf(12.34).setScale(0, RoundingMode.CEILING);

// 음수인 경우는 특정 자릿수 이하 제거
// -12.3
BigDecimal.valueOf(-12.34).setScale(1, RoundingMode.CEILING);

// 특정 자릿수 이하 버림
// 12.3
new BigDecimal("12.37").setScale(1, RoundingMode.FLOOR);

 

 

출처 :

https://madplay.github.io/post/the-need-for-bigdecimal-in-java

http://www.tcpschool.com/cpp/cpp_datatype_floatingPointNumber

'WebBase' 카테고리의 다른 글

[WebBase] CORS Error  (0) 2023.03.07
[WebBase] 스택(Stack )과 힙(Heap) 메모리영역(Memory)  (0) 2022.09.22
[WebBase] Spring에서 Ajax로 JSON데이터 받기  (0) 2022.07.20
[WebBase] jQuery.ajax  (0) 2022.06.23
[WebBase] Ajax  (0) 2022.06.23

+ Recent posts