我想最多四舍五入两位小数,但只有在必要时。

输入:

10
1.7777777
9.1

输出:

10
1.78
9.1

如何在JavaScript中执行此操作?


当前回答

通常,小数舍入是通过缩放来完成的:round(num*p)/p

天真的实施

将以下函数与中间数一起使用,您将获得预期的上舍入值,或有时根据输入获得下舍入值。

舍入中的这种不一致可能会在客户端代码中引入难以检测的错误。

函数naiveRound(num,decimalPlaces=0){var p=数学.pow(10,小数位数);return数学舍入(num*p)/p;}console.log(naiveRound(1.245,2));//1.25正确(按预期四舍五入)console.log(naiveRound(1.255,2));//1.25不正确(应为1.26)//测试边缘案例console.log(naiveRound(1.005,2));//1不正确(应为1.01)console.log(naiveRound(2.175,2));//2.17不正确(应为2.18)console.log(naiveRound(5.015,2));//5.01不正确(应为5.02)

为了确定舍入操作是否涉及中点值,Round函数将要舍入的原始值乘以10**n,其中n是返回值中所需的小数位数,然后确定该值的剩余小数部分是否大于或等于.5。由于浮点格式在二进制表示和精度方面存在问题,这种“精确相等测试”对于浮点值是有问题的。这意味着一个数字的任何小数部分如果稍微小于.5(因为精度损失),都不会向上舍入。

在上一个示例中,如果要舍入到两位小数,则5.015是一个中间值,5.015*100的值实际上是501.49999999999994。因为.49999999999994小于.5,所以向下舍入为501,最终结果为5.01。

更好的实施

指数表示法

通过将数字转换为指数表示法中的字符串,正数将按预期取整。但是,请注意负数与正数的舍入方式不同。

事实上,它执行的基本上等同于“向上舍入一半”的操作。作为规则,您将看到,尽管舍入(1.005,2)的值为1.01,但舍入(-1.005,2)仍计算为-1。lodash-round方法使用了这种技术。

/***向上舍入一半(“向正无穷大舍入一半”)*负数的舍入方式不同于正数。*/函数舍入(num,decimalPlaces=0){num=数学舍入(num+“e”+小数位数);返回数字(num+“e”+-decimalPlaces);}//一半的测试舍入console.log(圆形(0.5));//1.console.log(圆形(-0.5));//0//测试边缘案例console.log(圆形(1.005,2));//1.01console.log(圆形(2.175,2));//2.18console.log(圆形(5.015,2));//5.02console.log(圆形(-1.005,2));//-1.console.log(圆形(-2.175,2));//-2.17console.log(圆形(-5.015,2));//-5.01

如果您想要负数舍入时的通常行为,则需要在调用Math.rround()之前将负数转换为正数,然后在返回之前将它们转换回负数。

// 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示例中显示的舍入问题,我们可以定义一个自定义舍入函数,该函数执行“近似相等”测试,以确定分数值是否足够接近中点值以进行中点舍入。

//离零约一半函数舍入(num,decimalPlaces=0){如果(num<0)return-round(-num,decimalPlaces);var p=数学.pow(10,小数位数);变量n=num*p;var f=n-数学楼层(n);var e=数字.EPSILON*n;//确定该分数是否为中点值。返回(f>=.5-e)?数学ceil(n)/p:数学floor(n)/p;}//一半的测试舍入console.log(圆形(0.5));//1.console.log(圆形(-0.5));//-1.//测试边缘案例console.log(圆形(1.005,2));//1.01console.log(圆形(2.175,2));//2.18console.log(圆形(5.015,2));//5.02console.log(圆形(-1.005,2));//-1.01console.log(圆形(-2.175,2));//-2.18console.log(圆形(-5.015,2));//-5.02

数字.EPSILON

有一种不同的纯数学技术来执行最接近的舍入(使用“距离零的舍入半”),其中在调用舍入函数之前应用epsilon校正。

简单地说,我们在舍入之前将最小的浮点值(=1.0 ulp;单位在最后一位)添加到乘积中。这将移动到下一个可表示的浮点值,远离零,因此它将抵消在乘以10**n期间可能出现的二进制舍入误差。

/***从零开始舍入一半(“商业”舍入)*使用校正来抵消浮点精度。*对正数和负数对称工作。*/函数舍入(num,decimalPlaces=0){var p=数学.pow(10,小数位数);var n=(num*p)*(1+Number.EPSILON);return数学舍入(n)/p;}//一半舍入console.log(圆形(0.5));//1.console.log(圆形(-0.5));//-1.//测试边缘案例console.log(圆形(1.005,2));//1.01console.log(圆形(2.175,2));//2.18console.log(圆形(5.015,2));//5.02console.log(圆形(-1.005,2));//-1.01console.log(圆形(-2.175,2));//-2.18console.log(圆形(-5.015,2));//-5.02

添加1 ulp后,5.015*100的值(即501.49999999999994)将被修正为501.50000000000006,这将四舍五入到502,最终结果为5.02。

请注意,最后一位单位的大小(“ulp”)由(1)数字的大小和(2)相对机器ε(2^-52)决定。Ulps在震级较大的数值上比在震级较小的数值上相对较大。

双舍入

这里,我们使用toPrecision()方法去除中间计算中的浮点舍入错误。简单地说,我们四舍五入到15位有效数字,以去除第16位有效数字的舍入误差。PHP 7 round函数也使用这种将结果预转为有效数字的技术。

5.015*100的值(即501.49999999999994)将首先四舍五入到15位有效数字,即501.500000000000,然后再次四舍五进到502,最终结果为5.02。

//距离零的一半函数舍入(num,decimalPlaces=0){如果(num<0)return-round(-num,decimalPlaces);var p=数学.pow(10,小数位数);var n=(num*p).toPrecision(15);return数学舍入(n)/p;}//一半舍入console.log(圆形(0.5));//1.console.log(圆形(-0.5));//-1.//测试边缘案例console.log(圆形(1.005,2));//1.01console.log(圆形(2.175,2));//2.18console.log(圆形(5.015,2));//5.02console.log(圆形(-1.005,2));//-1.01console.log(圆形(-2.175,2));//-2.18console.log(圆形(-5.015,2));//-5.02

任意精度JavaScript库-decimal.js

//距离零的一半函数舍入(num,decimalPlaces=0){return new Decimal(num).toDecimalPlaces(decimalPlaces).toNumber();}//一半舍入console.log(圆形(0.5));//1.console.log(圆形(-0.5));//-1.//测试边缘案例console.log(圆形(1.005,2));//1.01console.log(圆形(2.175,2));//2.18console.log(圆形(5.015,2));//5.02console.log(圆形(-1.005,2));//-1.01console.log(圆形(-2.175,2));//-2.18console.log(圆形(-5.015,2));//-5.02<script src=“https://cdnjs.cloudflare.com/ajax/libs/decimal.js/10.2.1/decimal.js“integrity=”sha512-GKse2KVGCCMVBn4riigHjXE8j5hCxYLPXDw8avjUtrt+a9TbZFtIKGdArXwYOlZvdmkhQLWQ46ZE3Q1RIa7uQ=“crossrorigin=”匿名“></script>

解决方案1:以指数表示的字符串

灵感来自KFish提供的解决方案:https://stackoverflow.com/a/55521592/4208440

一种简单的插入式解决方案,可提供精确的小数舍入、地板和上限,以达到特定的小数位数,而无需添加整个库。它通过修复二进制舍入问题,将浮点值处理得更像小数,以避免意外结果:例如,floor((0.1+0.7)*10)将返回预期结果8。

数字四舍五入到特定的小数位数。指定负精度将舍入到小数点左侧的任意位数。

//解决方案1var DecimalPrecision=(函数){if(Math.trunc==未定义){Math.trunc=函数(v){返回v<0?数学ceil(v):数学floor(v);};}var decimalAdjust=函数本身(type,num,decimalPlaces){if(类型==“round”&&num<0)return-我自己(type,-num,decimalPlaces);var shift=函数(值,指数){值=(值+'e').拆分(e');返回+(值[0]+'e'+(+值[1]+(指数||0)));};var n=移位(num,+小数位数);返回移位(数学[type](n),-decimalPlaces);};返回{//十进制舍入(距离零的一半)round:函数(num,decimalPlaces){return decimalAdjust('round',num,decimalPlaces);},//十进制ceilceil:函数(num,decimalPlaces){return decimalAdjust('eil',num,decimalPlaces);},//十进制楼层floor:函数(num,decimalPlaces){return decimalAdjust('floor',num,decimalPlaces);},//十进制截断trunca:函数(num,decimalPlaces){return decimalAdjust('trunc',num,decimalPlaces);},//使用定点表示法格式化toFixed:函数(num,decimalPlaces){return decimalAdjust('round',num,decimalPlaces).toFixed(decimalPlace);}};})();//一半的测试舍入console.log(DecimalPrecision.round(0.5));//1.console.log(DecimalPrecision.round(-0.5));//-1.//测试非常小的数字console.log(DecimalPrecision.ceil(1e-8,2)==0.01);console.log(DecimalPrecision.floor(1e-8,2)==0);//测试简单案例console.log(DecimalPrecision.round(5.12,1)==5.1);console.log(DecimalPrecision.round(-5.12,1)==-5.1);console.log(DecimalPrecision.ceil(5.12,1)==5.2);console.log(DecimalPrecision.ceil(-5.12,1)==-5.1);console.log(DecimalPrecision.floor(5.12,1)===5.1);console.log(DecimalPrecision.floor(-5.12,1)==-5.2);console.log(DecimalPrecision.trunc(5.12,1)==5.1);console.log(DecimalPrecision.trunc(-5.12,1)==-5.1);//测试圆形边壳console.log(DecimalPrecision.round(1.005,2)==1.01);console.log(DecimalPrecision.round(39.425,2)==39.43);console.log(DecimalPrecision.round(-1.005,2)==-1.01);console.log(DecimalPrecision.round(-39.425,2)==-39.43);//测试ceil的边缘案例console.log(DecimalPrecision.ceil(9.13,2)==9.13);console.log(DecimalPrecision.ceil(65.18,2)==65.18);console.log(DecimalPrecision.ceil(-2.26,2)==-2.26);console.log(DecimalPrecision.ceil(-18.15,2)==-18.15);//测试地板边缘案例console.log(DecimalPrecision.floor(2.26,2)==2.26);console.log(DecimalPrecision.floor(18.15,2)==18.15);console.log(DecimalPrecision.floor(-9.13,2)==-9.13);console.log(DecimalPrecision.floor(-65.18,2)==-65.18);//trunc的边缘用例测试console.log(DecimalPrecision.trunc(2.26,2)==2.26);console.log(DecimalPrecision.trunc(18.15,2)==18.15);console.log(DecimalPrecision.trunc(-2.26,2)==-2.26);console.log(DecimalPrecision.trunc(-18.15,2)==-18.15);//测试到数十和数百console.log(DecimalPrecision.round(1262.48,-1)==1260);console.log(DecimalPrecision.round(1262.48,-2)==1300);//测试到Fixed()console.log(DecimalPrecision.toFixed(1.005,2)==“1.01”);

解决方案2:纯数学(编号:EPSILON)

由于性能原因,此解决方案避免了任何类型的字符串转换/操作。

//解决方案2var DecimalPrecision2=(函数){if(数字.EPSILON===未定义){Number.EPSILON=数学功率(2,-52);}if(数学符号==未定义){Math.sign=函数(x){return((x>0)-(x<0))||+x;};}返回{//十进制舍入(距离零的一半)round:函数(num,decimalPlaces){var p=数学.pow(10,小数位数||0);var n=(num*p)*(1+Number.EPSILON);return数学舍入(n)/p;},//十进制ceilceil:函数(num,decimalPlaces){var p=数学.pow(10,小数位数||0);var n=(num*p)*(1-数学符号(num)*数字.EPSILON);返回数学ceil(n)/p;},//十进制楼层floor:函数(num,decimalPlaces){var p=数学.pow(10,小数位数||0);var n=(num*p)*(1+数学符号(num)*数字.EPSILON);返回数学楼层(n)/p;},//十进制截断trunca:函数(num,decimalPlaces){return(num<0?this.eil:this.floor)(num,decimalPlaces);},//使用定点表示法格式化toFixed:函数(num,decimalPlaces){返回this.round(num,decimalPlaces).toFixed(decimalPlace);}};})();//一半的测试舍入console.log(DecimalPrecision2.round(0.5));//1.console.log(DecimalPrecision2.round(-0.5));//-1.//测试非常小的数字console.log(DecimalPrecision2.ceil(1e-8,2)==0.01);console.log(DecimalPrecision2.floor(1e-8,2)==0);//测试简单案例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);//测试圆形边壳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);//测试ceil的边缘案例console.log(DecimalPrecision2.ceil(9.13,2)==9.13);console.log(DecimalPrecision2.ceil(65.18,2)==65.18);console.log(DecimalPrecision2.ceil(-2.26,2)==-2.26);console.log(DecimalPrecision2.ceil(-18.15,2)==-18.15);//测试地板边缘案例console.log(DecimalPrecision2.floor(2.26,2)==2.26);console.log(DecimalPrecision2.floor(18.15,2)==18.15);console.log(DecimalPrecision2.floor(-9.13,2)==-9.13);console.log(DecimalPrecision2.floor(-65.18,2)==-65.18);//trunc的边缘用例测试console.log(DecimalPrecision2.trunc(2.26,2)==2.26);console.log(DecimalPrecision2.trunc(18.15,2)==18.15);console.log(DecimalPrecision2.trunc(-2.26,2)==-2.26);console.log(DecimalPrecision2.trunc(-18.15,2)==-18.15);//测试到数十和数百console.log(DecimalPrecision2.round(1262.48,-1)==1260);console.log(DecimalPrecision2.round(1262.48,-2)==1300);//测试到Fixed()console.log(DecimalPrecision2.toFixed(1.005,2)==“1.01”);

解决方案3:双舍入

此解决方案使用toPrecision()方法去除浮点舍入错误。

//解决方案3var DecimalPrecision3=(函数){if(Math.trunc==未定义){Math.trunc=函数(v){返回v<0?数学ceil(v):数学floor(v);};}var功率=[1e0、1e1、1e2、1e3、1e4、1e5、1e6、1e7,1e8、1e9、1e10、1e11、1e12、1e13、1e14、1e15,1e16、1e17、1e18、1e19、1e20、1e21、1e22];var intpow10=函数(功率){/*不在查找表中*/如果(功率<0 | |功率>22){return Math.pow(10,幂);}返回功率[功率];};//消除二进制浮点精度。var stripError=函数(num){if(数字.isInteger(num))返回num;返回parseFloat(num.toPrecision(15));};var decimalAdjust=函数本身(type,num,decimalPlaces){if(类型==“round”&&num<0)return-我自己(type,-num,decimalPlaces);var p=intpow10(小数位数||0);var n=stripError(num*p);返回数学[type](n)/p;};返回{//十进制舍入(距离零的一半)round:函数(num,decimalPlaces){return decimalAdjust('round',num,decimalPlaces);},//十进制ceilceil:函数(num,decimalPlaces){return decimalAdjust('eil',num,decimalPlaces);},//十进制楼层floor:函数(num,decimalPlaces){return decimalAdjust('floor',num,decimalPlaces);},//十进制截断trunca:函数(num,decimalPlaces){return decimalAdjust('trunc',num,decimalPlaces);},//使用定点表示法格式化toFixed:函数(num,decimalPlaces){return decimalAdjust('round',num,decimalPlaces).toFixed(decimalPlace);}};})();//一半的测试舍入console.log(DecimalPrecision3.round(0.5));//1.console.log(DecimalPrecision3.round(-0.5));//-1.//测试非常小的数字console.log(DecimalPrecision3.ceil(1e-8,2)==0.01);console.log(DecimalPrecision3.floor(1e-8,2)==0);//测试简单案例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);//测试圆形边壳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);//测试ceil的边缘案例console.log(DecimalPrecision3.ceil(9.13,2)==9.13);console.log(DecimalPrecision3.ceil(65.18,2)==65.18);console.log(DecimalPrecision3.ceil(-2.26,2)==-2.26);console.log(DecimalPrecision3.ceil(-18.15,2)==-18.15);//测试地板边缘案例console.log(DecimalPrecision3.floor(2.26,2)==2.26);console.log(DecimalPrecision3.floor(18.15,2)==18.15);console.log(DecimalPrecision3.floor(-9.13,2)==-9.13);console.log(DecimalPrecision3.floor(-65.18,2)==-65.18);//trunc的边缘用例测试console.log(DecimalPrecision3.trunc(2.26,2)==2.26);console.log(DecimalPrecision3.trunc(18.15,2)==18.15);console.log(DecimalPrecision3.trunc(-2.26,2)==-2.26);console.log(DecimalPrecision3.trunc(-18.15,2)==-18.15);//测试到数十和数百console.log(DecimalPrecision3.round(1262.48,-1)==1260);console.log(DecimalPrecision3.round(1262.48,-2)==1300);//测试到Fixed()console.log(DecimalPrecision3.toFixed(1.005,2)==“1.01”);

解决方案4:双舍入v2

此解决方案与解决方案3类似,但它使用了自定义的toPrecision()函数。

//解决方案4var DecimalPrecision4=(函数){if(Math.trunc==未定义){Math.trunc=函数(v){返回v<0?数学ceil(v):数学floor(v);};}var功率=[1e0、1e1、1e2、1e3、1e4、1e5、1e6、1e7,1e8、1e9、1e10、1e11、1e12、1e13、1e14、1e15,1e16、1e17、1e18、1e19、1e20、1e21、1e22];var intpow10=函数(功率){/*不在查找表中*/如果(功率<0 | |功率>22){return Math.pow(10,幂);}返回功率[功率];};var toPrecision=函数(num,significantDigits){//提前返回±0、NaN和Infinity。if(!num||!Number.isFinite(num))返回num;//计算小数点的移位(sf-leftSidedDigits)。var shift=significantDigits-1-数学楼层(数学log10(数学abs(num)));//如果舍入到相同或更高的精度,则返回。var decimalPlaces=0;for(var p=1;num!=数学舍入(num*p)/p;p*=10)小数位数++;if(shift>=小数位数)返回num;//舍入为“移位”小数位数var scale=intpow10(数学.abs(移位));返回移位>0?数学舍入(num*刻度)/刻度:数学舍入(num/scale)*刻度;};//消除二进制浮点精度。var stripError=函数(num){if(数字.isInteger(num))返回num;返回精度(num,15);};var decimalAdjust=函数本身(type,num,decimalPlaces){if(类型==“round”&&num<0)return-我自己(type,-num,decimalPlaces);var p=intpow10(小数位数||0);var n=stripError(num*p);返回数学[type](n)/p;};返回{//十进制舍入(距离零的一半)round:函数(num,decimalPlaces){return decimalAdjust('round',num,decimalPlaces);},//十进制ceilceil:函数(num,decimalPlaces){return decimalAdjust('eil',num,decimalPlaces);},//十进制楼层floor:函数(num,decimalPlaces){return decimalAdjust('floor',num,decimalPlaces);},//十进制截断trunca:函数(num,decimalPlaces){return decimalAdjust('trunc',num,decimalPlaces);},//使用定点表示法格式化toFixed:函数(num,decimalPlaces){return decimalAdjust('round',num,decimalPlaces).toFixed(decimalPlace);}};})();//一半的测试舍入console.log(DecimalPrecision4.round(0.5));//1.console.log(DecimalPrecision4.round(-0.5));//-1.//测试非常小的数字console.log(DecimalPrecision4.ceil(1e-8,2)==0.01);console.log(DecimalPrecision4.floor(1e-8,2)==0);//测试简单案例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);//测试圆形边壳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);//测试ceil的边缘案例console.log(DecimalPrecision4.ceil(9.13,2)==9.13);console.log(DecimalPrecision4.ceil(65.18,2)==65.18);console.log(DecimalPrecision4.ceil(-2.26,2)==-2.26);console.log(DecimalPrecision4.ceil(-18.15,2)==-18.15);//测试地板边缘案例console.log(DecimalPrecision4.floor(2.26,2)==2.26);console.log(DecimalPrecision4.floor(18.15,2)==18.15);console.log(DecimalPrecision4.floor(-9.13,2)==-9.13);console.log(DecimalPrecision4.floor(-65.18,2)==-65.18);//trunc的边缘用例测试console.log(DecimalPrecision4.trunc(2.26,2)==2.26);console.log(DecimalPrecision4.trunc(18.15,2)==18.15);console.log(DecimalPrecision4.trunc(-2.26,2)==-2.26);console.log(DecimalPrecision4.trunc(-18.15,2)==-18.15);//测试到数十和数百console.log(DecimalPrecision4.round(1262.48,-1)==1260);console.log(DecimalPrecision4.round(1262.48,-2)==1300);//测试到Fixed()console.log(DecimalPrecision4.toFixed(1.005,2)==“1.01”);

基准

http://jsbench.github.io/#31ec3a8b3d22bd840f8e6822e681a3ac

下面是一个比较Chrome109.0.0.0上上述解决方案每秒操作数的基准。使用Number.EPSILON的舍入功能至少快10x-20x。显然,所有浏览器都不同,因此您的里程可能会有所不同。

(注:越多越好)

感谢@Mike添加基准的截图。

其他回答

根据评论中已给出的答案,链接至http://jsfiddle.net/AsRqx/,下面的一个对我来说很好。

function C(num)
{
    return +(Math.round(num + "e+2") + "e-2");
}

function N(num, places)
{
    return +(Math.round(num + "e+" + places) + "e-" + places);
}

C(1.005);

N(1.005, 0);
N(1.005, 1); // Up to 1 decimal places
N(1.005, 2); // Up to 2 decimal places
N(1.005, 3); // Up to 3 decimal places

最简单的方法是使用toFixed,然后使用Number函数去除尾随零:

const number = 15.5;
Number(number.toFixed(2)); // 15.5
const number = 1.7777777;
Number(number.toFixed(2)); // 1.78

const formattedNumber=数学舍入(数字*100)/100;

对这个答案稍作修改,似乎效果不错。

作用

function roundToStep(value, stepParam) {
   var step = stepParam || 1.0;
   var inv = 1.0 / step;
   return Math.round(value * inv) / inv;
}

用法

roundToStep(2.55) = 3
roundToStep(2.55, 0.1) = 2.6
roundToStep(2.55, 0.01) = 2.55

这对我(TypeScript)起到了作用:

round(decimal: number, decimalPoints: number): number{
    let roundedValue = Math.round(decimal * Math.pow(10, decimalPoints)) / Math.pow(10, decimalPoints);

    console.log(`Rounded ${decimal} to ${roundedValue}`);
    return roundedValue;
}

样本输出

Rounded 18.339840000000436 to 18.34
Rounded 52.48283999999984 to 52.48
Rounded 57.24612000000036 to 57.25
Rounded 23.068320000000142 to 23.07
Rounded 7.792980000000398 to 7.79
Rounded 31.54157999999981 to 31.54
Rounded 36.79686000000004 to 36.8
Rounded 34.723080000000124 to 34.72
Rounded 8.4375 to 8.44
Rounded 15.666960000000074 to 15.67
Rounded 29.531279999999924 to 29.53
Rounded 8.277420000000006 to 8.28