javascript 取商
Very often calculations in JavaScript produce results that don’t quite fall within the ranges we want, or generate numbers that need to be “cleaned up” before use. Exactly what happens to those numbers - rounding up or down, set within a range, or being “clipped” to a certain number of decimal places - depends on what you want to use them for.
通常, JavaScript中的计算结果不会完全落在我们想要的范围内,或者生成的数字在使用前需要“清理”。 这些数字到底发生了什么(向上舍入或向下舍入,在一个范围内设置或“固定”到某个小数位数)取决于您要使用它们的目的。
为什么要四舍五入? (Why Round Numbers?)
One of the curious aspects of JavaScript is that it doesn’t actually store integers: instead, it thinks of numbers as floating point binaries. This, combined with the fact that many fractions can’t be expressed in a finite number of decimal places, means that JavaScript can create results like the following (using the console):
JavaScript的一个令人好奇的方面是它实际上并不存储整数:而是将数字视为浮点二进制。 结合许多分数不能用有限的小数位表示的事实,这意味着JavaScript可以创建如下结果(使用console ):
0.1 * 0.2;
> 0.020000000000000004
For practical purposes, this imprecision won’t matter in the vast majority of cases - we’re talking about an error of 2 quintillionths - but it is a little frustrating. It can also introduce slightly odd-looking results when dealing with numbers that represent values of currencies, percentages, or file sizes. To fix these representations, we need to round the results, or set decimal precision.
出于实际目的,这种不精确性在绝大多数情况下都无关紧要-我们所说的误差是2十分之几-但这有点令人沮丧。 当处理代表货币值,百分比或文件大小的数字时,它也可能会带来看起来有些奇怪的结果。 要修复这些表示形式,我们需要对结果取整或设置小数精度。
Rounding numbers has many other practical applications: if a user is manipulating a range
element, for example, we may want to round the associated value to the nearest whole number, rather than dealing with a decimal.
舍入数字还有许多其他实际应用:例如,如果用户正在操作range
元素 ,我们可能希望将关联的值舍入到最接近的整数,而不是处理小数。
舍入十进制数 (Rounding Decimal Numbers)
To clip a decimal number, use the toFixed()
or toPrecision
methods. Both take a single argument that determines, respectively, how many significant digits (i.e. the total number of digits used in the number) or decimal places (the number of digitals after the decimal point) to include in the result:
要裁剪十进制数字,请使用toFixed()
或toPrecision
方法。 两者都采用一个参数,分别确定要在结果中包括多少个有效数字(即,数字中使用的数字总数)或小数位(小数点后的数字数):
If no argument is specified for
toFixed()
, the default is 0, i.e. no decimal places; the argument has a maximum value of 20.如果没有为
toFixed()
指定参数,则默认值为0,即不带小数位;否则为0。 该参数的最大值为20。if no argument is specified for
toPrecision
, the number is unchanged.如果没有为
toPrecision
指定参数,则数字不变。
var randNum = 6.25;
randNum.toFixed();
> "6"
Math.PI.toPrecision(1);
> "3"
var randNum = 87.335;
randNum.toFixed(2);
> "87.33"
var randNum = 87.337;
randNum.toPrecision(3);
> "87.3"
重要的提示 (Important Note)
Both toFixed()
and toPrecision()
return the rounded string representation of the result, rather than a number. This means that “adding” rounded
to randNum
in the first example above will result in a concatenation, not the number you might expect:
toFixed()
和toPrecision()
返回结果的舍入字符串表示形式 ,而不是数字。 这意味着在上面的第一个示例中将“加” rounded
到randNum
将导致串联 , 而不是您可能期望的数字:
console.log(randNum + rounded);
> "6.256"
If you want the result to be an actual number, use parseFloat
on it:
如果希望结果为实际数字,请在其上使用parseFloat
:
var randNum = 6.25;
var rounded = parseFloat(randNum.toFixed(1));
console.log(rounded);
> 6.3
(Note that values of 5 are rounded up, except in rare occasions; for a better solution, see below.)
(注意,第5值向上舍入,除了在罕见的情况下;对于更好的解决方案,见下文)
toFixed()
and toPrecision()
are also useful methods to pad out a whole number to include decimal places, which can be especially handy when dealing with numbers that represent currencies:
toFixed()
和toPrecision()
也是填充整数以包括小数位的有用方法,在处理代表货币的数字时尤其方便:
var wholeNum = 1
var dollarsCents = wholeNum.toFixed(2);
console.log(dollarsCents);
> "1.00"
Note that toPrecision
will produce results in scientific notation if there are more integer digits in the number than the precision amount:
请注意,如果数字中的整数位数多于精度数值,则toPrecision
将产生科学计数法的结果:
var num = 123.435
num.toPrecision(2);
> "1.2e+2"
避免小数舍入错误 (Avoiding Decimal Rounding Errors)
In some cases, both toFixed
and toPrecision
will round values of 5
down rather than up:
在某些情况下, toFixed
和toPrecision
会将5
值向下 toPrecision
整而不是向上 toPrecision
整:
var numTest = 1.005;
numTest.toFixed(2);
> 1;
The result of the calculation above should be 1.01
, not 1
. If avoiding this error is important, I’d recommend a solution suggested by Jack L Moore that uses exponential numbers for the calculation:
上面的计算结果应该是1.01
, 而不是 1
。 如果避免发生此错误很重要,我建议使用Jack L Moore建议的解决方案,该解决方案使用指数数进行计算:
function round(value, decimals) {
return Number(Math.round(value+'e'+decimals)+'e-'+decimals);
}
To use:
使用方法:
round(1.005,2);
> 1.01
If you want an even more robust rounding solution than the one shown here, one is available on MDN.
如果您想要一种比此处显示的解决方案更强大的舍入解决方案,则可以在MDN上使用 。
Epsilon舍入 (Epsilon Rounding)
An alternative method of rounding decimals was introduced with ES6 (aka JavaScript 2015). “Machine epsilon” provides a reasonable margin of error when comparing two floating point numbers. Without rounding, comparisons can yield results like the following:
ES6(aka JavaScript 2015)引入了另一种四舍五入小数的方法。 比较两个浮点数时, “机器ε”可提供合理的误差范围。 不进行四舍五入, 比较会产生如下结果:
0.1 + 0.2 === 0.3
> false
Math.EPSILON
can be used in a function to produce correct comparisons:
Math.EPSILON
可以在函数中使用以产生正确的比较:
function epsEqu(x, y) {
return Math.abs(x - y) < Number.EPSILON * Math.max(Math.abs(x), Math.abs(y));
}
The function takes two arguments: one carrying the calculation, the other the (rounded) expected result. It returns the comparison of the two:
该函数有两个参数:一个参数进行计算,另一个参数(取整)。 它返回两者的比较:
epsEqu(0.1 + 0.2, 0.3)
> true
Every current modern browser supports ES6 math functions, but if you want to gain support in older browsers such as IE 11, there are polyfills available.
当前的每个现代浏览器都支持ES6数学功能,但是如果您想在IE 11等较旧的浏览器中获得支持,则可以使用polyfill 。
截断小数 (Truncating Decimal Numbers)
All the techniques shown so far do some kind of rounding to decimal numbers. To truncate a positive number to two decimal places, multiply it by 100, truncate it, then divide the result by 100:
到目前为止显示的所有技术都进行了某种四舍五入到十进制数的操作。 要将正数截断为两位小数,请将其乘以100,截断然后将结果除以100:
function truncated(num) {
return Math.trunc(num * 100) / 100;
}
truncated(3.1416)
> 3.14
If you want something with a bit more adaptability, you can take advantage of the bitwise double tilde operator:
如果您想要更多的适应性,可以利用按位双波浪号运算符:
function truncated(num, decimalPlaces) {
var numPowerConverter = Math.pow(10, decimalPlaces);
return ~~(num * numPowerConverter)/numPowerConverter;
}
To use:
使用方法:
var randInt = 35.874993;
truncated(randInt,3);
> 35.874
I’ll have more to say about bitwise operations in a future article.
在以后的文章中,我将更多地介绍按位运算。
四舍五入到最接近的数字 (Rounding To The Nearest Number)
To round a decimal number up or down to the nearest whole number, depending on which is closest, use Math.round()
:
要将十进制数向上或向下舍入为最接近的整数(取决于最接近的整数Math.round()
,请使用Math.round()
:
Math.round(4.3)
> 4
Math.round(4.5)
> 5
Note that “half values” like .5
round up.
请注意,“。半值”如.5
向上舍入。
四舍五入到最接近的整数 (Rounding Down To The Nearest Whole Number)
If you always want to round down, use Math.floor
:
如果你总是想回合下来 ,用Math.floor
:
Math.floor(42.23);
> 42
Math.floor(36.93);
> 36
Note that this “down” rounding direction is true for all numbers, including negatives. Think of a skyscraper with an infinite number of floors, including basement levels (representing the negative numbers). If you’re in an elevator between basement levels 2 and 3 (represented by a value of -2.5), Math.floor
will take you to -3:
请注意,此“向下”舍入方向适用于所有数字,包括负数。 想一想摩天大楼的楼层数是无限的,包括地下楼层(代表负数)。 如果您在2到3级地下室之间(由-2.5表示),则Math.floor
会将您带到-3:
Math.floor(-2.5);
> -3
If want to avoid this behaviour, use Math.trunc
, supported in all modern browsers (except IE/Edge):
如果要避免这种行为,请使用所有现代浏览器(IE / Edge除外)都支持的Math.trunc
:
Math.trunc(-41.43);
> -41
MDN also provides a three-line polyfill to gain Math.trunc
support in older browsers and IE/Edge.
MDN还提供了三行Math.trunc
以在较旧的浏览器和IE / Edge中获得Math.trunc
支持 。
舍入到最接近的整数 (Rounding Up To The Nearest Whole Number)
Alternatively, if you always want to round up, use Math.ceil
. Again, think of that infinite elevator: Math.ceil
will always go “upwards”, regardless of whether the number is negative or not:
或者,如果你总是希望围捕 ,使用Math.ceil
。 再次考虑一下无限电梯: Math.ceil
将始终“向上”,无论数字是否为负:
Math.ceil(42.23);
> 43
Math.ceil(36.93);
> 37
Math.ceil(-36.93); -36
四舍五入到数字的最近倍数 (Rounding Up/Down To The Nearest Multiple of a Number)
If we want to round to the nearest multiple of 5, the easiest way is to create a function that divides the number by 5, rounds it, then multiplies it by the same amount:
如果我们想四舍五入到最接近的5的倍数,最简单的方法是创建一个将数字除以5的函数,将其四舍五入,然后将其乘以相同的数量:
function roundTo5(num) {
return Math.round(num/5)*5;
}
To use it:
要使用它:
roundTo5(11);
> 10
If you were rounding to multiples of different numbers, we could make this function more general by passing the function both the initial number and the multiple:
如果四舍五入为不同数字的倍数,我们可以通过传递初始数字和倍数来使该函数更通用:
function roundToMultiple(num, multiple) {
return Math.round(num/multiple)*multiple;
}
To use it, include both the number and the multiple in the call to the function:
要使用它,请在函数调用中同时包含数字和倍数:
var initialNumber = 11;
var multiple = 10;
roundToMultiple(initialNumber, multiple);
> 10;
To exclusively round up or round down; substitute ceil
or floor
for round
in the function.
专心地向上或向下取整: 在功能中用ceil
或floor
代替round
。
夹紧数到一定范围 (Clamping Number To a Range)
There are plenty of times when you may receive a value x
that needs to be within the bounds of a range. For example, you may need a value from 1
to 100
, but receive a value of 123
. To fix this, we can use min
(which will always return the smallest
of a set of numbers) and max
(the largest member of any set of numbers). Using the 1
to 100
example:
很多时候,您可能会收到一个必须在范围范围内的值x
。 例如,您可能需要一个1
到100
的值,但是会收到123
的值。 为了解决这个问题,我们可以使用min
(总是返回一组数字中的smallest
数)和max
(任何一组数字中的最大数)。 以1
到100
为例:
var lowBound = 1;
var highBound = 100;
var numInput = 123;
var clamped = Math.max(lowBound, Math.min(numInput, highBound));
console.log(clamped);
> 100;
Again, this could be turned into a function, or possibly an extension of the Number
class, a variation first suggested by Daniel X. Moore:
同样,这可以被转换为一个函数,或者可能是Number
类的扩展,这是Daniel X. Moore首先提出的一种变体:
Number.prototype.clamp = function(min, max) {
return Math.min(Math.max(this, min), max);
};
To use it:
要使用它:
(numInput).clamp(lowBound, highBound);
高斯舍入 (Gaussian Rounding)
Gaussian rounding, also known as “bankers” rounding, convergent rounding, Dutch rounding, and odd–even rounding, is a method of rounding without statistical bias; regular rounding has a native upwards bias. Gaussian rounding avoids this by rounding to the nearest even number. The best solution I’m aware of is by Tim Down:
高斯舍入,也称为“银行家”舍入,收敛舍入舍入,荷兰舍入和奇偶舍入,是一种无统计偏差的舍入方法; 常规取整具有固有的向上偏差。 高斯舍入通过舍入到最接近的偶数来避免这种情况。 我知道的最好的解决方案是Tim Down :
function gaussRound(num, decimalPlaces) {
var d = decimalPlaces || 0,
m = Math.pow(10, d),
n = +(d ? num * m : num).toFixed(8),
i = Math.floor(n), f = n - i,
e = 1e-8,
r = (f > 0.5 - e && f < 0.5 + e) ?
((i % 2 == 0) ? i : i + 1) : Math.round(n);
return d ? r / m : r;
}
Examples in use:
使用示例:
gaussRound(2.5)
> 2
gaussRound(3.5)
> 4
gaussRound(2.57,1)
> 2.6
CSS中的小数 (Decimals in CSS)
Since JavaScript is often used to generate positional or transform information for HTML elements, you might wonder what happens if we generate decimal values for elements:
由于JavaScript通常用于生成HTML元素的位置或变换信息,因此您可能想知道如果我们为元素生成十进制值会发生什么:
#box { width: 63.667731993px; }
The good news is that modern browsers will respect decimal values in the box model, including percentage and pixel measurements.
好消息是, 现代浏览器将使用盒模型中的十进制值 ,包括百分比和像素尺寸。
javascript 取商