데이터 정확성을 위한 빅 데시멀 타입과 지수 표기법의 소수점 표시

3 minute read

AWS 빌링 데이터를 다루는 프론트엔드 개발자로 일하면서 백엔드에서 전달받은 숫자 데이터가 부동 소수점 연산의 함정과 지수 표기식(e notation)으로 표현되면서 생긴 이슈들을 해결하면서 빅 데시멀과 지수 표기법에 대해 정리해보았습니다.

부동 소수점 타입과 빅 데시멀

부동 소수점 타입

부동 소수점 타입은 IEEE 754 표준에 따라 일반적으로 내부적으로 2진수로 저장됩니다. 이진수로 저장하면 정확하게 표현할 수 없는 소수점 위치가 발생해 오차가 발생할 수 있습니다. 예를 들어 0.1을 더블(Double) 타입으로 표현하면 0.1이 아니라 0.1000000000000000055511151231257827021181583404541015625와 같이 표현 될 수 있습니다.

부동 소수점의 한계

부동 소수점의 이러한 오차는 일반적으로 작은 범위에서 무시할 만한 수준이지만 계산이 계속해서 이어지면서 쌓이게 되면 결국 오차가 누적되어서 결과값이 크게 차이나게 될 수 있습니다.

특히 큰 값을 다룰 때는 오차가 누적되어 부동소수점의 한계로 인한 계산 문제가 더 많이 발생하기 때문에 정밀한 계산이 필요한 경우에는 부동소수점 타입을 사용하지 않는 것이 좋습니다.

빅 데시멀

자바에서는 부동 소수점의 한계를 극복하기 위해 일반적인 부동 소수점 타입과는 구분되는 Java 표준의 빅 데시멀(Big Decimal)이라는 타입이 제공됩니다. 빅 데시멀은 내부적으로 십진수로 변환하여 저장하기 때문에 0.1은 0.1이라고 저장합니다. 따라서 부동 소수점의 한계로 발생하는 오차를 줄일 수 있어서 정확한 계산을 보장합니다. 하지만 빅 데시멀은 부동소수점에 비해 느리고 메모리 사용량도 더 많기 때문에 큰 데이터셋을 다루는 경우에는 성능상 이슈가 될 수 있습니다.

빅 데시멀은 자바에서 제공하는 부동 소수점 타입으로, IEEE 754 표준을 따르는 일반적인 부동 소수점 타입과는 구분됩니다.

지수 표기법

너무 크거나 작은 숫자의 데이터를 보면 숫자에 e가 표함된 지수 표기식 데이터를 받는 경우가 있습니다. 이는 “지수 표기법” 또는 “과학적 표기법” 이라고도 하며 보통 숫자 다음에 e와 함께 지수를 표기합니다. 예를 들어, 1,000,0001.0e+6으로 표기될 수 있습니다.

e 지수로 표현되는 기준

일반적으로 IEEE 754 표준을 따르는 더블 타입은 1e-16 이하 또는 1e+16 이상인 대략적으로 16자리 이상인 경우 e 지수 형태로 표기될 가능성이 높습니다. 하지만 이는 숫자 타입의 구현 방식과 사용되는 언어나 프로그램에 따라 다를 수 있으므로 정확한 기준은 없습니다. 빅 데시멀의 경우에는 소수점 이하 6자리 이상일 때 e 지수 형태로 표기됩니다.

  • Java: 더블 타입의 경우 4.9e-324(10의 -324승) 미만 또는 1.79769e+308(10의 308승) 초과 이거나 16자리 이상인 경우
  • Python: 더블 타입의 경우 2.22507e-308(10의 -308승) 미만 또는 1.79769e+308(10의 308승) 초과 이거나 16자리 이상인 경우
  • JavaScript/Node.js: V8엔진을 기반으로 동작하며 5e-324(10의 -324승) 미만 또는 1.8e+308(10의 308승) 초과 이거나 16자리 이상인 경우

자바에서 e 지수로 표기될 가능성이 있는 더블 타입의 일부 숫자 예시입니다.

  • 4.9e-324
  • 1.7976931348623157e+308
  • 1.0000000000000002e-14
  • 9.999999999999998e+15
  • 1.0000000000000001e+16
  • 9.223372036854776e+18
  • 1.2345678901234567e+18
  • 9.999999999999997e+18
  • 1.2345678901234567e+19
  • 9.999999999999998e+19
  • 1.2345678901234567e+20
  • 9.999999999999998e+20
  • 1.2345678901234567e+21

다음은 보다 짧은 자릿수에도 e 지수 표기법으로 표현되는 빅 데시멀 타입의 일부 숫자 예시입니다.

  • 1.234567e-4
  • 9.8765432e+5
  • 1.23e-8
  • 9.87e-10
  • 7.654321e+5
  • 9.87654e-4
  • 1.23456789e+8
  • 3.21e-9
  • 4.321e+3
  • 7.89e-9

결론

소수점 이하 자릿수가 중요한 분야에서 백엔드가 자바로 작성되어 있다면 빅 데시멀 타입을 주로 사용하게 될 것입니다. 위의 e 지수 표기법으로 표현되는 숫자 예시처럼 빅 데시멀은 더블 타입보다 소수점 이하 자릿수가 적은 숫자에서도 e 지수 형태로 표기되므로 소수점 표기 변환이 필요한 경우가 더 많이 발생 할 것입니다.

if (item.rate.toString().indexOf('e-') > -1) {
    const points = item.rate.toString().split('e-')
    let decimal = 0
    if (points[1]) {
        decimal += Number(points[1])
    }
    if (points[0].split('.').length === 2) {
        decimal += points[0].split('.')[1].length
    }
    item.rate = item.rate.toFixed(decimal)
}
return item

이 코드는 소수점 이하 자릿수가 큰 데이터에서 e 지수 형태로 표기될 가능성이 있는 숫자를 소수점 형태로 변환하기 위해 작성한 코드입니다. 먼저 indexOf 메소드를 이용하여 해당 숫자가 e-를 포함하고 있는지를 검사하고, split 메소드를 이용하여 e-를 기준으로 문자열을 분리합니다. 분리된 두 번째 요소에서 소수점 이하 자릿수를 추출하고 첫 번째 요소에서 split 메소드를 이용하여 정수부분과 소수점 이하 부분을 분리하여 소수점 이하 자릿수를 계산합니다. 계산된 소수점 이하 자릿수를 toFixed 메소드를 이용하여 소수점 자릿수를 맞춘 숫자로 변환하고 item.rate에 다시 할당합니다.

예를 들어, 1.234567e-4는 소수점 형태로 변환하면 0.0001234567입니다. 이렇게 e 지수 형태로 표현된 숫자를 소수점 형태로 변환하여 더욱 직관적으로 사용자가 이해할 수 있도록 했습니다.

결론적으로, 빅 데시멀과 지수 표기법은 부동 소수점 타입의 한계를 극복하기 위한 대안입니다. 백엔드에서 전달받은 데이터의 타입과 형태를 이해하고 적절한 처리를 수행하여 정확한 데이터 표현과 계산을 보장하는 것이 중요합니다. 프론트엔드 개발자는 이러한 이슈들을 고려하여 데이터 처리 및 표현 방법을 결정해야 하며, 데이터의 정확성과 사용자 경험을 고려하여 적절한 방법을 선택해야 합니다.