Rounding a number to N decimal places means snapping it to the nearest value on a grid of that fineness — 1.005 to two places is 1.01. The obvious one-liner, Math.round(value * 100) / 100, quietly returns the wrong answer for that exact case, because 1.005 cannot be stored precisely in binary floating point and the stray multiply drifts it the wrong way. You will build roundToPrecision = { round, floor, ceil }, three functions that round to a given number of places correctly, in every direction.
const roundToPrecision: {
round(value: number, digits?: number): number; // digits defaults to 0
floor(value: number, digits?: number): number;
ceil(value: number, digits?: number): number;
};
roundToPrecision.round(1.005, 2); // 1.01 (the classic — naive Math.round gives 1)
roundToPrecision.round(1234.5, -2); // 1200 (negative digits round to hundreds)
roundToPrecision.floor(-1.15, 1); // -1.2 (floor goes down, toward -Infinity)
roundToPrecision.ceil(-1.15, 1); // -1.1 (ceil goes up, toward +Infinity)
roundToPrecision.round(2.4); // 2 (digits defaults to 0)
roundToPrecision.round(1e-8, 2); // 0 (exponent-notation input is fine)
roundToPrecision.round(Infinity); // Infinity (non-finite values pass through)
round(15, -1) is 20).round follows Math.round: ties go toward positive infinity, so round(2.5) is 3 and round(-2.5) is -2.floor always moves toward negative infinity and ceil toward positive infinity, which for negatives means floor(-1.15, 1) is -1.2 and ceil(-1.15, 1) is -1.1.Math.round(value * 10 ** digits) / 10 ** digits. For value = 1.005 the product is 100.4999999..., which rounds the wrong way. Shift the decimal point without multiplying instead.NaN, Infinity, and -Infinity unchanged; you do not round them.value is always a number and digits an integer; you do not need to validate types or reject strings.