使用纯数学(即不涉及字符串)
您可以通过先计算数字包含的最大十的幂,然后使用该值计算应将数字舍入到哪个精度来将其舍入为任意数量的有效数字。
const round = (value, significantFigures) => {
const exponent = Math.floor(Math.log10(value))
const nIntegers = exponent + 1
const precision = 10 ** (nIntegers - significantFigures)
return Math.round(value / precision) * precision
}
在上述代码中,
precision
表示我们想要舍入到的最近倍数。例如,如果我们想要舍入到最接近的100的倍数,则精度为100。如果我们想要舍入到最接近0.1的倍数,即保留一位小数,那么精度为0.1。
exponent
是值中包含的10的最大幂的指数。如果您对该指数的来源感兴趣,请继续阅读以下内容。
nIntegers
是值中整数(小数点左侧的数字)的数量。
一些例子:
> round(173.25, 1)
200
> round(173.25, 2)
170
> round(173.25, 3)
173
> round(173.25, 4)
173.3
> round(173.25, 5)
173.25
一个直观而富有教育性的解释
通常,我们可以通过首先将数字向一个方向“移动小数点”(除法),然后将数字四舍五入到最近的整数,最后将小数点移回其原始位置(乘法),以达到给定精度的四舍五入。
const rounded = Math.round(value / precision) * precision
例如,要将一个值舍入到最接近的
10的倍数,精度设置为
10,然后我们得到:
> Math.round(173.25 / 10) * 10
170
同样地,如果我们想将一个值舍入到小数点后一位,即找到最接近的 0.1 的倍数,精度应设置为 0.1
> Math.round(173.25 / 0.1) * 0.1
173.3
在这里,"精度" 简单地指的是 "我们想要四舍五入的最接近的倍数"。
那么我们如何利用这个知识来将一个值四舍五入到给定数量的有效数字呢?
我们需要解决的问题是确定我们应该四舍五入到的精度,根据有效数字的数量来确定。假设我们想要将值 12345.67 四舍五入到三个有效数字。在这种情况下,我们如何确定精度应该是 100?
精度应该是 100,因为 Math.round(12345.67 / 100) * 100 得到 12300,即四舍五入到三个有效数字。
实际上,解决这个问题非常简单。
基本上,我们需要做的是:1)确定小数点左边有多少位数字,然后2)根据这个数字确定在四舍五入数字之前 "移动小数点" 的步骤数。
我们首先计算数字整数部分的位数(即5位数字),然后减去我们要舍入到的有效数字的位数(3位数字)。结果为5-3=2,这是我们应该将小数点向左移动的步数(如果结果为负数,则应将小数点向右移动)。
为了将小数点向左移动两个步骤,我们必须使用精度10^2 = 100。换句话说,结果给出了我们应该将10提高到的幂,以便获得精度。
n_integer = "number of digits in the integer part of the number" = 5
n_significant = "the number of significant figures we want to round" = 3
precision = 10 ** (n_integer - n_significant)
就是这样!
但是,等一下!你还没有向我们展示如何编写代码!而且,你说我们不想使用字符串。那么,我们如何计算数字部分的位数,而不将其转换为字符串?好吧,我们不必使用字符串。数学来拯救我们!
我们知道一个实数 v 可以表示为十的幂(使用十进制系统)。也就是说,v = 10^a。如果我们现在只取 a 的整数部分,称之为 a',新值 v' = 10^a' 将是包含在 v 中的最大的 10 的幂。v 中的数字位数与 v' 中的数字位数相同,即 a' + 1。因此,我们已经证明了n_integer = a' + 1,其中a' = floor(log10(v))。
在代码中,它看起来像:
const exponent = Math.floor(Math.log10(v))
const nIntegers = exponent + 1
正如我们之前所说,精度是
const precision = 10 ** (nInteger - nSignificant)
最后,四舍五入
return Math.round(value / precision) * precision
例子
假设我们想将值
v = 12345.67四舍五入到
1个有效数字。为了使上述代码正常工作,精度必须是
precision = 10000 = 10^(n_integers - 1)。
如果我们想要四舍五入到
6个有效数字,精度必须是
precision = 0.1 = 10^(n_integers - 6)。
通常,精度必须是
precision = 10^(n_integers - n_significant)
一个有趣的副作用
使用这个代码和知识,你可以将一个数字四舍五入到任何值的最接近倍数,而不仅仅是普通的、无聊的
10的幂次方(即
{..., 1000, 100, 10, 1, 0.1, 0.01, ...})。不,使用这个方法,你可以将一个数字四舍五入到最接近的
0.3的倍数。
> Math.round(4 / 0.3) * 0.3
3.9
0.3 * 13 = 3.9,这是最接近 4 的 0.3 的倍数。
parseFloat(number.toPrecision(precision))
。感谢@Gianluca Casati。 - B Seven