自定义舍入到最接近的0.05

时间:2018-11-07 22:21:16

标签: javascript

我想根据以下条件进行自定义舍入到0.05附近,这很难解释,但是下面的例子很容易理解。

12.910 - 12.90
12.920 - 12.90
12.930 - 12.90
12.940 - 12.90
12.941 - 12.95
12.950 - 12.95

12.960 - 12.95
12.970 - 12.95
12.980 - 12.95
12.990 - 12.95
12.991 - 13.00 
13.000 - 13.00 

我尝试了几种功能,但将12.98舍入为13.00。

function   customRound( num) {
    return Math.round(num * 20) / 20;
}

3 个答案:

答案 0 :(得分:2)

从视觉上来看,您的舍入算法看起来像这样:
A number line, extending from 12.94 to 13.04, with the interval (12.94, 12.99] marked in red and the interval (12.99, 13.04] marked in blue. A red dot is at 12.95, and a blue dot is at 13.00.
该点是您要舍入到该间隔的位置。 标记间隔的开放端,] 封闭端。(12.99属于红色间隔。)我们将通过操纵与{ {1}}。

首先,让我们处理整数。

DevExpress.XtraReports.v18.1

The same number line, but the scale has changed. Line: [1294, 1304], red: (1294, 1299] with dot at 1295, blue: (1299, 1304] with dot at 1300.

您的舍入间隔是左开和右开的,而Math.floor是左开和右开的。我们可以乘以−1来翻转直线以进行匹配:

num * 100

The number line has been flipped. Line: [-1304, -1294], blue: [-1304, -1299) with dot at -1300, red: [-1299, -1294) with dot at -1295.

您的舍入间隔的长度为5,因此我们需要将间隔的末端设为5的倍数...

Math.floor

The intervals have shifted 1 towards negative infinity. Line: [-1305, -1295], blue: [-1305, -1300) with dot at -1301, red: [-1299, -1294) with dot at -1296.

...除以5以匹配 num * 100 * -1 ⇒ num * -100

num * -100 - 1

The scale has changed again. Line: [-261, -259], blue: [-261, -260) with dot at -260.2, red: [-260, -259) with dot at -259.2.

现在我们可以发言。

Math.floor

The same number line, now with Math.floor arrows! Blue arrow pointing to -261, red arrow pointing to -260.

乘以5可以放大到原始大小:

 (num * -100 - 1  ) / 5
⇒ num *  -20 - 0.2

Another scale change. Line: [-1305, -1295], blue: [-1305, -1300) with dot at -1301 and arrow at -1305, red: [-1299, -1294) with dot at -1296 and arrow at -1300.

通过添加4将返回值移到点上:

return Math.floor(num * -20 - 0.2);

The arrows have moved. Blue arrow pointing to -1301, red arrow pointing to -1296.

撤消我们之前所做的对齐:

return Math.floor(num * -20 - 0.2) * 5;

The intervals have shifted 1 towards positive infinity. Line: [-1304, -1294], blue: [-1304, -1299) with dot and arrow at -1300, red: [-1299, -1294) with dot and arrow at -1295.

撤消翻转:

return Math.floor(num * -20 - 0.2) * 5 + 4;

The number line has been flipped again. Line: [1294, 1304], red: (1294, 1299] with dot and arrow at 1295, blue: (1299, 1304] with dot and arrow at 1300.

将整个结果除以100,即可恢复原始比例:

  return Math.floor(num * -20 - 0.2) * 5 + 4 + 1;
⇒ return Math.floor(num * -20 - 0.2) * 5 + 5;

The original number line is back, now with arrows pointing at the dots. Line: [12.94, 13.04], red: (12.94, 12.99] with arrow and dot at 12.95, blue: (12.99, 13.04] with arrow and dot at 13.00.

使用Robin Zigmond的测试框架,

  return (Math.floor(num * -20 - 0.2) *  5 + 5) * -1;
⇒ return  Math.floor(num * -20 - 0.2) * -5 - 5;

答案 1 :(得分:1)

如果您确实打算将所有数字舍入到.05的最接近的.01之内,请尝试以下操作,以获取其使用的数字的精度Is there a reliable way in JavaScript to obtain the number of decimal places of an arbitrary number?

function decimalPlaces(n) {
  var s = "" + (+n);
  var match = /(?:\.(\d+))?(?:[eE]([+\-]?\d+))?$/.exec(s);
  if (!match) { return 0; }  
  return Math.max(
      0,  // lower limit.
      (match[1] == '0' ? 0 : (match[1] || '').length)  
      - (match[2] || 0));  
}

var test = 12.941;
var factor = Math.pow(10,decimalPlaces(test));
var remainder = ((test * factor) % (.05 * factor))/factor;

var result;

if (remainder>.04) {
    result = Math.round(test*20)/20;
} else {
    result = (test*factor - remainder*factor)/factor;
}

console.log('result is:',result);

答案 2 :(得分:1)

据我从您的示例可以看出,所需的行为似乎是“向上舍入到最接近的0.01,然后将结果舍入到最接近的0.05”。

这可以如下实现。如您所见,它与您的示例完全吻合(我什至都​​以相同的方式对其进行了格式设置)-但请告诉我,如果我的手法不对头。

function customRound(num) {
    var intermediateResult = Math.ceil(num*100)/100;
    return Math.floor(intermediateResult*20)/20;
}

// test desired results
var tests = [12.91, 12.92, 12.93, 12.94, 12.941, 12.95, 12.96, 12.97, 12.98, 12.99, 12.991, 13];

for (var i=0; i<tests.length; i++) {
  console.log(`${tests[i].toFixed(3)} - ${customRound(tests[i]).toFixed(2)}`);
}