sindresorhus/round-to

Floating-point roundoff errors #20

amrali-eg posted onGitHub

I found these bugs in the code:

roundTo(0.597/6, 3) === 0.1
// false
roundTo.up((0.1+0.2)*10, 0) === 3
// false
roundTo.down((0.1+0.7)*10, 0) === 8
// false

I suggest to use the toPrecision() method to preround the result to 15 significant digits. This will strip the floating-point round-off errors in the intermediate calculations.

Please refer to my answer on stackoverflow https://stackoverflow.com/a/48764436

'use strict';

function round(method, number, precision) {
    if (typeof number !== 'number') {
        throw new TypeError('Expected value to be a number');
    }

    if (precision === Infinity) {
        return number;
    }

    if (!Number.isInteger(precision)) {
        throw new TypeError('Expected precision to be an integer');
    }

    const isRoundingAndNegative = method === 'round' && number < 0;
    if (isRoundingAndNegative) {
        number = Math.abs(number);
    }

    /*
    let exponent;
    [number, exponent] = `${number}e`.split('e');
    let result = Math[method](`${number}e${Number(exponent) + precision}`);

    [number, exponent] = `${result}e`.split('e');
    result = Number(`${number}e${Number(exponent) - precision}`);
    */

    let power = Math.pow(10, precision);

    // preround the result to 15 significant digits to strip the floating-point
    // round-off errors in the intermediate calculations.
    let result = Math[method](+(number * power).toPrecision(15)) / power;

    if (isRoundingAndNegative) {
        result = -result;
    }

    return result;
}

module.exports = round.bind(undefined, 'round');
module.exports.up = round.bind(undefined, 'ceil');
module.exports.down = round.bind(undefined, 'floor');

Fund this Issue

$0.00
Funded

Pull requests