我想最多舍入2位小数,但是 只在必要时。
输入:
10
1.7777777
9.1
输出:
10
1.78
9.1
我怎么能这样做 JavaScript
?
我想最多舍入2位小数,但是 只在必要时。
输入:
10
1.7777777
9.1
输出:
10
1.78
9.1
我怎么能这样做 JavaScript
?
使用 Math.round(num * 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
解决方案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');
});
})
一个人可以使用 .toFixed(NumberOfDecimalPlaces)
。
var str = 10.234.toFixed(2); // => '10.23'
var number = Number(str); // => 10.23
精确的舍入方法。资源: Mozilla的
(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;
这个问题很复杂。
假设我们有一个功能, roundTo2DP(num)
,将float作为参数,并返回舍入到2位小数的值。每个表达式应该评估什么?
roundTo2DP(0.014999999999999999)
roundTo2DP(0.0150000000000000001)
roundTo2DP(0.015)
“显而易见”的答案是第一个例子应该舍入到0.01(因为它接近0.01而不是0.02),而另外两个应该舍入到0.02(因为0.0150000000000000001接近0.02而不是0.01,因为0.015恰好在中间他们并且有一个数学约定,这些数字被四舍五入)。
您可能已经猜到的那个问题是 roundTo2DP
不可能 实施以提供那些明显的答案,因为传递给它的所有三个数字都是 相同的数字。 IEEE 754二进制浮点数(JavaScript使用的类型)不能精确地表示大多数非整数数字,因此上面的所有三个数字文字都会四舍五入到附近的有效浮点数。这个数字恰好是 究竟
0.01499999999999999944488848768742172978818416595458984375
它接近0.01而不是0.02。
您可以在浏览器控制台,Node shell或其他JavaScript解释器中看到所有三个数字都相同。只是比较它们:
> 0.014999999999999999 === 0.0150000000000000001
true
所以当我写作 m = 0.0150000000000000001
, 确切的价值 m
我最终得到的更接近 0.01
比它更好 0.02
。然而,如果我转换 m
到一个字符串......
> var m = 0.0150000000000000001;
> 的console.log(字符串(M));
0.015
> var m = 0.014999999999999999;
> 的console.log(字符串(M));
0.015
...我得到0.015,应该是0.02,这是明显的 不 我之前说过的56位小数,所有这些数字都完全等于。那么黑魔法是什么?
答案可以在ECMAScript规范的部分中找到 7.1.12.1:ToString应用于Number类型。这里转换一些数字的规则 米 放下一个字符串。关键部分是第5点,其中是一个整数 小号 生成的数字将在字符串表示中使用 米:
让 ñ, ķ,和 小号 是这样的整数 ķ ≥1,10ķ-1 ≤ 小号 <10ķ,数字值 小号 ×10ñ - ķ 是 米,和 ķ 尽可能小。请注意,k是十进制表示中的位数 小号那个 小号 不能被10整除,而且是最不重要的数字 小号 不一定由这些标准唯一确定。
这里的关键部分是要求“ķ “尽可能小”。这个要求相当于一个要求,给定一个数字 m
, 的价值 String(m)
一定有 尽可能少的数字 同时仍然满足要求 Number(String(m)) === m
。既然我们已经知道了 0.015 === 0.0150000000000000001
,现在很清楚为什么 String(0.0150000000000000001) === '0.015'
必须是真的。
当然,这一讨论都没有直接回答 roundTo2DP(m)
应该 返回。如果 m
的确切值是0.01499999999999999944488848768742172978818416595458984375,但它的字符串表示是'0.015',那么什么是 正确 回答 - 数学,实际,哲学或其他什么 - 当我们将它四舍五入到小数点后两位?
对此没有一个正确的答案。这取决于您的使用案例。在下列情况下,您可能希望尊重String表示并向上舍入:
另一方面,当您的值来自固有的连续标度时,您可能希望尊重二进制浮点值并向下舍入 - 例如,如果它是来自传感器的读数。
这两种方法需要不同的代码。为了尊重Number的String表示,我们可以(通过相当多一些相当微妙的代码)实现我们自己的舍入,它直接作用于String表示,逐个数字,使用你在学校时使用的相同算法被教导如何围绕数字。下面是一个例子,它尊重OP要求通过剥离小数点后面的尾随零来“仅在必要时”将数字表示为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.015,最高为0.02,所以0.014999999 ...... 375轮到0.02”。
在这里我们可以简单地使用内置功能 toFixed
方法。请注意,通过电话 Number()
在返回的字符串上 toFixed
,我们得到一个Number,其String表示没有尾随零(由于JavaScript计算数字的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()
:
这是一个简单的方法:
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);
}