본문 바로가기
코딩 생활

[프로그래머스 | JS] 레벨 0. 구슬을 나누는 경우의 수 (feat. 소수점 계산 오류)

by everyhahaha 2023. 4. 10.
반응형

❓ 문제: 점의 위치 구하기

머쓱이는 구슬을 친구들에게 나누어주려고 합니다. 구슬은 모두 다르게 생겼습니다. 
머쓱이가 갖고 있는 구슬의 개수 balls와 친구들에게 나누어 줄 구슬 개수 share이 매개변수로 주어질 때, 
balls개의 구슬 중 share개의 구슬을 고르는 가능한 모든 경우의 수를 return 하는 solution 함수를 완성해주세요.

📝 문제 풀이

재귀로 푸는 문제였지만 아직 재귀에 익숙하지 않아

부끄럽지만 힌트에 나온 공식을 그대로 적용해서 풀었다.

내가 참고한 공식

function solution(balls, share) {
    let x = 1;
    let y = 1;
    let z = 1;

    for (let i = 1; i <= balls; i++) x *= i;
    for (let i = 1; i <= (balls - share); i++) y *= i;
    for (let i = 1; i <= share; i++) z *= i;
    
    return x / (y * z);
}

...정말 아름답지 않은 코드....많이 부끄럽다.....

 

그런데 테스트 케이스는 모두 통과하는데 채점이 80점을 넘기지 못했다.

계속 붙잡고 이런저런 시도를 하다가 정말 모르겠어서 질문하기를 보니

 Math.round  를 해줘야한다는 얘기를 보고 리턴값에 추가하니 바로 통과됐다.

통과해서 기분은 좋았지만 정확하게 파악하고 넘어가고자 한다.

function solution(balls, share) {
    let x = 1;
    let y = 1;
    let z = 1;

    for (let i = 1; i <= balls; i++) x *= i;
    for (let i = 1; i <= (balls - share); i++) y *= i;
    for (let i = 1; i <= share; i++) z *= i;
    
    // 기존 코드에 Math.round()만 추가
    return Math.round(x / (y * z));
}

🔍 더 파고들기 - 소수점 오류

우선 문제의 '다른 사람의 풀이'의 댓글에 적혀있는 사이트를 살펴봤다.
정리를 잘 해놓으셔서 이해하기 좋았다.

https://joooing.tistory.com/entry/Javascript-%EC%86%8C%EC%88%98%EC%A0%90floating-point-%EA%B3%84%EC%82%B0-%EC%98%A4%EB%A5%98

 

Javascript 소수점 오류 원인, 해결방안

자바스크립트에서 소수점 숫자 연산을 하면, 생각지도 못한 오류가 발생한다. 아래처럼 콘솔창에 0.1 + 0.2를 입력하면, 0.3이 아니라 0.30000000000000004 가 나오는 걸 확인해볼 수 있다. 이렇게 소수점

joooing.tistory.com

 

더 자세히 알아보니,

자바스크립트에서는 소수점 오류가 생기는 이유는

실수를 이진수로 변환(부동소수점)해서 연산하기 때문이다.

 

10진법으로 계산하는 우리와 다르게 컴퓨터는 2진법으로 연산하는데,

2진수로 정확하게 표현할 수 없는 일부 10진수 값들은 정확한 값이 아닌 근사값으로 저장돼

반올림 오차가 생기기 때문이다.

 

예를 들어, 우리가 생각하기에 0.1 * 10 은 1이지만

부동소수점 형식, 즉, 2진수로 0.1을 정확히 나타낼 수 없기 때문에 생기는 반올림 오차로

1에 도달하지 못하고 0.9999999999999999와 같은 값이 나올 수 있다.

(물론 컴퓨터의 메모리 한계로 소수점 아래가 자릿수가 무한대로 생기진 않고 유한소수로 저장한다)

 

이렇듯 연산을 위한 10진수 → 2진수 변환 과정에서 정수가 소수가 되어 버리면서

우리가 예상하지 못한 오차가 생기는 경우가 생기는 게 바로 소수점 게산 오류 현상이다.


그렇다면 이런 오류를 방지하기 위해서는 어떻게 해야할까?

크게 3가지 방법이 있다.


 

1. 자바스크립트 메소드 사용

 toFixed()  
소수점 이하의 자릿수를 지정해서 결과값을 문자열로 반환한다.

var numObj = 12345.6789;

numObj.toFixed();       // Returns '12346': 별도 값을 주지 않으면 0을 사용한다.
numObj.toFixed(1);      // Returns '12345.7': 소수점 이하 한자리만 남기고 반올림한다.
numObj.toFixed(6);      // Returns '12345.678900': 빈 공간을 0으로 채운다.

→ mdn에서 더 자세히 알아보기

 

Math.round() - JavaScript | MDN

Math.round() 함수는 입력값을 반올림한 수와 가장 가까운 정수 값을 반환합니다.

developer.mozilla.org


 Math.round()  

입력값을 반올림한 수와 가장 가까운 정수 값을 반환한다.

Math.round( 20.49);    //  20
Math.round( 20.5 );    //  21
Math.round( 42   );    //  42
Math.round(-20.5 );    // -20
Math.round(-20.51);    // -21

→ mdn에서 더 자세히 알아보기

 

Math.round() - JavaScript | MDN

Math.round() 함수는 입력값을 반올림한 수와 가장 가까운 정수 값을 반환합니다.

developer.mozilla.org


2. 가급적 정수형 사용

계산할 때부터 정수형을 사용하는 방법도 가능하다.

// 소수점 아래 길이에 맞춰 정수로 변환
let num1 = 0.1;
let num2 = 0.2;
let result = (num1 * 10 + num2 * 10) / 10;
console.log(result); // 0.3

3. 기타 라이브러리 사용

▶ decimal.js

JavaScript에서 소수점 연산을 위한 라이브러리로 toFixed()와 같은 내장 함수에서 발생하는 소수점 오류를 방지할 수 있다.
대규모 숫자의 연산도 지원하고, 자바스크립트의 Number 타입으로 표현할 수 없는 숫자도 정확하게 처리할 수 있다.

→ decimal.js에 대해 더 자세히 알아보기

 

decimal.js API

decimal.js An arbitrary-precision Decimal type for JavaScript. Hosted on GitHub. The library is incorporated into this page, so it should be available in the console now. API See the README on GitHub for a quick-start introduction. In all examples below, v

mikemcl.github.io


▶ big.js

자바스크립트의 기본적인 숫자 연산 방식을 개선하여 소수점 오류를 해결할 수 있는 라이브러리.
숫자를 문자열 형태로 다루기 때문에, 오차 없는 숫자 계산이 가능합니다.

→ big.js에 대해 더 자세히 알아보기

 

big.js API

big.js A small, fast, easy-to-use library for arbitrary-precision decimal arithmetic. Hosted on GitHub. The library is incorporated into this page, so it should be available in the console now. API For brevity, var, semicolons and toString calls have been

mikemcl.github.io

둘 다 많이 사용되는 라이브러리이며, 깃헙 자료 보시고

프로젝트에 따라 적절한 라이브러리를 선택하시면 될 듯 하다.

 

 

 

현재 21,700위........ 쭉 가보자

모두 즐코하세요-!

반응형

댓글