Reputation: 1714
I'm testing toFixed() method of javascript. The result is seen as below.
(49.175).toFixed(2) => "49.17"
(49.775).toFixed(2) => "49.77"
(49.185).toFixed(2) => "49.19"
(49.785).toFixed(2) => "49.78"
(49.1175).toFixed(3) => "49.117"
(49.1775).toFixed(3) => "49.178"
(49.1185).toFixed(3) => "49.118"
(49.1785).toFixed(3) => "49.178"
I made this test at chrome browser, and I'm surprised with the result. I couldn't catch the logic. It doesn't fit neither 'round away from zero' nor 'round to even'. What is the rule behind of 'toFixed()' function ?
Upvotes: 2
Views: 1343
Reputation: 2148
About toFixed
Returns a String containing this Number value represented in decimal fixed-point notation with fractionDigits digits after the decimal point. If fractionDigits is undefined, 0 is assumed. Specifically, perform the following steps:
Algorithm Number.prototype.toFixed (fractionDigits)
: https://www.ecma-international.org/ecma-262/5.1/#sec-15.7.4.5
The length property of the toFixed method is 1.
An implementation is permitted to extend the behaviour of toFixed for values of fractionDigits less than 0 or greater than 20. In this case toFixed would not necessarily throw RangeError for such values.
NOTE The output of toFixed may be more precise than toString for some values because toString only prints enough significant digits to distinguish the number from adjacent number values.
JS Work Around
function fix(n, p) {
return (+(Math.round(+(n + 'e' + p)) + 'e' + -p)).toFixed(p);
}
let exampleA = fix(49.1175, 3);
let exampleB = fix(49.1775, 3);
let exampleC = fix(49.775, 2);
const random = Math.random();
console.log(exampleA);
console.log(exampleB);
console.log(exampleC);
console.log('Before:', random, 'After Custom =>', fix(random, 3), 'Default:', random.toFixed(3));
// 49.118
// 49.178
// 49.78
Precision Needed
I suggest just simply porting set precision
from C++ to a Node.JS Module.
child_process
also in Node.JS to call a C++ program with an argument, and have the C++ run a function to convert the value and output to the console.Upvotes: 3
Reputation: 3572
The issue is, that the numbers you entered do not exist! On scanning, they are (binary) rounded to the nearest possible/existing number. toPrecision(18)
shows the numbers after scanning more exact:
(49.175).toPrecision(18); // "49.1749999999999972" => "49.17"
(49.775).toPrecision(18); // "49.7749999999999986" => "49.77"
(49.185).toPrecision(18); // "49.1850000000000023" => "49.19"
(49.785).toPrecision(18); // "49.7849999999999966" => "49.78"
So the number is rounded 2 times: First on scanning, and then by toFixed()
.
Upvotes: 3
Reputation: 17190
From the MDN:
toFixed()
returns a string representation ofnumObj
that does not use exponential notation and has exactly digits digits after the decimal place. The number is rounded if necessary, and the fractional part is padded with zeros if necessary so that it has the specified length. IfnumObj
is greater or equal to 1e+21, this method simply callsNumber.prototype.toString()
and returns a string in exponential notation.
And later you can read:
WARNING: Floating point numbers cannot represent all decimals precisely in binary which can lead to unexpected results such as
0.1 + 0.2 === 0.3
returning false.
The above warning in conjuntion with the round logic (maybe arithmetical operations on the number) will explain the different behaviours you are experimenting in the rounding procedure (you can read it here).
Upvotes: 0