본문 바로가기
[ javascript ]

부동소수점 이슈

by 히앤님 2024. 3. 29.
반응형
SMALL

부동소수점이란?

부동소수점 또는 떠돌이 소수점 방식은 실수를 컴퓨터상에서 근사하여 표현할 때 소수점의 위치를 고정하지 않고 그 위치를 나타내는 수를 따로 적는 것으로, 유효숫자를 나타내는 가수와 소수점의 위치를 풀이하는 지수로 나누어 표현한다. (출처 위키백과)

 

정리하자면 실수를 표시하기 위해 소수점을  근사값으로 표시하는 것.

 

사람과 컴퓨터의 숫자 표현 방법

컴퓨터 : 2진법

사람 : 10진법

 

컴퓨터는 2진법으로 데이터를 전부 표시하는데, 사람은 10진법으로 숫자를 표현한다. 즉, 숫자 표현법이 달라지는 데에서 이를 최대한 근사하게 표시하기 위한 방법이 필요하다. 본래 이진법에서는 십진수 소수가 정확히 표현되지 않고 근삿값으로 표현된다.

 

예시를 잘 들어주신 분이 있어서 예제를 가져와봄.(출처 : https://velog.io/@dacircle/부동소수점이란 )

10진수의 경우

  • 1/3을 소수로 나타내면 0.3333333333333333333333333333333333… 무한소수가 된다.
  • 10진법로도 정확히 나타낼 수 없는 수가 있다.. 그렇다면 다른 진법으로는 어떨까?

2진법의 경우

  • 9.625를 2진법으로 나타내면 다음과 같다.
    9 = 2³ + 2² + 2¹ + 2⁰ ⇒ 1001
    0.625 =1/2¹ + 1/2² + 1/2³ ⇒ 101
    9.625 = 1001.101
  • 1/10을 2진법으로 나타내면 다음과 같다.
    1/10은 0.1이므로 소수부분만 나타내면 된다.
    1/2¹ + 1/2² + 1/2³ + 1/2⁴ + 1/2⁵ + 1/2⁶ + 1/2⁷ + 1/2⁸ + 1/2⁹ + 1/2¹⁰ + 1/2¹¹ + 1/2¹² + 1/2¹³ …
    0.0001100110011…
    0.0999755859375…
    ⇒ 0.1이 아니다!

 

컴퓨터가 숫자를 저장하기 위해 2진법으로 소수부를 표현할 때,

10진법에서는 무한소수가 아닌 명확한 유한소수이더라도 2진법으로는 표현이 안될 때가 있다.

 

10진법으로는 0.1 + 0.2 === 0.3 일 수 있지만,

2진법에서는 0.1 + 0.2 는 0.30000000000000004 이므로 같다고 볼 수 없다.

 

부동소수점 해결 방법

 

  • 간단한 계산: 정수 변환 또는 Math.round()를 사용.
  • 높은 정확도가 필요한 계산: 부동소수점 문제를 해결하기 위한 전문적인 라이브러리를 사용 (Decimal.js 또는 Big.js 가 유명하다)
  • 실시간 비교: Number.EPSILON 기반 비교.

 

 

1. 정수로 변환하여 계산

소수점을 제거하기 위해 값을 정수로 변환한 후 계산하고, 결과를 다시 소수로 변환한다.

const a = 0.1;
const b = 0.2;

// 정수로 변환하여 계산
const result = (a * 100 + b * 100) / 100;
console.log(result); // 0.3
 

 

2. toFixed() 메서드 사용

JavaScript의 toFixed() 메서드를 사용해 원하는 소수점 이하 자릿수로 결과를 제한한다.단, toFixed()는 문자열을 반환하므로, 숫자로 다시 변환하려면 Number()를 사용해야 함.

 

const a = 0.1;
const b = 0.2;

const result = (a + b).toFixed(2); // 소수점 둘째 자리까지 표시
console.log(result); // '0.30'
console.log(Number(result)); // 0.3 (문자열을 숫자로 변환)
 

3. Math.round()로 반올림

계산 후 원하는 자리수로 반올림한다.

const a = 0.1;
const b = 0.2;

// 소수점 10자리까지 계산 후 반올림
const result = Math.round((a + b) * 100) / 100;
console.log(result); // 0.3

 

4. EPSILON 비교 (허용 오차 사용)

부동소수점 문제를 근본적으로 없애는 것이 아니라, 값의 차이가 아주 작으면 같은 값으로 간주하는 방식.

const a = 0.1 + 0.2;
const b = 0.3;

// JavaScript의 가장 작은 차이값인 Number.EPSILON 사용
if (Math.abs(a - b) < Number.EPSILON) {
  console.log("같은 값입니다."); // 출력됨
} else {
  console.log("다른 값입니다.");
}
 

 

 

[제품 가이드]

Q. 계산이 딱 떨어지지 않고 .99999999 와 같이 근사치로 떨어집니다.

javascript 에서는 소수점이라는 형식이 없기 때문에 소수점 표시를 위해 6자리의 숫자를 곱해서 다시 나누는 방식으로 소수점을 표시한다. 제품의 Float 타입은 별도의 Format 을 지정하지 않는다면, 소수점 이하 6자리의 format (#,###.######) 이 지정되고, 소수점 표현을 위해서는 정수부자리수 + 소수점 + 소수부자리수 합쳐 18자리까지 표시 가능하다. 따라서 Format을 설정해서 소수점 자리를 제한하는 것이 필요하다. 소수점 이하 6자리가 넘어가면 제대로 표현이 안될 수 있음.

 

반응형
LIST

댓글