[javascript] 소수점 이하 2 자리까지 반올림 (필요한 경우에만)

소수점 이하 두 자리까지 반올림하고 싶지만 필요한 경우에만 .

입력:

10
1.7777777
9.1

산출:

10
1.78
9.1

JavaScript로 어떻게 할 수 있습니까?



답변

사용하다 Math.round(num * 100) / 100

편집 : 1.005와 같은 것을 올바르게 반올림하기 위해

Math.round((num + Number.EPSILON) * 100) / 100


답변

값이 텍스트 유형 인 경우 :

parseFloat("123.456").toFixed(2);

값이 숫자 인 경우 :

var numb = 123.23454;
numb = numb.toFixed(2);

1.5와 같은 값은 “1.50”을 출력으로 제공한다는 단점이 있습니다. @minitech가 제안한 수정 사항 :

var numb = 1.5;
numb = +numb.toFixed(2);
// Note the plus sign that drops any "extra" zeroes at the end.
// It changes the result (which is a string) into a number again (think "0 + foo"),
// which means that it uses only as many digits as necessary.

Math.round더 나은 솔루션 인 것 같습니다 . 그러나 그렇지 않습니다! 어떤 경우에는 올바르게 반올림 되지 않습니다 .

Math.round(1.005 * 1000)/1000 // Returns 1 instead of expected 1.01!

경우에 따라 toFixed ()도 올바르게 반올림 되지 않습니다 (Chrome v.55.0.2883.87에서 테스트)!

예 :

parseFloat("1.555").toFixed(2); // Returns 1.55 instead of 1.56.
parseFloat("1.5550").toFixed(2); // Returns 1.55 instead of 1.56.
// However, it will return correct result if you round 1.5551.
parseFloat("1.5551").toFixed(2); // Returns 1.56 as expected.

1.3555.toFixed(3) // Returns 1.355 instead of expected 1.356.
// However, it will return correct result if you round 1.35551.
1.35551.toFixed(2); // Returns 1.36 as expected.

내 생각에 이것은 1.555가 실제로 배후에 1.55499994 인 플로트와 같은 것이기 때문입니다.

해결 방법 1 은 필요한 반올림 알고리즘이있는 스크립트를 사용하는 것입니다. 예를 들면 다음과 같습니다.

function roundNumber(num, scale) {
  if(!("" + num).includes("e")) {
    return +(Math.round(num + "e+" + scale)  + "e-" + scale);
  } else {
    var arr = ("" + num).split("e");
    var sig = ""
    if(+arr[1] + scale > 0) {
      sig = "+";
    }
    return +(Math.round(+arr[0] + "e" + sig + (+arr[1] + scale)) + "e-" + scale);
  }
}

https://plnkr.co/edit/uau8BlS1cqbvWPCHJeOy?p=preview

참고 : 이것은 모든 사람에게 보편적 인 솔루션은 아닙니다. 몇 가지 다른 반올림 알고리즘이 있으며 구현이 다를 수 있으며 요구 사항에 따라 다릅니다. https://en.wikipedia.org/wiki/Rounding

해결 방법 2 는 프런트 엔드 계산을 피하고 백엔드 서버에서 반올림 된 값을 가져 오는 것입니다.


답변

당신이 사용할 수있는

function roundToTwo(num) {    
    return +(Math.round(num + "e+2")  + "e-2");
}

나는 이것을 MDN 에서 발견했다 . 그들의 방법은 언급 된 1.005의 문제를 피합니다 .

roundToTwo(1.005)
1.01
roundToTwo(10)
10
roundToTwo(1.7777777)
1.78
roundToTwo(9.1)
9.1
roundToTwo(1234.5678)
1234.57


답변

MarkG의 답변이 맞습니다. 소수점 이하 자릿수에 대한 일반적인 확장은 다음과 같습니다.

Number.prototype.round = function(places) {
  return +(Math.round(this + "e+" + places)  + "e-" + places);
}

용법:

var n = 1.7777;    
n.round(2); // 1.78

단위 테스트 :

it.only('should round floats to 2 places', function() {

  var cases = [
    { n: 10,      e: 10,    p:2 },
    { n: 1.7777,  e: 1.78,  p:2 },
    { n: 1.005,   e: 1.01,  p:2 },
    { n: 1.005,   e: 1,     p:0 },
    { n: 1.77777, e: 1.8,   p:1 }
  ]

  cases.forEach(function(testCase) {
    var r = testCase.n.round(testCase.p);
    assert.equal(r, testCase.e, 'didn\'t get right number');
  });
})


답변

다음을 사용해야합니다.

Math.round( num * 100 + Number.EPSILON ) / 100

아무도 모르는 것 같습니다 Number.EPSILON.

또한 이것은 일부 사람들이 언급 한 것처럼 이상한 JavaScript 가 아니라는 점에 주목할 가치가 있습니다.

그것은 컴퓨터에서 부동 소수점 숫자가 작동하는 방식입니다. 프로그래밍 언어의 99 %와 마찬가지로 JavaScript에는 집에서 만든 부동 소수점 숫자 가 없습니다 . 이를 위해 CPU / FPU에 의존합니다. 컴퓨터는 이진법을 사용하며, 이진법에는와 같은 숫자가 0.1없지만 단순한 이진법이 있습니다. 왜? 같은 이유로 1/3보다 10 진수로 쓸 수 없습니다. 값은 0.33333333입니다. 무한대는 3입니다.

여기로 오세요 Number.EPSILON. 이 숫자는 배정 밀도 부동 소수점 숫자에 존재하는 1과 다음 숫자 의 차이 입니다. 그게 다야 : 1와 1 + 사이에는 숫자가 없습니다 Number.EPSILON.

편집하다:

주석에서 요청했듯이 한 가지를 분명히합시다. Number.EPSILON반올림 할 값이 부동 소수점 오류 델타를 삼킬 수 있기 때문에 반올림 값이 산술 연산의 결과 일 때만 관련이 있습니다.

값이 직접 소스 (예 : 리터럴, 사용자 입력 또는 센서)에서 오는 경우에는 유용하지 않습니다.

편집 (2019) :

@maganap과 일부 사람들이 지적했듯이 Number.EPSILON곱하기 전에 추가하는 것이 가장 좋습니다 .

Math.round( ( num + Number.EPSILON ) * 100 ) / 100

편집 (2019 년 12 월) :

최근에 나는 엡실론을 인식하는 숫자를 비교하기 위해이 기능과 비슷한 기능을 사용합니다.

const ESPILON_RATE = 1 + Number.EPSILON ;
const ESPILON_ZERO = Number.MIN_VALUE ;

function epsilonEquals( a , b ) {
  if ( Number.isNaN( a ) || Number.isNaN( b ) ) {
    return false ;
  }
  if ( a === 0 || b === 0 ) {
    return a <= b + EPSILON_ZERO && b <= a + EPSILON_ZERO ;
  }
  return a <= b * EPSILON_RATE && b <= a * EPSILON_RATE ;
}

내 유스 케이스는 몇 년 동안 개발중인 주장 + 데이터 유효성 검사 라이브러리 입니다.

실제로, 부동 소수점 오류를 누적하기 위해 동등성 검사기를 느슨하게 원하기 때문에 내가 사용하는 코드에서 (엡실론의 네 배) 사용 ESPILON_RATE = 1 + 4 * Number.EPSILON하고 EPSILON_ZERO = 4 * Number.MIN_VALUE있습니다.

지금까지는 나에게 완벽 해 보입니다. 도움이 되길 바랍니다.


답변

하나를 사용할 수 있습니다 .toFixed(NumberOfDecimalPlaces).

var str = 10.234.toFixed(2); // => '10.23'
var number = Number(str); // => 10.23


답변

이 질문은 복잡합니다.

roundTo2DP(num)float을 인수로 사용하고 소수점 이하 2 자리로 반올림 한 값을 반환 하는 함수가 있다고 가정 합니다. 이러한 각 표현은 무엇을 평가해야합니까?

  • roundTo2DP(0.014999999999999999)
  • roundTo2DP(0.0150000000000000001)
  • roundTo2DP(0.015)

‘명백한’답변은 첫 번째 예제는 0.01로 반올림해야하고 (0.02보다 0.01에 가깝기 때문에) 다른 두 개는 0.02로 반올림해야합니다 (0.0150000000000000001은 0.01보다 0.02에 가깝기 때문에 0.015는 정확히 중간에 있기 때문에) 그들과 같은 숫자가 반올림되는 수학적 규칙이 있습니다).

당신이 짐작 할 수있다 캐치는 점이다 roundTo2DP 가능하게 할 수 그것을 전달되는 모든 세 개의 숫자이기 때문에, 그 명백한 해답을 제공하도록 구현 될 같은 번호 . IEEE 754 이진 부동 소수점 숫자 (JavaScript에서 사용되는 종류)는 대부분의 정수가 아닌 숫자를 정확하게 나타낼 수 없으므로 위의 세 숫자 리터럴은 모두 근처의 유효한 부동 소수점 숫자로 반올림됩니다. 이 숫자는 발생하는 대로 정확하게

0.01499999999999999944488848768742172978818416595458984375

0.02보다 0.01에 가깝습니다.

브라우저 콘솔, 노드 셸 또는 기타 JavaScript 인터프리터에서 세 숫자가 모두 같은 것을 알 수 있습니다. 그냥 그들을 비교하십시오 :

> 0.014999999999999999 === 0.0150000000000000001
true

제가 글을 쓸 때 m = 0.0150000000000000001, 그로m 끝나는 정확한 가치실제0.01 보다 더 가깝습니다 0.02. 그러나 mString으로 변환 하면 …

> var m = 0.0150000000000000001;
> console.log(String(m));
0.015
> var m = 0.014999999999999999;
> console.log(String(m));
0.015

… 0.015를 얻습니다. 0.02로 반올림해야합니다. 이것은 이전 에이 모든 숫자가 정확히 같았다고 말한 56 세 자리 수 는 아닙니다 . 이게 무슨 마법입니까?

답변은 ECMAScript 사양, 7.1.12.1 : ToString이 Number 유형에 적용됨 섹션에 있습니다. 여기에 일부 숫자 m 을 문자열 로 변환하는 규칙 이 정리되어 있습니다. 핵심 부분은 포인트 5이며, m 의 문자열 표현에 숫자가 사용될 정수 s 가 생성됩니다 .

있도록 N , K를 , 및 s의 정수가되도록 수행 K ≥ 1, 10 K -1S <10 K 에 대한 숫자 값 S × 10 NK는mk는 가능한 한 작게한다. 이 K의 소수 표현 자리수 유의 S , S를 10로 나누어 아니고, 최하위 디지트 것을 반드시 고유 이러한 기준에 의해 결정되지 않는다.

여기서 중요한 부분은 ” k 는 가능한 한 작아야 “한다는 요구 사항입니다 . 그 요구 사항 m의 가치 는 Number가 주어지면 의 요구 사항을 충족하면서도 가능한 최소 자릿수String(m)가져야 한다는 요구 사항입니다 . 우리는 이미 그것을 알고 있기 때문에 , 왜 사실이어야 하는지 분명 합니다.Number(String(m)) === m0.015 === 0.0150000000000000001String(0.0150000000000000001) === '0.015'

물론,이 토론의 아무도는 직접 어떤 대답하지 않았다 roundTo2DP(m) 해야 돌아갑니다. 경우 m‘의 정확한 값이 0.01499999999999999944488848768742172978818416595458984375이지만, 그것의 String 표현은’0.015, 그 다음 무엇이며 올바른 수학적, 실제적으로, 철학적으로, 또는 무엇 이건 – – 대답은 우리는 두 개의 소수 자릿수로 반올림 할 때?

이에 대한 올바른 정답은 없습니다. 사용 사례에 따라 다릅니다. 다음과 같은 경우 문자열 표현을 존중하고 위쪽으로 반올림하려고합니다.

  • 표현되는 가치는 본질적으로 이산 적입니다. 예를 들어 디나르와 같은 3 자리 통화의 통화 금액입니다. 이 경우 0.015와 같은 Number 의 실제 0.015이며, 이진 부동 소수점으로 얻는 0.0149999999 … 표현은 반올림 오류입니다. (물론, 많은 사람들은 그러한 값을 처리하기 위해 10 진수 라이브러리를 사용해야하며 처음에는 이진 부동 소수점 숫자로 나타내지 않아야한다고 합리적으로 주장합니다.)
  • 사용자가 값을 입력했습니다. 이 경우에도 입력 한 정확한 십진수는 가장 가까운 이진 부동 소수점 표현보다 ‘참’입니다.

반면에, 이진 부동 소수점 값을 존중하고 값이 본질적으로 연속적인 스케일 (예 : 센서에서 읽은 값) 인 경우 아래쪽으로 반올림합니다.

이 두 가지 접근 방식에는 다른 코드가 필요합니다. 숫자의 문자열 표현을 존중하기 위해 (합리적으로 약간의 미묘한 코드를 사용하여) 학교에서 사용했을 때와 동일한 알고리즘을 사용하여 숫자로 숫자로 문자열 표현에 직접 작용하는 자체 반올림을 구현할 수 있습니다 숫자를 반올림하는 방법을 배웠습니다. 다음은 소수점 이하의 후행 0을 제거하여 “필요한 경우에만”소수점 이하 2 자리까지 숫자를 나타내는 OP의 요구 사항을 준수하는 예입니다. 물론 정확한 요구에 맞게 조정해야 할 수도 있습니다.

/**
 * Converts num to a decimal string (if it isn't one already) and then rounds it
 * to at most dp decimal places.
 *
 * For explanation of why you'd want to perform rounding operations on a String
 * rather than a Number, see http://stackoverflow.com/a/38676273/1709587
 *
 * @param {(number|string)} num
 * @param {number} dp
 * @return {string}
 */
function roundStringNumberWithoutTrailingZeroes (num, dp) {
    if (arguments.length != 2) throw new Error("2 arguments required");

    num = String(num);
    if (num.indexOf('e+') != -1) {
        // Can't round numbers this large because their string representation
        // contains an exponent, like 9.99e+37
        throw new Error("num too large");
    }
    if (num.indexOf('.') == -1) {
        // Nothing to do
        return num;
    }

    var parts = num.split('.'),
        beforePoint = parts[0],
        afterPoint = parts[1],
        shouldRoundUp = afterPoint[dp] >= 5,
        finalNumber;

    afterPoint = afterPoint.slice(0, dp);
    if (!shouldRoundUp) {
        finalNumber = beforePoint + '.' + afterPoint;
    } else if (/^9+$/.test(afterPoint)) {
        // If we need to round up a number like 1.9999, increment the integer
        // before the decimal point and discard the fractional part.
        finalNumber = Number(beforePoint)+1;
    } else {
        // Starting from the last digit, increment digits until we find one
        // that is not 9, then stop
        var i = dp-1;
        while (true) {
            if (afterPoint[i] == '9') {
                afterPoint = afterPoint.substr(0, i) +
                             '0' +
                             afterPoint.substr(i+1);
                i--;
            } else {
                afterPoint = afterPoint.substr(0, i) +
                             (Number(afterPoint[i]) + 1) +
                             afterPoint.substr(i+1);
                break;
            }
        }

        finalNumber = beforePoint + '.' + afterPoint;
    }

    // Remove trailing zeroes from fractional part before returning
    return finalNumber.replace(/0+$/, '')
}

사용법 예 :

> roundStringNumberWithoutTrailingZeroes(1.6, 2)
'1.6'
> roundStringNumberWithoutTrailingZeroes(10000, 2)
'10000'
> roundStringNumberWithoutTrailingZeroes(0.015, 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes('0.015000', 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes(1, 1)
'1'
> roundStringNumberWithoutTrailingZeroes('0.015', 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes(0.01499999999999999944488848768742172978818416595458984375, 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes('0.01499999999999999944488848768742172978818416595458984375', 2)
'0.01'

위의 기능은 사용자가 입력 한 숫자가 잘못 반올림되는 것을 방지하기 위해 사용하려는 것일 수 있습니다.

(대안으로, 다른 구현으로 비슷하게 동작하는 기능을 제공하는 round10 라이브러리를 사용해 볼 수도 있습니다 .)

그러나 두 번째 종류의 숫자-연속 스케일에서 가져온 값을 사용하면 소수점 이하 자릿수가 적은 근사 소수를 더 많이 나타내는 것보다 더 정확 하다고 생각할 이유가 없습니다 . 이 경우, 우리 String 표현을 존중하고 싶지 않습니다. 그 표현은 (사양에서 설명 된 바와 같이) 이미 반올림 되었기 때문입니다. “0.014999999 … 375는 0.015로 반올림하여 0.02로 반올림하므로 0.014999999 … 375는 0.02로 반올림”하는 실수를 저지르고 싶지 않습니다.

여기서 우리는 단순히 내장 toFixed방법을 사용할 수 있습니다 . 에 의해 Number()반환 된 String 을 호출 하면 String toFixed표현에 후행 0이없는 Number를 얻습니다 (JavaScript 가이 답변의 앞부분에서 설명한 Number의 String 표현을 계산하는 방식 덕분입니다).

/**
 * Takes a float and rounds it to at most dp decimal places. For example
 *
 *     roundFloatNumberWithoutTrailingZeroes(1.2345, 3)
 *
 * returns 1.234
 *
 * Note that since this treats the value passed to it as a floating point
 * number, it will have counterintuitive results in some cases. For instance,
 *
 *     roundFloatNumberWithoutTrailingZeroes(0.015, 2)
 *
 * gives 0.01 where 0.02 might be expected. For an explanation of why, see
 * http://stackoverflow.com/a/38676273/1709587. You may want to consider using the
 * roundStringNumberWithoutTrailingZeroes function there instead.
 *
 * @param {number} num
 * @param {number} dp
 * @return {number}
 */
function roundFloatNumberWithoutTrailingZeroes (num, dp) {
    var numToFixedDp = Number(num).toFixed(dp);
    return Number(numToFixedDp);
}