1

这是我们正在使用的舍入函数(取自 stackoverflow 关于如何舍入的答案)。它四舍五入到 2dp(默认情况下)

例如 2.185 应该转到 2.19

function myRound(num, places) {
    if (places== undefined) {
        // default to 2dp
        return  Math.round(num* 100) / 100;
    }
    var mult = Math.pow(10,places);

    return  Math.round(num* mult) / mult;
}

它运行良好,但现在我们在其中发现了一些错误(在 chrome 中和在 IIS 7.5 上作为 jscript 经典 asp 运行)。

例如:

alert(myRound(2.185));      // = 2.19
alert (myRound(122.185));   // = 122.19
alert (myRound(511.185));   // = 511.19
alert (myRound(522.185));   // = 522.18  FAIL!!!!
alert (myRound(625.185));   // = 625.18  FAIL!!!!

有人知道吗:

  1. 为什么会发生这种情况。
  2. 我们如何在没有像这样的随机舍入错误的情况下将一半舍入到 2 dp。

更新:好的,问题的症结在于,在js中,625.185 * 100 = 62518.499999 我们怎样才能克服呢?

4

3 回答 3

3

你的问题不容易解决。发生这种情况是因为 IEEE 双精度数使用无法准确表示所有小数的二进制表示。最接近 625.185 的内部表示是 625.18499999999994543031789362430572509765625,它比 625.185 略小,并且正确的舍入是向下的。


根据您的情况,您可能会摆脱以下情况:

Math.round(Math.round(625.185 * 1000) / 10) / 100    // evaluates to 625.19

然而,这并不完全正确,因为例如,它将从 625.1847 向上舍入到 625.19。仅当您知道输入的小数位不会超过三位时才使用它。


一个更简单的选择是在舍入之前添加一个小 epsilon:

Math.round(625.185 * 100 + 1e-6) / 100

这仍然是一个折衷方案,因为您可能有一个比 625.185 略小的数字,但它可能比第一个解决方案更健壮。不过要注意负数。

于 2013-07-03T09:45:25.837 回答
1

Try using toFixed function on value. example is below:

var value = parseFloat(2.185);
var fixed = value.toFixed(2);
alert(fixed);

I tried and it worked well.

EDIT: You can always transform string to number using parseFloat(stringVar).

EDIT2:

function myRound(num, places) {
    return parseFloat(num.toFixed(places));
}

EDIT 3:

Updated answer, tested and working:

function myRound(num, places) {
    if (places== undefined) {
     places = 2;
    }
    var mult = Math.pow(10,places + 1);
    var mult2 = Math.pow(10,places);
    return  Math.round(num* mult / 10) / mult2;
}

EDIT 4:

Tested on most examples noted in comments:

function myRound(num, places) {
    if (places== undefined) {
     places = 2;
    }
    var mult = Math.pow(10,places);
    var val = num* mult;
    var intVal = parseInt(val);
    var floatVal = parseFloat(val);

    if (intVal < floatVal) {
        val += 0.1;
    }
    return Math.round(val) / mult;
}

EDIT 5: Only solution that I managed to find is to use strings to get round on exact decimal. Solution is pasted below, with String prototype extension method, replaceAt. Please check and let me know if anyone finds some example that is not working.

function myRound2(num, places) {
    var retVal = null;
    if (places == undefined) {
        places = 2;
    }

    var splits = num.split('.');
    if (splits && splits.length <= 2) {
        var wholePart = splits[0];
        var decimalPart = null;
        if (splits.length > 1) {
            decimalPart = splits[1];
        }
        if (decimalPart && decimalPart.length > places) {
            var roundingDigit = parseInt(decimalPart[places]);
            var previousDigit = parseInt(decimalPart[places - 1]);
            var increment = (roundingDigit < 5) ? 0 : 1;
            previousDigit = previousDigit + increment;
            decimalPart = decimalPart.replaceAt(places - 1, previousDigit + '').substr(0, places);
        }
        retVal = parseFloat(wholePart + '.' + decimalPart);
    }

    return retVal;
}

String.prototype.replaceAt = function (index, character) {
    return this.substr(0, index) + character + this.substr(index + character.length);
}
于 2013-07-03T08:56:28.467 回答
0

好的,找到了该问题的“完整”解决方案。

首先,从这里下载 Big.js:https ://github.com/MikeMcl/big.js/

然后修改源代码使其可以与 jscript/asp 一起使用:

/* big.js v2.1.0 https://github.com/MikeMcl/big.js/LICENCE */
var Big = (function ( global ) {
    'use strict';
:
// EXPORT
return Big;
})( this );

然后使用 Big 类型进行计算并使用 Big toFixed(dp),然后转换回一个数字:

var bigMult = new Big (multiplier);
var bigLineStake = new Big(lineStake);
var bigWin = bigLineStake.times(bigMult);
var strWin = bigWin.toFixed(2);  // this does the rounding correctly.
var win = parseFloat(strWin);  // back to a number!

这基本上在其 toFixed 中使用 Bigs 自己的舍入,这似乎在所有情况下都能正常工作。

Shame Big 没有一种无需通过字符串即可转换回数字的方法。

于 2013-07-05T13:46:34.137 回答