Last active
March 8, 2018 13:53
-
-
Save eip/803daf4d3a571cf25de8554a222d44ba to your computer and use it in GitHub Desktop.
Round (decimal adjustment) of the numbers test and performance
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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