Assuming toFixed casts to 32-bit float;
Check with this utility...
19373.315 is stored as 19373.314453125 (an error of -0.000546875) in 32-bit floating point format.
This is despite (19373.315).toFixed(4)
coming out as 19373.3150
.
Even if this is "expected" or "intended", I'd still report it as a bug.
It should use a double during the rounding check, and thus proper rounding during conversion to fixed string.
I think the spec even says so. :\
In the V8 javascript engine source, the Number.prototype.toFixed
function invokes DoubleToFixedCString
in this file ...
There's probably some inappropriate optimization in there... (Looking into it.)
I'd suggest submitting an additional test case for V8 with 19373.315 specifically.
(19373.3150).toFixed(39)
yields 19373.314999999998690327629446983337402343750.
Rounding occurs once to bring it up to 19373.315 - which is correct - but not at the right digit when rounding to 2 digits.
I think this should have a second pass on rounding here to catch edge cases like this. I think it might have to round to n+1
digits, then again to n
digits. Maybe there's some other clever way to fix it though.
function toFixedFixed(a,n) {
return (a|0) + parseFloat((a % 1).toFixed(n+1)).toFixed(n).substr(1);
}
console.log(toFixedFixed(19373.315,2)); // "19373.32"
console.log(toFixedFixed(19373.315,3)); // "19373.315"
console.log(toFixedFixed(19373.315,4)); // "19373.3150"
console.log(toFixedFixed(19373.315,37)); // "19373.3149999999986903276294469833374023438"
console.log(toFixedFixed(19373.315,38)); // "19373.31499999999869032762944698333740234375"
console.log(toFixedFixed(19373.315,39)); // "19373.314999999998690327629446983337402343750"
(Adopted from my comments on Vahid Rahmani's answer, who is correct.)