필요한 경우에만 소수점 이하 2자리까지 반올림하고 싶습니다.
입력:
10 1.7777777 9.1
산출:
10 1.78 9.1
JavaScript에서 이 작업을 수행하려면 어떻게 해야 합니까?
질문자 :stinkycheeseman
필요한 경우에만 소수점 이하 2자리까지 반올림하고 싶습니다.
입력:
10 1.7777777 9.1
산출:
10 1.78 9.1
JavaScript에서 이 작업을 수행하려면 어떻게 해야 합니까?
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 * 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가 실제로 무대 뒤에서 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
모르는 것 같습니다.
또한 이것이 일부 사람들이 말한 것처럼 JavaScript의 이상함 이 아니라는 점에 주목할 가치가 있습니다.
그것은 단순히 부동 소수점 숫자가 컴퓨터에서 작동하는 방식입니다. 프로그래밍 언어의 99%와 마찬가지로 JavaScript에는 집에서 만든 부동 소수점 숫자가 없습니다. 이를 위해 CPU/FPU에 의존합니다. 컴퓨터는 이진법을 사용하고 이진법에는 0.1
과 같은 숫자가 없지만 이에 대한 이진 근사치일 뿐입니다. 왜요? 같은 이유로 1/3은 십진수로 쓸 수 없습니다. 그 값은 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
(엡실론의 4배)를 사용하고 있습니다. 부동 소수점 오류를 누적하기에 충분히 느슨한 평등 검사기를 원하기 때문입니다.
지금까지는 나에게 완벽해 보인다. 도움이 되기를 바랍니다.
이 질문은 복잡합니다.
float를 인수로 사용하고 소수점 이하 2자리로 반올림된 값을 반환하는 roundTo2DP(num)
함수가 있다고 가정합니다. 이러한 각 표현식은 무엇으로 평가되어야 합니까?
roundTo2DP(0.014999999999999999)
roundTo2DP(0.0150000000000000001)
roundTo2DP(0.015)
'명백한' 대답은 첫 번째 예는 0.01로 반올림되어야 하고(0.02보다 0.01에 더 가깝기 때문에) 다른 두 예는 0.02로 반올림되어야 한다는 것입니다(0.0150000000000000001은 정확히 0.01보다 0.02에 더 가깝기 때문입니다. 그리고 그러한 숫자가 반올림되는 수학적 규칙이 있습니다).
당신이 짐작 할 수있다 캐치는 점이다 roundTo2DP
자신에게 전달 모두 세 개의 숫자가 같은 수 있기 때문에 아마도 그 명백한 해답을 제공하도록 구현 될 수 없습니다. IEEE 754 이진 부동 소수점 숫자(JavaScript에서 사용하는 종류)는 정수가 아닌 대부분의 숫자를 정확하게 나타낼 수 없으므로 위의 세 숫자 리터럴은 모두 가까운 유효한 부동 소수점 숫자로 반올림됩니다. 이 숫자는 정확히
0.014999999999999999944488848768742172978818416595458984375
이는 0.02보다 0.01에 더 가깝습니다.
세 개의 숫자가 모두 브라우저 콘솔, 노드 셸 또는 기타 JavaScript 인터프리터에서 동일한 것을 볼 수 있습니다. 그냥 비교하십시오:
> 0.014999999999999999 === 0.0150000000000000001 true
내가 쓸 때 그래서 m = 0.0150000000000000001
의 정확한 값 m
나는 가까이에있다 끝낼 것을 0.01
가에보다 0.02
. m
을 문자열로 변환하면 ...
> 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 을 문자열로 변환하는 규칙이 나와 있습니다. 핵심 부분은 m 의 문자열 표현에 사용될 숫자 의 정수 s 가 생성되는 포인트 5입니다.
n , k 및 s 를 k ≥ 1, 10 k -1 ≤ s < 10 k , s × 10 n - k 의 숫자 값이 m , k 가 가능한 한 작은 정수가 되도록 둡니다. k는 s의 (S)의 십진 표현에 자릿수로되어 있습니다 10의 배수되지 않고, S의 최하위 디지트는 반드시 이러한 기준 의적으로 결정되지 않는다는 것을.
여기서 중요한 부분은 " k 는 가능한 한 작다"는 요구 사항입니다. 무엇에 그 요구 사항은 양하는 것은 번호를 지정해, 그 요구 사항입니다 m
의 값 String(m)
여전히 요구 사항을 만족시키면서 숫자의 최소한의 번호가 있어야하는 Number(String(m)) === m
. 우리는 이미 0.015 === 0.0150000000000000001
String(0.0150000000000000001) === '0.015'
가 참이어야 하는 이유가 이제 명확해졌습니다.
roundTo2DP(m)
이 반환해야 하는 것에 대해 직접적으로 대답하지 않았습니다. m
의 정확한 값이 0.0149999999999999944488848768742172978818416595458984375 이지만 String 표현이 '0.015'인 경우 수학적으로 두 자리 반올림 또는 반올림 하면 정확한 답은 무엇입니까?
이에 대한 정답은 없습니다. 사용 사례에 따라 다릅니다. 다음과 같은 경우 String 표현을 존중하고 위쪽으로 반올림하고 싶을 것입니다.
반면에 값이 본질적으로 연속적인 스케일에서 나온 경우(예: 센서에서 읽은 값인 경우) 이진 부동 소수점 값을 존중하고 아래로 반올림하기를 원할 수 있습니다.
이 두 가지 접근 방식에는 서로 다른 코드가 필요합니다. 숫자의 문자열 표현을 존중하기 위해 학교에서 사용했을 때와 동일한 알고리즘을 사용하여 숫자별로 문자열 표현에 직접 작용하는 자체 반올림을 구현할 수 있습니다. 숫자를 반올림하는 방법을 배웠습니다. 다음은 소수점 뒤의 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
메서드를 간단히 사용할 수 있습니다. toFixed
에서 반환된 String 에서 Number()
를 호출하면 String 표현에 후행 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); }
.toFixed()
및 .toPrecision()
고려:
.toFixed(NumberOfDecimalPlaces)
사용할 수 있습니다.
var str = 10.234.toFixed(2); // => '10.23' var number = Number(str); // => 10.23
일반적으로 십진 반올림은 다음과 같이 스케일링하여 수행됩니다. round(num * p) / p
순진한 구현
중간 숫자와 함께 다음 함수를 사용하면 예상한 대로 위쪽 반올림된 값을 얻거나 입력에 따라 더 낮은 반올림된 값을 얻을 수 있습니다.
이러한 inconsistency
로 인해 클라이언트 코드에서 버그를 감지하기 어려울 수 있습니다.
function naiveRound(num, decimalPlaces = 0) { var p = Math.pow(10, decimalPlaces); return Math.round(num * p) / p; } console.log( naiveRound(1.245, 2) ); // 1.25 correct (rounded as expected) console.log( naiveRound(1.255, 2) ); // 1.25 incorrect (should be 1.26) // testing edge cases console.log( naiveRound(1.005, 2) ); // 1 incorrect (should be 1.01) console.log( naiveRound(2.175, 2) ); // 2.17 incorrect (should be 2.18) console.log( naiveRound(5.015, 2) ); // 5.01 incorrect (should be 5.02)
반올림 연산에 중간값이 포함되는지 여부를 확인하기 위해 Round 함수는 반올림할 원래 값에 10 ** n을 곱합니다. 여기서 n은 반환 값의 원하는 소수 자릿수입니다. 값의 일부가 .5 이상입니다. "Exact Testing for Equality"
는 부동 소수점 형식의 이진 표현 및 정밀도 문제로 인해 문제가 됩니다. 이것은 0.5보다 약간 작은 숫자의 소수 부분(정밀도 손실 때문에)은 반올림되지 않음을 의미합니다.
이전 예에서 5.015
는 소수점 이하 두 자리로 반올림되는 경우 중간 값이고 5.015 * 100의 값은 실제로 501.49999999999994
입니다. .49999999999994는 .5보다 작기 때문에 501로 내림하고 최종 결과는 5.01입니다.
더 나은 구현
지수 표기법
지수 표기법에서 숫자를 문자열로 변환하면 양수가 예상대로 반올림됩니다. 그러나 음수는 양수와 다르게 반올림됩니다.
사실, 기본적으로 "반올림" 과 동일한 규칙을 수행합니다. round(1.005, 2)
1.01
평가되더라도 round(-1.005, 2)
가 -1
평가되는 것을 볼 수 있습니다. lodash _.round 메서드는 이 기술을 사용합니다.
/** * Round half up ('round half towards positive infinity') * Uses exponential notation to avoid floating-point issues. * Negative numbers round differently than positive numbers. */ function round(num, decimalPlaces = 0) { num = Math.round(num + "e" + decimalPlaces); return Number(num + "e" + -decimalPlaces); } // test rounding of half console.log( round(0.5, 0) ); // 1 console.log( round(-0.5, 0) ); // 0 // testing edge cases console.log( round(1.005, 2) ); // 1.01 console.log( round(2.175, 2) ); // 2.18 console.log( round(5.015, 2) ); // 5.02 console.log( round(-1.005, 2) ); // -1 console.log( round(-2.175, 2) ); // -2.17 console.log( round(-5.015, 2) ); // -5.01
음수를 반올림할 때 일반적인 동작을 원하면 Math.round() 를 호출하기 전에 음수를 양수로 변환한 다음 반환하기 전에 다시 음수로 변환해야 합니다.
// Round half away from zero function round(num, decimalPlaces = 0) { if (num < 0) return -round(-num, decimalPlaces); num = Math.round(num + "e" + decimalPlaces); return Number(num + "e" + -decimalPlaces); }
대략적인 반올림
naiveRound
예제에 표시된 반올림 문제를 수정하기 위해 "거의 같음" 테스트를 수행하는 사용자 정의 반올림 함수를 정의하여 분수 값이 중간점 반올림의 대상이 되기에 중간점 값에 충분히 가까운지 여부를 결정할 수 있습니다.
// round half away from zero function round(num, decimalPlaces = 0) { if (num < 0) return -round(-num, decimalPlaces); var p = Math.pow(10, decimalPlaces); var n = num * p; var f = n - Math.floor(n); var e = Number.EPSILON * n; // Determine whether this fraction is a midpoint value. return (f >= .5 - e) ? Math.ceil(n) / p : Math.floor(n) / p; } // test rounding of half console.log( round(0.5, 0) ); // 1 console.log( round(-0.5, 0) ); // -1 // testing edge cases console.log( round(1.005, 2) ); // 1.01 console.log( round(2.175, 2) ); // 2.18 console.log( round(5.015, 2) ); // 5.02 console.log( round(-1.005, 2) ); // -1.01 console.log( round(-2.175, 2) ); // -2.18 console.log( round(-5.015, 2) ); // -5.02
번호.엡실론
반올림 함수를 호출하기 전에 엡실론 수정이 적용되는 가장 가까운 반올림을 수행하는 다른 순수 수학적 기술이 있습니다( "0에서 반올림" 사용).
간단히 말해서, 우리는 반올림하기 전에 제품에 가능한 가장 작은 부동 소수점 값(= 1.0 ulp; 마지막 위치의 단위)을 추가합니다. 이것은 0에서 멀리 떨어진 다음으로 표현할 수 있는 부동 소수점 값으로 이동하므로 곱하는 동안 발생할 수 있는 이진 반올림 오류 10 ** n
상쇄합니다.
/** * Round half away from zero ('commercial' rounding) * Uses correction to offset floating-point inaccuracies. * Works symmetrically for positive and negative numbers. */ function round(num, decimalPlaces = 0) { var p = Math.pow(10, decimalPlaces); var n = (num * p) * (1 + Number.EPSILON); return Math.round(n) / p; } // test rounding of half console.log( round(0.5, 0) ); // 1 console.log( round(-0.5, 0) ); // -1 // testing edge cases console.log( round(1.005, 2) ); // 1.01 console.log( round(2.175, 2) ); // 2.18 console.log( round(5.015, 2) ); // 5.02 console.log( round(-1.005, 2) ); // -1.01 console.log( round(-2.175, 2) ); // -2.18 console.log( round(-5.015, 2) ); // -5.02
1 ULP이다 5.015 * 100의 값에 추가 한 후 501.49999999999994
로 수정한다 501.50000000000006
이 뜻 502 올림 마지막 결과는 5.02이다.
마지막 위치 에 있는 단위의 크기("ulp")는 (1) 숫자의 크기와 (2) 상대 기계 엡실론(2^-52)에 의해 결정됩니다. Ulp는 크기가 작은 숫자보다 크기가 큰 숫자에서 상대적으로 더 큽니다.
이중 반올림
여기에서는 toPrecision() 메서드를 사용하여 중간 계산에서 부동 소수점 반올림 오류를 제거합니다. 간단히 말해서 16번째 유효 숫자에서 반올림 오류를 제거하기 위해 15개의 유효 숫자로 반올림합니다. 결과를 유효 숫자로 사전 반올림하는 이 기술은 PHP 7 반올림 함수에서도 사용됩니다.
되고 5.015 * 100의 값 501.49999999999994
같은 15 개 개의 유효 숫자에 제 반올림한다 501.500000000000
다음이를 다시 502 반올림되며 마지막 결과는 5.02이다.
// Round half away from zero function round(num, decimalPlaces = 0) { if (num < 0) return -round(-num, decimalPlaces); var p = Math.pow(10, decimalPlaces); var n = (num * p).toPrecision(15); return Math.round(n) / p; } // test rounding of half console.log( round(0.5, 0) ); // 1 console.log( round(-0.5, 0) ); // -1 // testing edge cases console.log( round(1.005, 2) ); // 1.01 console.log( round(2.175, 2) ); // 2.18 console.log( round(5.015, 2) ); // 5.02 console.log( round(-1.005, 2) ); // -1.01 console.log( round(-2.175, 2) ); // -2.18 console.log( round(-5.015, 2) ); // -5.02
임의 정밀도 JavaScript 라이브러리 - decimal.js
// Round half away from zero function round(num, decimalPlaces = 0) { return new Decimal(num).toDecimalPlaces(decimalPlaces).toNumber(); } // test rounding of half console.log( round(0.5, 0) ); // 1 console.log( round(-0.5, 0) ); // -1 // testing edge cases console.log( round(1.005, 2) ); // 1.01 console.log( round(2.175, 2) ); // 2.18 console.log( round(5.015, 2) ); // 5.02 console.log( round(-1.005, 2) ); // -1.01 console.log( round(-2.175, 2) ); // -2.18 console.log( round(-5.015, 2) ); // -5.02
<script src="https://cdnjs.cloudflare.com/ajax/libs/decimal.js/10.2.1/decimal.js" integrity="sha512-GKse2KVGCCMVBn4riigHjXE8j5hCxYLPXDw8AvcjUtrt+a9TbZFtIKGdArXwYOlZvdmkhQLWQ46ZE3Q1RIa7uQ==" crossorigin="anonymous"></script>
솔루션 1: 지수 표기법의 문자열
KFish에서 제공하는 솔루션에서 영감을 얻었습니다. https://stackoverflow.com/a/55521592/4208440
전체 라이브러리를 추가하지 않고도 특정 소수점 이하 자릿수까지 정확한 소수점 반올림, 바닥 및 천장을 제공하는 간단한 드롭인 솔루션입니다. 예기치 않은 결과를 피하기 위해 이진 반올림 문제를 수정하여 부동 소수점을 더 소수처럼 취급합니다. 예를 들어 floor((0.1+0.7)*10)은 예상 결과 8을 반환합니다.
숫자는 특정 소수 자릿수로 반올림됩니다. 음수 정밀도를 지정하면 소수점 왼쪽의 임의의 자릿수로 반올림됩니다.
// Solution 1 var DecimalPrecision = (function() { if (Math.sign === undefined) { Math.sign = function(x) { return ((x > 0) - (x < 0)) || +x; }; } if (Math.trunc === undefined) { Math.trunc = function(v) { return v < 0 ? Math.ceil(v) : Math.floor(v); }; } var decimalAdjust = function myself(type, num, decimalPlaces) { if (type === 'round' && num < 0) return -myself(type, -num, decimalPlaces); var shift = function(value, exponent) { value = (value + 'e').split('e'); return +(value[0] + 'e' + (+value[1] + (exponent || 0))); }; var n = shift(num, +decimalPlaces); return shift(Math[type](n), -decimalPlaces); }; return { // Decimal round (half away from zero) round: function(num, decimalPlaces) { return decimalAdjust('round', num, decimalPlaces); }, // Decimal ceil ceil: function(num, decimalPlaces) { return decimalAdjust('ceil', num, decimalPlaces); }, // Decimal floor floor: function(num, decimalPlaces) { return decimalAdjust('floor', num, decimalPlaces); }, // Decimal trunc trunc: function(num, decimalPlaces) { return decimalAdjust('trunc', num, decimalPlaces); }, // Format using fixed-point notation toFixed: function(num, decimalPlaces) { return decimalAdjust('round', num, decimalPlaces).toFixed(decimalPlaces); } }; })(); // test rounding of half console.log(DecimalPrecision.round(0.5)); // 1 console.log(DecimalPrecision.round(-0.5)); // -1 // testing very small numbers console.log(DecimalPrecision.ceil(1e-8, 2) === 0.01); // 0.01 console.log(DecimalPrecision.floor(1e-8, 2) === 0); // 0 // testing simple cases console.log(DecimalPrecision.round(5.12, 1) === 5.1); // 5.1 console.log(DecimalPrecision.round(-5.12, 1) === -5.1); // -5.1 console.log(DecimalPrecision.ceil(5.12, 1) === 5.2); // 5.2 console.log(DecimalPrecision.ceil(-5.12, 1) === -5.1); // -5.1 console.log(DecimalPrecision.floor(5.12, 1) === 5.1); // 5.1 console.log(DecimalPrecision.floor(-5.12, 1) === -5.2); // -5.2 console.log(DecimalPrecision.trunc(5.12, 1) === 5.1); // 5.1 console.log(DecimalPrecision.trunc(-5.12, 1) === -5.1); // -5.1 // testing edge cases for round console.log(DecimalPrecision.round(1.005, 2) === 1.01); // 1.01 console.log(DecimalPrecision.round(39.425, 2) === 39.43); // 39.43 console.log(DecimalPrecision.round(-1.005, 2) === -1.01); // -1.01 console.log(DecimalPrecision.round(-39.425, 2) === -39.43); // -39.43 // testing edge cases for ceil console.log(DecimalPrecision.ceil(9.130, 2) === 9.13); // 9.13 console.log(DecimalPrecision.ceil(65.180, 2) === 65.18); // 65.18 console.log(DecimalPrecision.ceil(-2.260, 2) === -2.26); // -2.26 console.log(DecimalPrecision.ceil(-18.150, 2) === -18.15); // -18.15 // testing edge cases for floor console.log(DecimalPrecision.floor(2.260, 2) === 2.26); // 2.26 console.log(DecimalPrecision.floor(18.150, 2) === 18.15); // 18.15 console.log(DecimalPrecision.floor(-9.130, 2) === -9.13); // -9.13 console.log(DecimalPrecision.floor(-65.180, 2) === -65.18); // -65.18 // testing edge cases for trunc console.log(DecimalPrecision.trunc(2.260, 2) === 2.26); // 2.26 console.log(DecimalPrecision.trunc(18.150, 2) === 18.15); // 18.15 console.log(DecimalPrecision.trunc(-2.260, 2) === -2.26); // -2.26 console.log(DecimalPrecision.trunc(-18.150, 2) === -18.15); // -18.15 // testing round to tens and hundreds console.log(DecimalPrecision.round(1262.48, -1) === 1260); // 1260 console.log(DecimalPrecision.round(1262.48, -2) === 1300); // 1300 // testing toFixed() console.log(DecimalPrecision.toFixed(1.005, 2) === "1.01"); // "1.01"
솔루션 2: 순수 수학(Number.EPSILON)
이 솔루션은 성능상의 이유로 모든 종류의 문자열 변환/조작을 방지합니다.
Solution 1: 25,838 ops/sec
Solution 2: 655,087 ops/sec
http://jsbench.github.io/#31ec3a8b3d22bd840f8e6822e681a3ac
// Solution 2 var DecimalPrecision2 = (function() { if (Number.EPSILON === undefined) { Number.EPSILON = Math.pow(2, -52); } if (Math.trunc === undefined) { Math.trunc = function(v) { return v < 0 ? Math.ceil(v) : Math.floor(v); }; } var isRound = function(num, decimalPlaces) { //return decimalPlaces >= 0 && // +num.toFixed(decimalPlaces) === num; var p = Math.pow(10, decimalPlaces); return Math.round(num * p) / p === num; }; var decimalAdjust = function(type, num, decimalPlaces) { if (isRound(num, decimalPlaces || 0)) return num; var p = Math.pow(10, decimalPlaces || 0); var n = (num * p) * (1 + Number.EPSILON); return Math[type](n) / p; }; return { // Decimal round (half away from zero) round: function(num, decimalPlaces) { return decimalAdjust('round', num, decimalPlaces); }, // Decimal ceil ceil: function(num, decimalPlaces) { return decimalAdjust('ceil', num, decimalPlaces); }, // Decimal floor floor: function(num, decimalPlaces) { return decimalAdjust('floor', num, decimalPlaces); }, // Decimal trunc trunc: function(num, decimalPlaces) { return decimalAdjust('trunc', num, decimalPlaces); }, // Format using fixed-point notation toFixed: function(num, decimalPlaces) { return decimalAdjust('round', num, decimalPlaces).toFixed(decimalPlaces); } }; })(); // test rounding of half console.log(DecimalPrecision2.round(0.5)); // 1 console.log(DecimalPrecision2.round(-0.5)); // -1 // testing very small numbers console.log(DecimalPrecision2.ceil(1e-8, 2) === 0.01); console.log(DecimalPrecision2.floor(1e-8, 2) === 0); // testing simple cases console.log(DecimalPrecision2.round(5.12, 1) === 5.1); console.log(DecimalPrecision2.round(-5.12, 1) === -5.1); console.log(DecimalPrecision2.ceil(5.12, 1) === 5.2); console.log(DecimalPrecision2.ceil(-5.12, 1) === -5.1); console.log(DecimalPrecision2.floor(5.12, 1) === 5.1); console.log(DecimalPrecision2.floor(-5.12, 1) === -5.2); console.log(DecimalPrecision2.trunc(5.12, 1) === 5.1); console.log(DecimalPrecision2.trunc(-5.12, 1) === -5.1); // testing edge cases for round console.log(DecimalPrecision2.round(1.005, 2) === 1.01); console.log(DecimalPrecision2.round(39.425, 2) === 39.43); console.log(DecimalPrecision2.round(-1.005, 2) === -1.01); console.log(DecimalPrecision2.round(-39.425, 2) === -39.43); // testing edge cases for ceil console.log(DecimalPrecision2.ceil(9.130, 2) === 9.13); console.log(DecimalPrecision2.ceil(65.180, 2) === 65.18); console.log(DecimalPrecision2.ceil(-2.260, 2) === -2.26); console.log(DecimalPrecision2.ceil(-18.150, 2) === -18.15); // testing edge cases for floor console.log(DecimalPrecision2.floor(2.260, 2) === 2.26); console.log(DecimalPrecision2.floor(18.150, 2) === 18.15); console.log(DecimalPrecision2.floor(-9.130, 2) === -9.13); console.log(DecimalPrecision2.floor(-65.180, 2) === -65.18); // testing edge cases for trunc console.log(DecimalPrecision2.trunc(2.260, 2) === 2.26); console.log(DecimalPrecision2.trunc(18.150, 2) === 18.15); console.log(DecimalPrecision2.trunc(-2.260, 2) === -2.26); console.log(DecimalPrecision2.trunc(-18.150, 2) === -18.15); // testing round to tens and hundreds console.log(DecimalPrecision2.round(1262.48, -1) === 1260); console.log(DecimalPrecision2.round(1262.48, -2) === 1300); // testing toFixed() console.log(DecimalPrecision2.toFixed(1.005, 2) === "1.01");
솔루션 3: 이중 반올림
이 솔루션은 toPrecision() 메서드를 사용하여 부동 소수점 반올림 오류를 제거합니다.
// Solution 3 var DecimalPrecision3 = (function() { if (Math.sign === undefined) { Math.sign = function(x) { return ((x > 0) - (x < 0)) || +x; }; } if (Math.trunc === undefined) { Math.trunc = function(v) { return v < 0 ? Math.ceil(v) : Math.floor(v); }; } // Eliminate binary floating-point inaccuracies. var stripError = function(num) { if (Number.isInteger(num)) return num; return parseFloat(num.toPrecision(15)); }; var decimalAdjust = function myself(type, num, decimalPlaces) { if (type === 'round' && num < 0) return -myself(type, -num, decimalPlaces); var p = Math.pow(10, decimalPlaces || 0); var n = stripError(num * p); return Math[type](n) / p; }; return { // Decimal round (half away from zero) round: function(num, decimalPlaces) { return decimalAdjust('round', num, decimalPlaces); }, // Decimal ceil ceil: function(num, decimalPlaces) { return decimalAdjust('ceil', num, decimalPlaces); }, // Decimal floor floor: function(num, decimalPlaces) { return decimalAdjust('floor', num, decimalPlaces); }, // Decimal trunc trunc: function(num, decimalPlaces) { return decimalAdjust('trunc', num, decimalPlaces); }, // Format using fixed-point notation toFixed: function(num, decimalPlaces) { return decimalAdjust('round', num, decimalPlaces).toFixed(decimalPlaces); } }; })(); // test rounding of half console.log(DecimalPrecision3.round(0.5)); // 1 console.log(DecimalPrecision3.round(-0.5)); // -1 // testing very small numbers console.log(DecimalPrecision3.ceil(1e-8, 2) === 0.01); console.log(DecimalPrecision3.floor(1e-8, 2) === 0); // testing simple cases console.log(DecimalPrecision3.round(5.12, 1) === 5.1); console.log(DecimalPrecision3.round(-5.12, 1) === -5.1); console.log(DecimalPrecision3.ceil(5.12, 1) === 5.2); console.log(DecimalPrecision3.ceil(-5.12, 1) === -5.1); console.log(DecimalPrecision3.floor(5.12, 1) === 5.1); console.log(DecimalPrecision3.floor(-5.12, 1) === -5.2); console.log(DecimalPrecision3.trunc(5.12, 1) === 5.1); console.log(DecimalPrecision3.trunc(-5.12, 1) === -5.1); // testing edge cases for round console.log(DecimalPrecision3.round(1.005, 2) === 1.01); console.log(DecimalPrecision3.round(39.425, 2) === 39.43); console.log(DecimalPrecision3.round(-1.005, 2) === -1.01); console.log(DecimalPrecision3.round(-39.425, 2) === -39.43); // testing edge cases for ceil console.log(DecimalPrecision3.ceil(9.130, 2) === 9.13); console.log(DecimalPrecision3.ceil(65.180, 2) === 65.18); console.log(DecimalPrecision3.ceil(-2.260, 2) === -2.26); console.log(DecimalPrecision3.ceil(-18.150, 2) === -18.15); // testing edge cases for floor console.log(DecimalPrecision3.floor(2.260, 2) === 2.26); console.log(DecimalPrecision3.floor(18.150, 2) === 18.15); console.log(DecimalPrecision3.floor(-9.130, 2) === -9.13); console.log(DecimalPrecision3.floor(-65.180, 2) === -65.18); // testing edge cases for trunc console.log(DecimalPrecision3.trunc(2.260, 2) === 2.26); console.log(DecimalPrecision3.trunc(18.150, 2) === 18.15); console.log(DecimalPrecision3.trunc(-2.260, 2) === -2.26); console.log(DecimalPrecision3.trunc(-18.150, 2) === -18.15); // testing round to tens and hundreds console.log(DecimalPrecision3.round(1262.48, -1) === 1260); console.log(DecimalPrecision3.round(1262.48, -2) === 1300); // testing toFixed() console.log(DecimalPrecision3.toFixed(1.005, 2) === "1.01");
솔루션 4: 이중 반올림 v2
이 솔루션은 솔루션 3과 비슷하지만 사용자 지정 toPrecision()
함수를 사용합니다.
// Solution 4 var DecimalPrecision4 = (function() { if (Math.sign === undefined) { Math.sign = function(x) { return ((x > 0) - (x < 0)) || +x; }; } if (Math.trunc === undefined) { Math.trunc = function(v) { return v < 0 ? Math.ceil(v) : Math.floor(v); }; } var toPrecision = function(num, significantDigits) { // Return early for ±0, NaN and Infinity. if (!num || !Number.isFinite(num)) return num; // Compute shift of the decimal point (sf - leftSidedDigits). var shift = significantDigits - 1 - Math.floor(Math.log10(Math.abs(num))); // Return if rounding to the same or higher precision. var decimalPlaces = 0; for (var p = 1; !Number.isInteger(num * p); p *= 10) decimalPlaces++; if (shift >= decimalPlaces) return num; // Round to "shift" fractional digits var scale = Math.pow(10, Math.abs(shift)); return shift > 0 ? Math.round(num * scale) / scale : Math.round(num / scale) * scale; }; // Eliminate binary floating-point inaccuracies. var stripError = function(num) { if (Number.isInteger(num)) return num; return toPrecision(num, 15); }; var decimalAdjust = function myself(type, num, decimalPlaces) { if (type === 'round' && num < 0) return -myself(type, -num, decimalPlaces); var p = Math.pow(10, decimalPlaces || 0); var n = stripError(num * p); return Math[type](n) / p; }; return { // Decimal round (half away from zero) round: function(num, decimalPlaces) { return decimalAdjust('round', num, decimalPlaces); }, // Decimal ceil ceil: function(num, decimalPlaces) { return decimalAdjust('ceil', num, decimalPlaces); }, // Decimal floor floor: function(num, decimalPlaces) { return decimalAdjust('floor', num, decimalPlaces); }, // Decimal trunc trunc: function(num, decimalPlaces) { return decimalAdjust('trunc', num, decimalPlaces); }, // Format using fixed-point notation toFixed: function(num, decimalPlaces) { return decimalAdjust('round', num, decimalPlaces).toFixed(decimalPlaces); } }; })(); // test rounding of half console.log(DecimalPrecision4.round(0.5)); // 1 console.log(DecimalPrecision4.round(-0.5)); // -1 // testing very small numbers console.log(DecimalPrecision4.ceil(1e-8, 2) === 0.01); console.log(DecimalPrecision4.floor(1e-8, 2) === 0); // testing simple cases console.log(DecimalPrecision4.round(5.12, 1) === 5.1); console.log(DecimalPrecision4.round(-5.12, 1) === -5.1); console.log(DecimalPrecision4.ceil(5.12, 1) === 5.2); console.log(DecimalPrecision4.ceil(-5.12, 1) === -5.1); console.log(DecimalPrecision4.floor(5.12, 1) === 5.1); console.log(DecimalPrecision4.floor(-5.12, 1) === -5.2); console.log(DecimalPrecision4.trunc(5.12, 1) === 5.1); console.log(DecimalPrecision4.trunc(-5.12, 1) === -5.1); // testing edge cases for round console.log(DecimalPrecision4.round(1.005, 2) === 1.01); console.log(DecimalPrecision4.round(39.425, 2) === 39.43); console.log(DecimalPrecision4.round(-1.005, 2) === -1.01); console.log(DecimalPrecision4.round(-39.425, 2) === -39.43); // testing edge cases for ceil console.log(DecimalPrecision4.ceil(9.130, 2) === 9.13); console.log(DecimalPrecision4.ceil(65.180, 2) === 65.18); console.log(DecimalPrecision4.ceil(-2.260, 2) === -2.26); console.log(DecimalPrecision4.ceil(-18.150, 2) === -18.15); // testing edge cases for floor console.log(DecimalPrecision4.floor(2.260, 2) === 2.26); console.log(DecimalPrecision4.floor(18.150, 2) === 18.15); console.log(DecimalPrecision4.floor(-9.130, 2) === -9.13); console.log(DecimalPrecision4.floor(-65.180, 2) === -65.18); // testing edge cases for trunc console.log(DecimalPrecision4.trunc(2.260, 2) === 2.26); console.log(DecimalPrecision4.trunc(18.150, 2) === 18.15); console.log(DecimalPrecision4.trunc(-2.260, 2) === -2.26); console.log(DecimalPrecision4.trunc(-18.150, 2) === -18.15); // testing round to tens and hundreds console.log(DecimalPrecision4.round(1262.48, -1) === 1260); console.log(DecimalPrecision4.round(1262.48, -2) === 1300); // testing toFixed() console.log(DecimalPrecision4.toFixed(1.005, 2) === "1.01");
http://jsbench.github.io/#31ec3a8b3d22bd840f8e6822e681a3ac
다음은 Chrome 85.0.4183.83에서 위 솔루션의 초당 작업을 비교한 벤치마크입니다. 분명히 모든 브라우저가 다르므로 마일리지가 다를 수 있습니다.
벤치마크의 스크린샷을 추가해 주신 @Mike에게 감사드립니다.
정확한 반올림 방법. 출처: 모질라
(function(){ /** * Decimal adjustment of a number. * * @param {String} type The type of adjustment. * @param {Number} value The number. * @param {Integer} exp The exponent (the 10 logarithm of the adjustment base). * @returns {Number} The adjusted value. */ function decimalAdjust(type, value, exp) { // If the exp is undefined or zero... if (typeof exp === 'undefined' || +exp === 0) { return Math[type](value); } value = +value; exp = +exp; // If the value is not a number or the exp is not an integer... if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0)) { return NaN; } // Shift value = value.toString().split('e'); value = Math[type](+(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp))); // Shift back value = value.toString().split('e'); return +(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp)); } // Decimal round if (!Math.round10) { Math.round10 = function(value, exp) { return decimalAdjust('round', value, exp); }; } // Decimal floor if (!Math.floor10) { Math.floor10 = function(value, exp) { return decimalAdjust('floor', value, exp); }; } // Decimal ceil if (!Math.ceil10) { Math.ceil10 = function(value, exp) { return decimalAdjust('ceil', value, exp); }; } })();
예:
// Round Math.round10(55.55, -1); // 55.6 Math.round10(55.549, -1); // 55.5 Math.round10(55, 1); // 60 Math.round10(54.9, 1); // 50 Math.round10(-55.55, -1); // -55.5 Math.round10(-55.551, -1); // -55.6 Math.round10(-55, 1); // -50 Math.round10(-55.1, 1); // -60 Math.round10(1.005, -2); // 1.01 -- compare this with Math.round(1.005*100)/100 above // Floor Math.floor10(55.59, -1); // 55.5 Math.floor10(59, 1); // 50 Math.floor10(-55.51, -1); // -55.6 Math.floor10(-51, 1); // -60 // Ceil Math.ceil10(55.51, -1); // 55.6 Math.ceil10(51, 1); // 60 Math.ceil10(-55.59, -1); // -55.5 Math.ceil10(-59, 1); // -50
여기에서 찾은 답변 중 정답이 없습니다 . @stinkycheeseman는 모든 숫자를 반올림, 올림 물었다.
반올림하려면 다음을 사용하십시오.
Math.ceil(num * 100)/100;
간단한 방법은 다음과 같습니다.
Math.round(value * 100) / 100
계속 진행하여 별도의 기능을 만들고 싶을 수도 있습니다.
function roundToTwo(value) { return(Math.round(value * 100) / 100); }
그런 다음 값을 전달하기만 하면 됩니다.
두 번째 매개변수를 추가하여 임의의 소수 자릿수로 반올림하도록 개선할 수 있습니다.
function myRound(value, places) { var multiplier = Math.pow(10, places); return (Math.round(value * multiplier) / multiplier); }
+(10).toFixed(2); // = 10 +(10.12345).toFixed(2); // = 10.12 (10).toFixed(2); // = 10.00 (10.12345).toFixed(2); // = 10.12
나에게 Math.round() 는 정답을 제공하지 않았습니다. toFixed(2) 가 더 잘 작동한다는 것을 알았습니다. 다음은 둘 다의 예입니다.
console.log(Math.round(43000 / 80000) * 100); // wrong answer console.log(((43000 / 80000) * 100).toFixed(2)); // correct answer
이 함수를 사용하십시오. Number(x).toFixed(2);
다음과 같은 경량 솔루션을 사용해 보십시오.
function round(x, digits){ return parseFloat(x.toFixed(digits)) } round(1.222, 2) ; // 1.22 round(1.222, 10) ; // 1.222
도움이 될 수 있습니다.
var result = Math.round(input*100)/100;
자세한 내용은 이 링크를 참조하세요.
2017년
네이티브 코드 .toFixed()
number = 1.2345; number.toFixed(2) // "1.23"
엄격해야 하고 필요한 경우에만 숫자를 추가해야 하는 경우 replace
number = 1; // "1" number.toFixed(5).replace(/\.?0*$/g,'');
두 가지 방법이 있습니다. 나와 같은 사람들을 위해 Lodash의 변종
function round(number, precision) { var pair = (number + 'e').split('e') var value = Math.round(pair[0] + 'e' + (+pair[1] + precision)) pair = (value + 'e').split('e') return +(pair[0] + 'e' + (+pair[1] - precision)) }
용법:
round(0.015, 2) // 0.02 round(1.005, 2) // 1.01
프로젝트에서 jQuery 또는 lodash를 사용하는 경우 라이브러리에서 round
n.toFixed(2)
변형이 정확하지 않기 때문에 제거했습니다. @avalanche1 감사합니다.
lodash 라이브러리를 사용하는 경우 다음과 같이 lodash의 round 방식을 사용할 수 있습니다.
_.round(number, precision)
예:
_.round(1.7777777, 2) = 1.78
ES6부터 toPrecision을 사용하여 이를 수행하는 '적절한' 방법(정적을 재정의하고 해결 방법을 만들지 않고)이 있습니다.
var x = 1.49999999999; console.log(x.toPrecision(4)); console.log(x.toPrecision(3)); console.log(x.toPrecision(2)); var y = Math.PI; console.log(y.toPrecision(6)); console.log(y.toPrecision(5)); console.log(y.toPrecision(4)); var z = 222.987654 console.log(z.toPrecision(6)); console.log(z.toPrecision(5)); console.log(z.toPrecision(4));
parseFloat
를 사용할 수 있으며 0은 '사라집니다'.
console.log(parseFloat((1.4999).toPrecision(3))); console.log(parseFloat((1.005).toPrecision(3))); console.log(parseFloat((1.0051).toPrecision(3)));
그것은 '1.005 반올림 문제'를 해결하지 못합니다. 부동 소수점 분수가 처리되는 방식에 내재되어 있기 때문입니다.
console.log(1.005 - 0.005);
라이브러리에 열려 있는 경우 bignumber.js를 사용할 수 있습니다.
console.log(1.005 - 0.005); console.log(new BigNumber(1.005).minus(0.005)); console.log(new BigNumber(1.005).round(4)); console.log(new BigNumber(1.005).round(3)); console.log(new BigNumber(1.005).round(2)); console.log(new BigNumber(1.005).round(1));
<script src="https://cdnjs.cloudflare.com/ajax/libs/bignumber.js/2.3.0/bignumber.min.js"></script>
가장 쉬운 방법은 toFixed를 사용한 다음 Number 함수를 사용하여 후행 0을 제거하는 것입니다.
const number = 15.5; Number(number.toFixed(2)); // 15.5
const number = 1.7777777; Number(number.toFixed(2)); // 1.78
MarkG와 Lavamantis는 수용된 것보다 훨씬 더 나은 솔루션을 제공했습니다. 그들이 더 많은 찬성표를 얻지 못하는 것은 유감입니다!
다음은 MDN 기반 의 부동 소수점 소수 문제를 해결하는 데 사용하는 함수입니다. Lavamantis의 솔루션보다 훨씬 더 일반적이지만 덜 간결합니다.
function round(value, exp) { if (typeof exp === 'undefined' || +exp === 0) return Math.round(value); value = +value; exp = +exp; if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0)) return NaN; // Shift value = value.toString().split('e'); value = Math.round(+(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp))); // Shift back value = value.toString().split('e'); return +(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp)); }
다음과 함께 사용:
round(10.8034, 2); // Returns 10.8 round(1.275, 2); // Returns 1.28 round(1.27499, 2); // Returns 1.27 round(1.2345678e+2, 2); // Returns 123.46
Lavamantis의 솔루션에 비해 우리는 할 수 있습니다...
round(1234.5678, -2); // Returns 1200 round("123.45"); // Returns 123
그것은 당신을 위해 일할 수 있습니다,
Math.round(num * 100)/100;
toFixed와 round의 차이점을 알고 싶습니다. Math.round(num) 대 num.toFixed(0) 및 브라우저 불일치를 볼 수 있습니다.
var roundUpto = function(number, upto){ return Number(number.toFixed(upto)); } roundUpto(0.1464676, 2);
toFixed(2)
여기서 2는 이 숫자를 반올림하려는 자릿수입니다.
필요한 경우에만 이러한 반올림을 수행하는 한 가지 방법 은 Number.prototype.toLocaleString() 을 사용하는 것입니다.
myNumber.toLocaleString('en', {maximumFractionDigits:2, useGrouping:false})
이것은 정확히 예상한 출력을 제공하지만 문자열로 제공됩니다. 원하는 데이터 유형이 아닌 경우 다시 숫자로 변환할 수 있습니다.
최종 업데이트:
이 답변은 후손을 위해 여기에 남겨두지만 @AmrAli의 DecimalPrecision 함수 적응을 사용하는 것이 좋습니다. 지수 표기법도 처리할 수 있기 때문입니다. 나는 원래 성능상의 이유로 모든 종류의 문자열 변환/조작을 피하려고 했지만 그의 구현과 성능에는 거의 차이가 없습니다.
2020년 8월 22일 편집: 여기에서 이러한 노력의 목표가 부동 소수점 데이터 유형으로 인한 고유한 반올림 오류를 완전히 제거하는 것이 아니라는 점을 분명히 해야 한다고 생각합니다. 실제로 값을 base10(십진수)으로 저장하고 있습니다. 목표는 부정확성을 가능한 한 최대한 멀리 밀어서 버그를 생성하지 않고 주어진 값에 대해 수학 연산을 수행할 수 있도록 하는 것이어야 합니다. 값이 절대 한계에 도달하는 순간, 값을 호출하기만 하면 조작하기 전이나 후에 JS가 버그를 생성하게 되므로 이를 완화하기 위해 더 이상 할 수 있는 일은 없습니다. 예를 들어 값 0.014999999999999999를 인스턴스화하면 JS는 즉시 0.015로 반올림합니다. 따라서 이러한 함수 중 하나에 해당 값을 전달하면 실제로 0.015를 전달하는 것입니다. 그 시점에서 먼저 문자열로 변환한 다음 조작할 수도 없었습니다. 값이 작동하려면 처음부터 문자열로 인스턴스화해야 합니다. 이 버그를 완화하기 위해 생성된 함수의 목표이자 합리적인 예상은 단순히 부동 소수점 값에 대해 수학 연산이 수행되도록 하는 동시에 버그를 시작 값이나 결과 값이 있는 가장자리로 끝까지 밀어 넣는 것입니다. 어쨌든 호출되기만 하면 버그가 발생합니다. 유일한 다른 대안 솔루션은 정수로 정수와 소수 값을 독립적으로 저장하여 항상 그대로 호출되도록 하거나 항상 값을 문자열로 저장하고 문자열 조작과 정수 기반의 조합을 사용하는 것입니다. 연산을 수행하는 수학.
정확한 십진수 반올림 정밀도를 달성하기 위해 가능한 모든 방법을 여러 번 반복한 후 가장 정확하고 효율적인 솔루션은 Number.EPSILON을 사용하는 것입니다. 이것은 부동 소수점 수학 정밀도 문제에 대한 진정한 수학 솔루션을 제공합니다. 여기에 표시된 대로 쉽게 폴리필할 수 있습니다. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/EPSILON 에서 마지막 남은 모든 IE 사용자를 지원합니다. 그 일을 중단해야 합니다).
여기에 제공된 솔루션에서 조정: https://stackoverflow.com/a/48850944/6910392
전체 라이브러리를 추가하지 않고 선택적 정밀도 변수를 사용하여 정확한 소수점 반올림, 바닥 및 천장을 제공하는 간단한 드롭인 솔루션입니다.
2020년 5월 19일 업데이트: Sergey가 주석에서 언급했듯이 지적할 가치가 있는 이(또는 모든) 방법에는 제한이 있습니다. 0.01499999999999999와 같은 숫자의 경우 부동 소수점 값 저장에 대한 정확도 제한의 절대 한계에 도달한 결과인 부정확성을 계속 경험할 수 있습니다. 값 자체가 즉시 0.015로 평가되기 때문에 이를 설명하기 위해 적용할 수 있는 수학 또는 기타 솔루션이 없습니다. 콘솔에서 해당 값 자체를 호출하기만 하면 이를 확인할 수 있습니다. 이 제한으로 인해 문자열 표현이 단순히 "0.015"이기 때문에 이 값을 줄이기 위해 문자열 조작을 사용하는 것조차 불가능합니다. 이를 설명하기 위한 솔루션은 값을 스크립트로 받아들이기 전에 데이터 소스에서 논리적으로 적용해야 합니다(예: 필드의 문자 길이 제한 등). 이는 고려해야 할 고려 사항입니다. 사례별로 최선의 접근 방식을 결정합니다.
2020년 8월 19일 업데이트: Amr의 의견에 따르면 ceil 및 floor 함수는 입력 값이 정수일 때 원하지 않는 결과를 생성합니다. 이는 예상되는 부동 소수점 정확도를 상쇄하기 위해 Number.EPSILON을 사용하여 입력에 적용된 추가 때문입니다. 입력 값이 정수인지 확인하고 변경되지 않은 값을 반환하도록 함수가 업데이트되었습니다. 이는 정수에 적용될 때 두 함수의 올바른 결과이기 때문입니다.
*참고: 이 문제는 또한 ceil 및 floor 함수에 여전히 Number.EPSILON 조정을 적용해야 하지만 입력 숫자의 소수점 이하 자릿수보다 작은 값에 적용할 때 바람직하지 않은 결과를 생성한다는 것을 보여줍니다. 출력에 대해 요청된 소수 자릿수(p). 예를 들어, ceil(17.1, 5)은 수학의 정수에 적용될 때 예상되는 "ceil" 함수 동작과 관련하여 17.1을 반환해야 합니다. 여기서 "1" 이후의 모든 소수 자릿수는 0으로 간주됩니다. 이를 수정하기 위해 I' 입력 숫자의 소수 자릿수가 요청된 출력 소수 자릿수보다 작은지 식별하고 정수와 마찬가지로 변경되지 않은 숫자를 반환하는 추가 기능 검사를 추가했습니다.
var DecimalPrecision = (function(){ if (Number.EPSILON === undefined) { Number.EPSILON = Math.pow(2, -52); } if(Number.isInteger === undefined){ Number.isInteger = function(value) { return typeof value === 'number' && isFinite(value) && Math.floor(value) === value; }; } this.isRound = function(n,p){ let l = n.toString().split('.')[1].length; return (p >= l); } this.round = function(n, p=2){ if(Number.isInteger(n) || this.isRound(n,p)) return n; let r = 0.5 * Number.EPSILON * n; let o = 1; while(p-- > 0) o *= 10; if(n<0) o *= -1; return Math.round((n + r) * o) / o; } this.ceil = function(n, p=2){ if(Number.isInteger(n) || this.isRound(n,p)) return n; let r = 0.5 * Number.EPSILON * n; let o = 1; while(p-- > 0) o *= 10; return Math.ceil((n + r) * o) / o; } this.floor = function(n, p=2){ if(Number.isInteger(n) || this.isRound(n,p)) return n; let r = 0.5 * Number.EPSILON * n; let o = 1; while(p-- > 0) o *= 10; return Math.floor((n + r) * o) / o; } return this; })(); console.log(DecimalPrecision.round(1.005)); console.log(DecimalPrecision.ceil(1.005)); console.log(DecimalPrecision.floor(1.005)); console.log(DecimalPrecision.round(1.0049999)); console.log(DecimalPrecision.ceil(1.0049999)); console.log(DecimalPrecision.floor(1.0049999)); console.log(DecimalPrecision.round(2.175495134384,7)); console.log(DecimalPrecision.round(2.1753543549,8)); console.log(DecimalPrecision.round(2.1755465135353,4)); console.log(DecimalPrecision.ceil(17,4)); console.log(DecimalPrecision.ceil(17.1,4)); console.log(DecimalPrecision.ceil(17.1,15));
가장 쉬운 방법:
+num.toFixed(2)
문자열로 변환한 다음 다시 정수 / 부동 소수점으로 변환합니다.
다음은 프로토타입 방법입니다.
Number.prototype.round = function(places){ places = Math.pow(10, places); return Math.round(this * places)/places; } var yournum = 10.55555; yournum = yournum.round(2);
"parseFloat(parseFloat(value).toFixed(2))"와 같은 것을 사용하십시오.
parseFloat(parseFloat("1.7777777").toFixed(2))-->1.78 parseFloat(parseFloat("10").toFixed(2))-->10 parseFloat(parseFloat("9.1").toFixed(2))-->9.1
출처 : http:www.stackoverflow.com/questions/11832914/how-to-round-to-at-most-2-decimal-places-if-necessary
C++에서 포인터 변수와 참조 변수의 차이점은 무엇입니까? (0) | 2021.10.06 |
---|---|
Java의 +=, -=, *=, /= 복합 할당 연산자에 캐스팅이 필요하지 않은 이유는 무엇입니까? (0) | 2021.10.06 |
셸 스크립트에서 JSON을 예쁘게 인쇄하려면 어떻게 해야 합니까? (0) | 2021.10.06 |
HashMap을 통해 반복 (0) | 2021.10.06 |
파이썬에서 현재 시간을 얻는 방법 (0) | 2021.10.06 |