작콩큰콩

[자바스크립트 JavaScript] 소수점 계산 오류 본문

작은 콩 개발자/JS

[자바스크립트 JavaScript] 소수점 계산 오류

귤치치 2024. 5. 29. 14:44
자바스크립트로 소수점을 계산한 경우

아주 놀랍게도 00000000004를 볼 수 있다. 즉 더하기가 잘 안 된다는 소리 더하기도 안 되는데 곱하기 하면 난리 날 것이다.

parseFloat 같은 거 쓴다고 되는게 아니라고 하셨다.

let a = 0.1;
let b = 0.2;
console.log(parseFloat(a) + parseFloat(b)); // 0.30000000000000004

 

원인

컴퓨터는 0과 1로만 이루어져서 우리가 사용하는 10진법과는 다르게 컴퓨터는 2진법을 사용해 계산을 한다.

몇몇 소수의 경우에는 10진법을 2진법으로 변환할 때 무한소수가 되어버리는데, 유한한 컴퓨터의 자원을 활용하는 프로그램은 당연히 저장공간의 한계로 인해 결국 무한 소수를 유한한 수로 바꾸게 되고, 그 과정에서 나타나는 미세한 오차가 계산의 오류를 범하게 하는 것이라고 한다. 그래서 어느 순간에 값을 자른 것 같다.

  • 더 상세한 내용 - 인용해 왔는데 좀 더 공부해야 할 것 같다.
    • 근본을 따지자면 사실 숫자는 내부적으로 64비트 형식 IEEE-754으로 표현되기 때문에 숫자를 저장하려면 정확히 64비트가 필요하다. 64비트 중 52비트는 숫자를 저장하는 데 사용되고, 11비트는 소수점 위치를(정수는 0), 1비트는 부호를 저장하는 데 사용된다. 그런데 너무 큰 수는 표현하는 데 64비트를 초과하기 때문에 Infinity로 처리 된다.

해결 방안

  • 박이사님께서 알려주신 방법

1.SQL단에서 계산해서 가져오기

  1. SQL 에서는 계산하지 않고 java 단에서 BigDecimal 로 계산해서 가져오기
    • 공식문서에선 BigDecimal을 불변의 성질을 띠며, 임의 정밀도와 부호를 지니는 10진수라고 표현합니다.
    • 아래 활용하는 코드
import java.math.BigDecimal;

public class study {
	
	public static void number() {
		BigDecimal a = new BigDecimal(0.1);
		System.out.println(a);
		
		BigDecimal b = new BigDecimal("0.1");
		System.out.println(b);
		
		BigDecimal c = new BigDecimal("0.2");
		System.out.println(c);
		
		System.out.println("\n" +"사칙연산");
		System.out.println(b.add(c));
		System.out.println(b.subtract(c));
		System.out.println(b.multiply(c));
		System.out.println(b.divide(c));
		// 주목할 부분은 나눗셈입니다. 
		// 기본적으로 BigDecimal은 소수점 처리(rounding)을 하지 않고 정확한 몫을 반환합니다. 
		// 만약 1 나누기 3처럼 몫의 값이 무한소수인 경우(Non-terminating decimal expansion)엔 ArithmeticException이 발생합니다.
		
		System.out.println("");
		System.out.println(BigDecimal.valueOf(1.12));
		
	};

	public static void main(String[] args) {
		
		number();
		
	}

}

3. javascript 라이브러리(Math.js) 등을 이용하여 계산하기

이 방법은 모르겠네… 이런 상태인데??

  • Math.floor() : 괄호 안의 숫자를 내림
  • Math.ceil() : 괄호 안의 숫자를 올림
  • Math.round() : 괄호 안의 숫자를 반올림
  • Math.abs() : 괄호 안의 숫자의 절대값
  • Math.sqrt() : 괄호 안의 숫자의 루트 값(제곱근)
  • Math.pow() : 괄호 안의 첫 번째 숫자가 밑, 두 번째 숫자가 지수(거듭제곱)
  • Math.random() : 0이상 1미만의 값 랜덤 리턴
conole.log(Math.floor(10.99));    // 10
conole.log(Math.ceil(99.1));      // 100
conole.log(Math.round(99.6));     // 100
conole.log(Math.abs(-50));        // 50
conole.log(Math.abs(50));         // 50
conole.log(Math.sqrt(2));         // 1.4142135623730951

4. post 를 이용하든 다른 방법으로 하든 java 단에서 BigDecimal로 계산해서 가져오기

  • 2번과 동일한 상태이다.

5. 그 외

  • 이사님께서 parseFloat(value).toFixed(4) 이렇게 했을 때 0.300000000000004 나왔는데 4자리 짤라서 쓰는건 찝찝하지 않는가? 라고 하셨다.
Comments