Skip to content

Instantly share code, notes, and snippets.

@eip
Last active March 8, 2018 13:53
Show Gist options
  • Save eip/803daf4d3a571cf25de8554a222d44ba to your computer and use it in GitHub Desktop.
Save eip/803daf4d3a571cf25de8554a222d44ba to your computer and use it in GitHub Desktop.
Round (decimal adjustment) of the numbers test and performance
/* eslint no-bitwise: "off", no-plusplus: "off", no-console: "off", no-unused-vars: warn */
const Benchmark = typeof window === 'object' ? window.Benchmark : require('benchmark'); // https://benchmarkjs.com
function roundFn01(value, precision) { // Simple version
const mult = +`1e${~~+precision || 0}`;
const v = +value;
return Number.isNaN(v) ? NaN : Math.round((v || 0) * mult) / mult;
}
function roundFn02(value, exp) { // Original MDN version
/* eslint-disable */
// If the exp is undefined or zero...
if (typeof exp === 'undefined' || +exp === 0) {
return Math.round(value);
}
value = +value;
exp = +exp;
// If the value is not a number or the exp is not an integer...
if (value === null || isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0)) {
return NaN;
}
// Shift
value = value.toString().split('e');
value = Math.round(+(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp)));
// Shift back
value = value.toString().split('e');
return +(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp));
/* eslint-enable */
}
function roundFn03(value, precision) { // Compact MDN version
const [v, e] = [+value, ~~+precision || 0];
if (Number.isNaN(v)) return v;
if (e === 0) return Math.round(v);
let [m, n] = [...v.toString().split('e'), '0'];
[m, n] = [...Math.round(`${m}e${+n + e}`).toString().split('e'), '0'];
return +`${m}e${+n - e}`;
}
function roundFn04(value, precision) { // Optimized for performance MDN version
const v = +value;
const e = ~~+precision || 0;
if (Number.isNaN(v)) return v;
if (e === 0) return Math.round(v);
let vs = `${v}`;
let ep = vs.indexOf('e');
vs = `${Math.round(`${ep > 0 ? `${vs.slice(0, ep)}e${+vs.slice(ep + 1) + e}` : `${vs}e${e}`}`)}`;
ep = vs.indexOf('e');
return +(ep > 0 ? `${vs.slice(0, ep)}e${+vs.slice(ep + 1) - e}` : `${vs}e${-e}`);
}
function test(round) {
const cases = [
[123.456789, 6, 123.456789],
[123.456789, 5, 123.45679],
[123.456789, 4, 123.4568],
[123.456789, 3, 123.457],
[123.456789, 2, 123.46],
[123.456789, 1, 123.5],
[123.456789, 0, 123],
[1.555, 2, 1.56],
[2.555, 2, 2.56],
[11.555, 2, 11.56],
[123.555, 2, 123.56],
[1.000005, 5, 1.00001],
[1.00005, 4, 1.0001],
[1.0005, 3, 1.001],
[1.005, 2, 1.01],
[1.05, 1, 1.1],
[1.5, 0, 2],
[-1.000005, 5, -1],
[-1.00005, 4, -1],
[-1.0005, 3, -1],
[-1.005, 2, -1],
[-1.05, 1, -1],
[-1.5, 0, -1],
[1.5555, 3, 1.556],
[0.01499999999999999, 2, 0.01],
[0.015000000000000001, 2, 0.02],
[0.015, 2, 0.02],
[123.456789, -1, 120],
[123.456789, -2, 100],
[123.456789, -3, 0],
[500, -3, 1000],
[undefined, undefined, NaN],
[null, null, 0],
['', '', 0],
[undefined, 0, NaN],
['xxx', 'yyy', NaN],
['true', 0, NaN],
[true, 0, 1],
[123.45, undefined, 123],
[[123.45], undefined, 123],
[123.45, 1.99, 123.5],
[123.45, -1.99, 120],
[1.23456789e20, 0, 123456789000000000000],
[1.23456789e21, 0, 1.23456789e+21],
[1.23456789e20, 1, 123456789000000000000],
[1.23456789e21, 1, 1.23456789e+21],
[1.23456789e-6, 14, 0.00000123456789],
[1.23456789e-7, 14, 1.2345679e-7],
[1.23456789e-8, 14, 1.234568e-8],
[-1.2345655e-8, 14, -1.234565e-8]
];
let errors = 0;
console.info(`Testing ${round.name} function`);
cases.forEach(([v, d, vr]) => {
try {
const r = round(v, d);
if (!(r === vr || (Number.isNaN(r) && Number.isNaN(vr)))) {
console.warn(`${round.name}(${v}, ${d}) returns ${r} instead of ${vr}`);
++errors;
}
} catch (e) {
console.error(`${round.name}(${v}, ${d}): ${e}`);
++errors;
}
});
if (!errors) console.info('--- No errors!');
}
function testRandom(round) {
let runTimes = 1000;
let errors = 0;
console.info(`Testing ${round.name} function on random data...`);
while (runTimes-- && errors < 10) {
let v;
let d;
try {
[v, d] = [Math.random() * 10 * (Math.random() >= 0.5 ? 1 : -1), ~~(Math.random() * 8)];
const r = round(v, d);
if (`${r}`.length > d + (r > 0 ? 2 : 3)) {
console.warn(`${round.name}(${v}, ${d}) returns ${r}`);
++errors;
}
} catch (e) {
console.error(`${round.name}(${v}, ${d}): ${e}`);
++errors;
}
}
if (!errors) console.info('--- No errors!');
}
console.clear();
test(roundFn01);
test(roundFn02);
test(roundFn03);
test(roundFn04);
testRandom(roundFn01);
testRandom(roundFn02);
testRandom(roundFn03);
testRandom(roundFn04);
const suite = new Benchmark.Suite();
// tests
suite.add('roundFn01', () => roundFn01(Math.random() * 10 * (Math.random() >= 0.5 ? 1 : -1), ~~(Math.random() * 8)))
.add('roundFn02', () => roundFn02(Math.random() * 10 * (Math.random() >= 0.5 ? 1 : -1), ~~(Math.random() * 8)))
.add('roundFn03', () => roundFn03(Math.random() * 10 * (Math.random() >= 0.5 ? 1 : -1), ~~(Math.random() * 8)))
.add('roundFn04', () => roundFn04(Math.random() * 10 * (Math.random() >= 0.5 ? 1 : -1), ~~(Math.random() * 8)))
// listeners
.on('cycle', (event) => { console.log(String(event.target)); })
.on('complete', function complete() {
console.log(`Fastest is ${this.filter('fastest').map('name').join(', ')}`);
})
// run async
.run({ async: true });
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment