这一行代码把数字四舍五入到小数点后两位。但我得到的数字是这样的:10.8、2.4等等。这些都不是我的小数点后两位的想法,所以我怎么能改善以下?

Math.round(price*Math.pow(10,2))/Math.pow(10,2);

我想要10.80、2.40等数字。jQuery的使用对我来说很好。


当前回答

通常,十进制舍入是通过缩放完成的:round(num * p) / p

天真的实现

使用下面带有中间数的函数,您将得到预期的上四舍五入值,或者有时根据输入得到下四舍五入值。

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

函数naiveRound(num, decimalPlaces) { var p =数学。decimalPlaces战俘(10日); 返回数学。Round (num * p) / p; } console.log(naiveRound(1.245, 2));// 1.25 correct(四舍五入) console.log(naiveRound(1.255, 2));// 1.25不正确(应该是1.26)

更好的实现

通过将数字转换为指数表示法中的字符串,正数将按预期四舍五入。 但是要注意,负数四舍五入和正数四舍五入是不同的。

事实上,它的执行基本上相当于“四舍五入”的规则,您将看到round(-1.005, 2)的计算结果为-1,即使round(1.005, 2)的计算结果为1.01。lodash _。圆法使用这种技术。

/** * Round half up ('round half towards positive infinity') * Uses exponential notation to avoid floating-point issues. * Negative numbers round differently than positive numbers. */ function round(num, decimalPlaces) { num = Math.round(num + "e" + decimalPlaces); return Number(num + "e" + -decimalPlaces); } // test rounding of half console.log( round(0.5, 0) ); // 1 console.log( round(-0.5, 0) ); // 0 // testing edge cases console.log( round(1.005, 2) ); // 1.01 console.log( round(2.175, 2) ); // 2.18 console.log( round(5.015, 2) ); // 5.02 console.log( round(-1.005, 2) ); // -1 console.log( round(-2.175, 2) ); // -2.17 console.log( round(-5.015, 2) ); // -5.01

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

// Round half away from zero
function round(num, decimalPlaces) {
    num = Math.round(Math.abs(num) + "e" + decimalPlaces) * Math.sign(num);
    return Number(num + "e" + -decimalPlaces);
}

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

简单地说,我们添加最小的浮动值(= 1.0 ulp;单位在最后的位置)到数字之前舍入。这将移动到数字之后的下一个可表示的值,远离零。

/** * Round half away from zero ('commercial' rounding) * Uses correction to offset floating-point inaccuracies. * Works symmetrically for positive and negative numbers. */ function round(num, decimalPlaces) { var p = Math.pow(10, decimalPlaces); var e = Number.EPSILON * num * p; return Math.round((num * p) + e) / p; } // test rounding of half console.log( round(0.5, 0) ); // 1 console.log( round(-0.5, 0) ); // -1 // testing edge cases console.log( round(1.005, 2) ); // 1.01 console.log( round(2.175, 2) ); // 2.18 console.log( round(5.015, 2) ); // 5.02 console.log( round(-1.005, 2) ); // -1.01 console.log( round(-2.175, 2) ); // -2.18 console.log( round(-5.015, 2) ); // -5.02

这是为了抵消十进制数编码过程中可能出现的隐式舍入误差,特别是十进制数的最后一个位置有“5”的数字,如1.005、2.675和16.235。实际上,十进制系统中的1.005在64位二进制浮点数中被编码为1.0049999999999999;而十进制系统中的1234567.005被编码为64位二进制浮点的1234567.0049999998882413。

值得注意的是,二进制舍入误差的最大值取决于(1)数字的大小和(2)相对机器的ε(2^-52)。

其他回答

我不知道为什么我不能在之前的答案上添加评论(也许我是绝望的盲人,我不知道),但我用@Miguel的答案想出了一个解决方案:

function precise_round(num,decimals) {
   return Math.round(num*Math.pow(10, decimals)) / Math.pow(10, decimals);
}

它的两条评论(来自@bighostkim和@Imre):

precise_round(1.275,2)不返回1.28的问题 precise_round(6,2)不返回6.00(正如他想要的)的问题。

我最终的解决方案如下:

function precise_round(num,decimals) {
    var sign = num >= 0 ? 1 : -1;
    return (Math.round((num*Math.pow(10,decimals)) + (sign*0.001)) / Math.pow(10,decimals)).toFixed(decimals);
}

正如你所看到的,我必须添加一点“更正”(这不是它是什么,但因为数学。round是有损的-你可以在jsfiddle.net上检查它-这是我知道如何“修复”它的唯一方法)。它在已经填充的数字上加了0.001,所以它在十进制值的右边加了一个1 3个0。所以使用起来应该是安全的。

之后,我添加了. tofixed(十进制),以始终以正确的格式输出数字(具有正确数量的小数)。

差不多就是这样了。好好使用它;)

编辑:增加了负数“更正”功能。

浮点值的问题在于,它们试图用固定数量的比特表示无限数量的(连续的)值。所以自然地,在游戏中一定会有一些损失,你会被一些价值观咬伤。

当计算机将1.275存储为浮点值时,它实际上不会记住它是1.275还是1.27499999999999993,甚至是1.27500000000000002。这些值在四舍五入到两个小数后应该会给出不同的结果,但它们不会,因为对于计算机来说,它们存储为浮点值后看起来完全相同,并且没有办法恢复丢失的数据。任何进一步的计算只会积累这样的不精确性。

因此,如果精度很重要,就必须从一开始就避免使用浮点值。最简单的选择是

使用专门的图书馆 使用字符串存储和传递值(附带字符串操作) 使用整数(例如,你可以传递实际值的百分之一,例如,用美分代替美元)

例如,当使用整数存储百分位数时,查找实际值的函数非常简单:

function descale(num, decimals) {
    var hasMinus = num < 0;
    var numString = Math.abs(num).toString();
    var precedingZeroes = '';
    for (var i = numString.length; i <= decimals; i++) {
        precedingZeroes += '0';
    }
    numString = precedingZeroes + numString;
    return (hasMinus ? '-' : '') 
        + numString.substr(0, numString.length-decimals) 
        + '.' 
        + numString.substr(numString.length-decimals);
}

alert(descale(127, 2));

对于字符串,你需要四舍五入,但它仍然是可控的:

function precise_round(num, decimals) {
    var parts = num.split('.');
    var hasMinus = parts.length > 0 && parts[0].length > 0 && parts[0].charAt(0) == '-';
    var integralPart = parts.length == 0 ? '0' : (hasMinus ? parts[0].substr(1) : parts[0]);
    var decimalPart = parts.length > 1 ? parts[1] : '';
    if (decimalPart.length > decimals) {
        var roundOffNumber = decimalPart.charAt(decimals);
        decimalPart = decimalPart.substr(0, decimals);
        if ('56789'.indexOf(roundOffNumber) > -1) {
            var numbers = integralPart + decimalPart;
            var i = numbers.length;
            var trailingZeroes = '';
            var justOneAndTrailingZeroes = true;
            do {
                i--;
                var roundedNumber = '1234567890'.charAt(parseInt(numbers.charAt(i)));
                if (roundedNumber === '0') {
                    trailingZeroes += '0';
                } else {
                    numbers = numbers.substr(0, i) + roundedNumber + trailingZeroes;
                    justOneAndTrailingZeroes = false;
                    break;
                }
            } while (i > 0);
            if (justOneAndTrailingZeroes) {
                numbers = '1' + trailingZeroes;
            }
            integralPart = numbers.substr(0, numbers.length - decimals);
            decimalPart = numbers.substr(numbers.length - decimals);
        }
    } else {
        for (var i = decimalPart.length; i < decimals; i++) {
            decimalPart += '0';
        }
    }
    return (hasMinus ? '-' : '') + integralPart + (decimals > 0 ? '.' + decimalPart : '');
}

alert(precise_round('1.275', 2));
alert(precise_round('1.27499999999999993', 2));

注意,这个函数舍入到最近邻,与零绑定,而IEEE 754建议舍入到最近邻,与偶数绑定,作为浮点运算的默认行为。这样的修改留给读者练习:)

我从几个月前的这篇文章中得到了一些想法,但这里的答案,以及其他文章/博客的答案都不能处理所有的场景(例如负数和我们的测试人员发现的一些“幸运数字”)。最后,我们的测试人员没有发现下面这个方法有任何问题。粘贴我的代码片段:

fixPrecision: function (value) {
    var me = this,
        nan = isNaN(value),
        precision = me.decimalPrecision;

    if (nan || !value) {
        return nan ? '' : value;
    } else if (!me.allowDecimals || precision <= 0) {
        precision = 0;
    }

    //[1]
    //return parseFloat(Ext.Number.toFixed(parseFloat(value), precision));
    precision = precision || 0;
    var negMultiplier = value < 0 ? -1 : 1;

    //[2]
    var numWithExp = parseFloat(value + "e" + precision);
    var roundedNum = parseFloat(Math.round(Math.abs(numWithExp)) + 'e-' + precision) * negMultiplier;
    return parseFloat(roundedNum.toFixed(precision));
},

我也有代码注释(对不起,我已经忘记了所有的细节)…我把我的答案贴在这里,以供将来参考:

9.995 * 100 = 999.4999999999999
Whereas 9.995e2 = 999.5
This discrepancy causes Math.round(9.995 * 100) = 999 instead of 1000.
Use e notation instead of multiplying /dividing by Math.Pow(10,precision).

/*Due to all told stuff. You may do 2 things for different purposes: When showing/printing stuff use this in your alert/innerHtml= contents: YourRebelNumber.toFixed(2)*/ var aNumber=9242.16; var YourRebelNumber=aNumber-9000; alert(YourRebelNumber); alert(YourRebelNumber.toFixed(2)); /*and when comparing use: Number(YourRebelNumber.toFixed(2))*/ if(YourRebelNumber==242.16)alert("Not Rounded"); if(Number(YourRebelNumber.toFixed(2))==242.16)alert("Rounded"); /*Number will behave as you want in that moment. After that, it'll return to its defiance. */

我没有找到这个问题的准确解决方案,所以我自己创造了一个:

function inprecise_round(value, decPlaces) {
  return Math.round(value*Math.pow(10,decPlaces))/Math.pow(10,decPlaces);
}

function precise_round(value, decPlaces){
    var val = value * Math.pow(10, decPlaces);
    var fraction = (Math.round((val-parseInt(val))*10)/10);

    //this line is for consistency with .NET Decimal.Round behavior
    // -342.055 => -342.06
    if(fraction == -0.5) fraction = -0.6;

    val = Math.round(parseInt(val) + fraction) / Math.pow(10, decPlaces);
    return val;
}

例子:

function inprecise_round(value, decPlaces) { return Math.round(value * Math.pow(10, decPlaces)) / Math.pow(10, decPlaces); } function precise_round(value, decPlaces) { var val = value * Math.pow(10, decPlaces); var fraction = (Math.round((val - parseInt(val)) * 10) / 10); //this line is for consistency with .NET Decimal.Round behavior // -342.055 => -342.06 if (fraction == -0.5) fraction = -0.6; val = Math.round(parseInt(val) + fraction) / Math.pow(10, decPlaces); return val; } // This may produce different results depending on the browser environment console.log("342.055.toFixed(2) :", 342.055.toFixed(2)); // 342.06 on Chrome & IE10 console.log("inprecise_round(342.055, 2):", inprecise_round(342.055, 2)); // 342.05 console.log("precise_round(342.055, 2) :", precise_round(342.055, 2)); // 342.06 console.log("precise_round(-342.055, 2) :", precise_round(-342.055, 2)); // -342.06 console.log("inprecise_round(0.565, 2) :", inprecise_round(0.565, 2)); // 0.56 console.log("precise_round(0.565, 2) :", precise_round(0.565, 2)); // 0.57