소수점 이하 두 자리까지 반올림하고 싶지만 필요한 경우에만 .
입력:
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
. 그러나 m
String으로 변환 하면 …
> 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 -1 ≤ S <10 K 에 대한 숫자 값 S × 10 N – K는 인 m 과 k는 가능한 한 작게한다. 이 K의 소수 표현 자리수 유의 S , S를 10로 나누어 아니고, 최하위 디지트 것을 들 반드시 고유 이러한 기준에 의해 결정되지 않는다.
여기서 중요한 부분은 ” k 는 가능한 한 작아야 “한다는 요구 사항입니다 . 그 요구 사항 m
의 가치 는 Number가 주어지면 의 요구 사항을 충족하면서도 가능한 최소 자릿수 를 String(m)
가져야 한다는 요구 사항입니다 . 우리는 이미 그것을 알고 있기 때문에 , 왜 사실이어야 하는지 분명 합니다.Number(String(m)) === m
0.015 === 0.0150000000000000001
String(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);
}