toFixed() behavior on performance.now()

975 views Asked by At

When using toFixed on performance.now, it reveals some digits which I assume normally rounded. But it seems, the range changes based on platform.

On chrome(v87.0.4280.66), it can get up to 35 digits

window.performance.now().toFixed(35); // "241989.00499999945168383419513702392578125"

On node.js(v15.2.1), it's only up to 28.

performance.now().toFixed(28) // "1092.9840000011026859283447265625"

Same behavior exist on performance.timeOrigin too. I assume that it is possible to make much more accurate measurements with performance.now() but that accuracy depends on hardware and software factors so they just keep that accuracy on minimum standart.

  1. Does using toFixed(100) on performance.now() makes it more accurate?
  2. What factors affect range of performance.now()?
  3. Can we safely say that (performance.timeOrigin + performance.now()).toFixed(12) let's us measure the time accurate up to almost femtoseconds(10⁻¹⁵) or at least much much more accurate than the Date.now()?
2

There are 2 answers

4
Benjamin Gruenbaum On BEST ANSWER

Node's performance.now is literally:

function now() {
  const hr = process.hrtime();
  return hr[0] * 1000 + hr[1] / 1e6;
}

Its accuracy is nanoseconds. (So not 26 digits after the point that's meaningless in that API). This just calls uv_hrtime (see node_process_methods.cc) which does clock_gettime which is just a standard way to get nanosecond time.

In browsers the situation is worse - because of timing attacks that do fingerprinting or cache value extraction performance.now is less accurate:

To offer protection against timing attacks and fingerprinting, the precision of performance.now() might get rounded depending on browser settings.

So you can really on rely on milliseconds value.

What it returns is clamped in chrome. See time_clamper.cc for more info. Basically its percision is limited to:

static constexpr double kResolutionSeconds = 5e-6;

Intentionally.

As the other answer points out .toFixed just formats a number as a string and is unrelated to any of this.


Note: the fact an API is precise to X digits does not in any way indicate digits after the Xth are zero. It only means that you can only rely on accuracy up to that digit.

3
Elad On

I think saying that toFixed() makes it more accurate is wrong.

Basically toFixed() only formats the input number.

As for the performant.now() it returns milliseconds value which represents the time elapsed since the time origin.

The time origin is a standard time which is considered to be the beginning of the current document's lifetime.

So from your question you could imagine that this value would be calculate differently across different platforms.

From mozilla.org:

  • If the current Document is the first one loaded in the Window, the time origin is the time at which the browser context was created.

  • If during the process of unloading the previous document which was loaded in the window, a confirmation dialog was displayed to let the user confirm whether or not to leave the previous page, the time origin is the time at which the user confirmed that navigating to the new page was acceptable.

  • If neither of the above determines the time origin, then the time origin is the time at which the navigation responsible for creating the window's current Document took place.