serefbilge
serefbilge

Reputation: 1714

What is the rule behind toFixed() function

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

Answers (3)

ABC
ABC

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.

    • If the toFixed method is called with more than one argument, then the behaviour is undefined (see clause 15).

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.

  • You could simply rig up and use a 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

Wiimm
Wiimm

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

Shidersz
Shidersz

Reputation: 17190

From the MDN:

toFixed() returns a string representation of numObj 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. If numObj is greater or equal to 1e+21, this method simply calls Number.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

Related Questions