필요한 경우 대부분의 소수점 2 자리로 둥글는 방법은 무엇입니까?


질문

 

나는 대부분의 소수점 2 자리에서 둥글고 필요할 경우에만 원하는 것입니다.

입력:

10
1.7777777
9.1

산출:

10
1.78
9.1

JavaScript에서 어떻게 할 수 있습니까?


답변

 

math.round ()를 사용하십시오.

Math.round(num * 100) / 100

또는보다 구체적이며 1.005 라운드와 같은 것을 보장하기 위해 NUMBER.EPSILON을 사용하십시오.

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 * 100)/100 // Returns 1 instead of expected 1.01!

tofixed ()는 경우에 따라 (크롬 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가 실제로 실제로 장면 뒤에있는 float 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는 프런트 엔드 계산을 방지하고 백엔드 서버에서 둥근 값을 가져 오는 것입니다.

편집 : 총알 증명이 아닌 또 다른 가능한 해결책도 있습니다.

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

어떤 경우에는 1.3549999999999998과 같은 숫자를 둥글면 잘못된 결과가 반환됩니다.1.35이어야하지만 결과는 1.36입니다.



답변

당신이 사용할 수있는

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을 알고있는 것 같습니다.

또한 이것은 어떤 사람들과 같은 자바 스크립트 이상이 아니라는 것을 주목할 가치가 있습니다.

그것은 단순히 부동 소수점 숫자가 컴퓨터에서 작동하는 방식입니다.프로그래밍 언어의 99 %와 마찬가지로 JavaScript에는 집에서 만든 부동 소수점 숫자가 없습니다.그것은 CPU / FPU에 의존합니다.컴퓨터는 바이너리를 사용하고 바이너리에서는 0.1과 같은 숫자가 없지만 이번 단전 근사.왜요?1/3 이외의 동일한 이유로 10 진수로 작성할 수 없습니다. 그 값은 0.33333333 ... Threes의 무한으로.

여기 번호가 있습니다. epsilon.그 숫자는 두 배 정밀도 부동 소수점 숫자에 존재하는 다음 번호 사이의 차이입니다.그것이 다음과 같습니다. 1 + 1 + NUMBER.EPSILON 사이에는 숫자가 없습니다.

편집하다:

댓글에 묻는 질문은 다음 사항을 명확히합시다. 숫자를 추가하는 것은 부동 소수점 오류 델타를 삼킬 수 있으므로 둥근 값이 산술 작업의 결과 인 경우에만 관련이 있습니다.

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

편집 (2019) :

@maganap과 같은 일부 사람들은 곱하기 전에 숫자를 추가하는 것이 가장 좋습니다.

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

편집 (2019 년 12 월) :

최근에는 epsilon-aware를 비교하기 위해이 방법과 유사한 함수를 사용합니다.

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 ;
}

내 유스 케이스는 수년 동안 개발중인 어설 션 + 데이터 유효성 검사 LIB입니다.

실제로, 나는 espilon_rate = 1 + 4 * number.epsilon 및 epsilon_zero = 4 * number.min_value (엡실론의 네 번 엡실론의 네 번)를 사용하고 있기 때문에 부동 소수점 오류를 누적 할만 큼 평등 검사기가 느슨합니다.

지금까지, 그것은 나를 위해 완벽한 것처럼 보입니다. 나는 그것이 도움이되기를 바랍니다.



답변

이 질문은 복잡합니다.

인수로 float를 취하고 소수점 2 자리로 반올림 된 값을 반환하는 함수, RoundTo2DP (num)가 있다고 가정합니다.이 표현들 각각은 무엇을 평가해야합니까?

roundto2dp (0.0149999999999999999999999999999999) roundto2dp (0.0150000000000000001) roundto2dp (0.015)

'명백한'대답은 첫 번째 예가 0.01로 반올림해야한다는 것입니다 (0.0150000000000000001이 0.02보다 0.02보다 더 가깝기 때문에 0.015가 있기 때문에 0.01에서 0.01보다 가깝기 때문에).그 (것)들은 그러한 숫자가 둥글게되는 수학적 협약이있다).

짐작할 수있는 캐치는 RoundTo2DP가 그 분명한 답변을 제공하기 위해 RoundTo2DP가 구현 될 수 없습니다.IEEE 754 이진 부동 소수점 숫자 (JavaScript에서 사용하는 종류)는 대부분의 비 정수 숫자를 나타낼 수 없으므로 위의 세 숫자 리터럴 모두가 근처의 유효한 부동 소수점 숫자로 둥글게됩니다.이 숫자는 일어납니다

0.0149999999999999999999999999999999999999999999999999999999999999999999999448888888888484876874217297881841659545898437589988984375

0.01에서 0.02보다 더 가깝습니다.

세 가지 숫자 모두 브라우저 콘솔, 노드 쉘 또는 기타 JavaScript 인터프리터에서 동일한 것을 알 수 있습니다.그냥 비교해보십시오 :

> 0.014999999999999999 === 0.0150000000000000001
true

그래서 M = 0.0150000000000000001을 쓸 때, 내가 끝나는 m의 정확한 값은 0.02보다 더 가깝습니다.그리고 아직, 내가 문자열을 변환하면 ...

> 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- 소수의 숫자가이 모든 수치가 정확히 동일하다고 말했다.그래서 어두운 마술은 무엇입니까?

답변은 7.1.12.1 절의 eCMAScript 사양에서 찾을 수 있습니다 : 숫자 유형에 tostring이 적용됩니다.여기서 일부 숫자를 문자열로 변환하기위한 규칙은 해제됩니다.키 부분은 정수가 m의 문자열 표현에서 숫자가 사용될 정수가 생성되는 점 5입니다.

n, k 및 s가 k ≥ 1, 10k-1 ≤ s <10k, s × 10n-k의 숫자 값이 m이고 k는 가능한 한 작게 작아야한다.k는 S의 소수 표현의 자릿수이며, S는 10만큼 나눌 수 없으며, S의 최하위 숫자가 이러한 기준에 의해 반드시 고유하게 결정되는 것은 아닙니다.

여기서 핵심 부분은 "k가 가능한 한 작습니다"요구 사항입니다.그 요구 사항은 숫자 m이 주어지는 것, 문자열 (m)의 값이 숫자 (문자열 (m)) === m에만 해당 요구 사항을 충족시키면서 가장 적은 숫자 수를 가져야 할 필요가있는 요구 사항입니다.우리는 이미 0.015 === 0.0150000000000000001을 알고 있기 때문에, 이제는 문자열 (0.0150000000000000001) === '0.015'이유가 중요해야합니다.

물론이 토론 중 어느 것도 RoundTo2DP (m)가 반환 해야하는 것에 직접적으로 답변했습니다.M의 정확한 값이 0.0149999999999999999999999999999999999999944888487687488848768842172978818416954589841695458984375이지만, 그 문자열 표현은 '0.015'이며, 수학적으로, 실질적으로, 철학적으로, 우리가 두 개의 소수점으로 둥글게 될 때 정답은 무엇입니까?

이것에 대한 단일 정답은 없습니다.그것은 귀하의 유스 케이스에 따라 다릅니다.당신은 아마도 문자열 표현을 존중하고 다음과 같은 경우 위쪽으로 둥글게하고 싶습니다.

대표되는 가치는 본질적으로 이산화되어 있습니다.디나르와 같은 3- 십진 화 통화 금액.이 경우 0.015와 같은 숫자의 진정한 값은 0.015이고 0.0149999999 ... 이진 부동 소수점에서 얻는 0.0149999999는 반올림 오류입니다.(물론 많은 사람들은 그러한 가치를 취급하기 위해 소수 라이브러리를 사용해야하며 첫 번째 장소에서 이진 부동 소수점 숫자로이를 나타내지 않아야합니다. 값은 사용자가 입력했습니다.이 경우 다시 가장 가까운 이진 부동 소수점 표현보다 정확한 10 진수가 'true'입니다.

반면에, 당신은 당신의 가치가 본질적으로 지속적으로 지속적으로 지속될 때, 예를 들어 센서에서 읽는 것이라면 가치가있는 경우 아래쪽으로 반올림하고 싶을 것입니다.

이 두 가지 접근법에는 다른 코드가 필요합니다.숫자의 문자열 표현을 존중하기 위해, 우리는 (약간의 합리적으로 미묘한 코드가 틀림없이) 문자열 표현에 직접 동작하는 우리 자신의 반올림을 구현할 수 있습니다.숫자를 둥글게하는 방법을 가르쳐졌습니다.다음은 소수점 이후에 후행 0을 제거하여 "필요할 때만 2 자리의 소수 자릿수를 나타내는 숫자의 요구 사항을 존중하는 예제입니다.물론 정확한 필요에 맞게 조정해야합니다.

/**
 * 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 라이브러리를 시도 할 수 있습니다.)

그러나 두 번째 종류의 번호가있는 경우 - 지속적인 규모에서 취한 값은 소수점이 적은 소수점이 적은 소수점이 더 적은 자원보다 더 정확하다고 생각할 이유는 무엇입니까?이 경우 해당 표현 (사양에서 설명한 것처럼)이 이미 분류되어 있기 때문에 문자열 표현을 존중하고 싶지 않습니다.우리는 "0.014999999 ... 375 둥근 0.02까지 0.02, 0.014999999 ... 375 라운드가 0.02"라는 것을 말하는 것을 실수하고 싶지 않습니다.

여기서 우리는 단순히 내장 된 tofixed 메소드를 사용할 수 있습니다.ToFixed에서 반환 한 문자열에서 number ()를 호출하면 문자열 표현이없는 숫자를 얻습니다 (JavaScript 가이 답변에서 앞에서 설명한 숫자의 문자열 표현을 계산하는 방식 덕분에).

/**
 * 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);
}
출처:https://stackoverflow.com/questions/11832914/how-to-round-to-at-most-2-decimal-places-if-necessary