This is not a question about how floating point works. Nor is it a question about why 19.95 * 0.1 produces 19.900000000004. I already get that floating point math isn't a perfect presentation. The question is rather to ask an expert their intuition why this particular case works.
Recently I wanted to round numbers to an arbitrary amount for a stepping slider. I had this code
steppedV = Math.round(v / step) * step
I found it's not very good because of floating point math issues. For example v = 19.94
step = 0.1
returns 19.900000000000002
I thought I was kind of out of luck but then just for fun I tried this
steppedV = Math.round(v / step) / (1 / step)
And surprisingly, for the test cases I've been using it's producing better results. For the same inputs as above I get back 19.9
For all my cases that were not rounding to my test step values it's all working now. Can someone who really groks how floating point math works explain why / (1 / step)
produces more accurate results and * step
does not? As someone who understands floating point math is this result intuitive? Am I just getting lucky or as an expert would you have known to do the division of (1 / step) for a better result?
function test(start, end, inc, step) {
const addRow = makeTable(step, 'div', 'mult');
for (let v = start; v <= end; v += inc) {
const steppedVMult = Math.round(v / step) * step;
const steppedVDiv = Math.round(v / step) / (1 / step);
addRow(v, steppedVDiv, steppedVMult);
}
}
test(0, 2, 0.03, 0.1);
test(0, 0.2, 0.003, 0.01);
test(0, 0.02, 0.0003, 0.001);
test(0, 0.002, 0.00003, 0.0001);
function c(tag, children = [], text = '') {
const e = document.createElement(tag);
e.textContent = text;
children.forEach(c => e.appendChild(c));
return e;
}
function makeTable(...args) {
const makeRow = arr => c('tr', arr.map(v => c('td', [], v)));
const tbody = c('tbody');
const table = c('table', [
c('thead', [makeRow(args)]),
tbody,
]);
document.body.appendChild(table);
return function(...args) {
tbody.appendChild(makeRow(args));
};
}
body { font-family: monospace; }
table { border-collapse: collapse; margin-bottom: 10px }
thead { font-weight: bold; }
td { border: 1px solid #888; padding: 3px; }