格式化计时器而不会失去准确性?

saa*_*adq 11 javascript precision rounding ecmascript-6

我有一系列开始/停止时间.我基本上想要显示每个条目花费的时间,以及所有条目的总时间.这是我写的尝试这样做的代码:

function timeFormatter (milliseconds) {
  const padZero = (time) => `0${time}`.slice(-2);

  const minutes = padZero(milliseconds / 60000 | 0);
  const seconds = padZero((milliseconds / 1000 | 0) % 60);
  const centiseconds = padZero((milliseconds / 10 | 0) % 100);

  return `${minutes} : ${seconds} . ${centiseconds}`;
}

// Example stopwatch times
const timeIntervals = [
  { startTime: 1470679294008, stopTime: 1470679300609 },
  { startTime: 1470679306278, stopTime: 1470679314647 },
  { startTime: 1470679319718, stopTime: 1470679326693 },
  { startTime: 1470679331229, stopTime: 1470679336420 }
];

// Calculate time it took for each entry
const times = timeIntervals.map(time => time.stopTime - time.startTime);

// Run the timeFormatter on each individual time
const individualTimes = times.map(timeFormatter);

// Run the timeFormatter on the sum of all the times
const mainTimer = timeFormatter(times.reduce((a, b) => a + b));

/**
     * [
     *   '00 : 06 . 60',
     *   '00 : 08 . 36',
     *   '00 : 06 . 97',
     *   '00 : 05 . 19'
     * ]
     */
console.log(individualTimes);

/**
     * 00 : 27 . 13
     */
console.log(mainTimer);
Run Code Online (Sandbox Code Playgroud)

但是,我正在失去准确性.正如您所看到的,个别时间不会累加到该mainTimer值.无论什么时候,它总是在.01 - .03之间.

有没有办法可以确保时间只显示两个地方,但仍然正确加起来?任何帮助,将不胜感激.

我在JSFiddle上也有这个,它更容易运行.


编辑:目前的答案做工作,为我上面提供的情况,但它并不像所有的情况都是这样的.

She*_*epy 4

问题

\n\n

每次显示舍入时间时,您都会失去准确性。\n您的圈数越多,问题可能就越大:

\n\n
\xe2\x95\x94\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\xa6\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x97\n\xe2\x95\x91 Lap  \xe2\x95\x91         Total time (ms)        \xe2\x95\x91\n\xe2\x95\x91 Time \xe2\x95\xa0\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\xa6\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\xa6\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\xa3\n\xe2\x95\x91 (ms) \xe2\x95\x91    JS \xe2\x95\x91  Real World \xe2\x95\x91  Display \xe2\x95\x91\n\xe2\x95\xa0\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\xac\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\xac\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\xac\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\xa3\n\xe2\x95\x91 3157 \xe2\x95\x91  3157 \xe2\x95\x91  3157.5\xc2\xb10.5 \xe2\x95\x91  3160\xc2\xb15  \xe2\x95\x91\n\xe2\x95\x91 2639 \xe2\x95\x91  5796 \xe2\x95\x91  5797.0\xc2\xb11   \xe2\x95\x91  5800\xc2\xb110 \xe2\x95\x91\n\xe2\x95\x91 3287 \xe2\x95\x91  9083 \xe2\x95\x91  9084.5\xc2\xb11.5 \xe2\x95\x91  9090\xc2\xb115 \xe2\x95\x91\n\xe2\x95\x91 3106 \xe2\x95\x91 12189 \xe2\x95\x91 12191.0\xc2\xb12   \xe2\x95\x91 12200\xc2\xb120 \xe2\x95\x91\n\xe2\x95\x9a\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\xa9\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\xa9\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\xa9\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x9d\n
Run Code Online (Sandbox Code Playgroud)\n\n

一旦考虑到容差,不同的总数实际上是相互重叠的:

\n\n
    \n
  • JS时间=12189
  • \n
  • 实际时间 = 12189 至 12193
  • \n
  • 显示时间 = 12180 至 12240
  • \n
\n\n

换句话说,通过将显示的时间相加,显示精度的损失也会被增加。\n未能考虑到人类造成的损失才是真正的问题。

\n\n

解决方案

\n\n
    \n
  1. 提高显示精度(如果不对数字进行四舍五入,则不会丢失任何内容)
  2. \n
  3. 使用显示的总和,该总和不太准确,但如果您还指定了容差 (\xc2\xb1),则并非不正确。
  4. \n
  5. 检测问题并显示一条消息。
  6. \n
\n\n

这是解决方案 3 的演示。

\n\n

\r\n
\r\n
function run ( set ) {\r\n   show ( set === 0\r\n      ? // Good set\r\n        [ { startTime: 1470679294008, stopTime: 1470679300609 },\r\n          { startTime: 1470679306278, stopTime: 1470679314647 },\r\n          { startTime: 1470679319718, stopTime: 1470679326693 },\r\n          { startTime: 1470679331229, stopTime: 1470679336420 } ]\r\n      : // Bad set\r\n        [ { startTime: 1472104779284,  stopTime: 1472104782441 },\r\n          { startTime: 1472104782442,  stopTime: 1472104785081 },\r\n          { startTime: 1472104785081,  stopTime: 1472104788368 },\r\n          { startTime: 1472104788369,  stopTime: 1472104791475 }, ] );\r\n}\r\n\r\nfunction show ( timeIntervals ) {\r\n   const sum = (a, b) => a + b;\r\n\r\n   const roundTime = (ms) => Math.round(ms/10);\r\n\r\n   function timeFormatter (centi) {\r\n     const padZero = (time) => `0${~~time}`.slice(-2);\r\n\r\n     const minutes = padZero(centi / 6000);\r\n     const seconds = padZero((centi / 100) % 60);\r\n     const centiseconds = padZero(centi % 100);\r\n\r\n     return `${minutes} : ${seconds} . ${centiseconds} `;\r\n   }\r\n\r\n   // Calculate time it took for each entry.\r\n   const times = timeIntervals.map(time => time.stopTime - time.startTime);\r\n\r\n   // Rou and run the timeFormatter on each individual time\r\n   const roundedTimes = times.map(roundTime);\r\n   const individualTimes = roundedTimes.map(timeFormatter);\r\n   // Calculate sum of displayed time\r\n   const displayedSum = roundedTimes.reduce(sum);\r\n\r\n   // Sum time and run timeFormatter\r\n   const totalTime = roundTime( times.reduce(sum) );\r\n   const mainTimer = timeFormatter(totalTime);\r\n\r\n   let html = \'<ol><li>\' + individualTimes.join(\'<li>\') + \'</ol>Sum: \' + mainTimer;\r\n   // Show warning if sum of rounded time is different.\r\n   if ( displayedSum !== totalTime )\r\n      html += \' (Rounding error corrected)\';\r\n\r\n   document.querySelector(\'div\').innerHTML = html;\r\n}\r\n\r\nrun(1);
Run Code Online (Sandbox Code Playgroud)\r\n
<button onclick=\'run(0)\'>Perfect</button>\r\n<button onclick=\'run(1)\'>Opps</button>\r\n<div></div>
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n\n

或者只是忍受它

\n\n

所有定时器,甚至是物理定时器,都必须忍受这个舍入问题。\n你见过任何定时器做出这样的免责声明吗?

\n\n

对于计时器来说,显示更准确的总时间当然更正确,即使不一致。

\n\n
\n

如果您注意的话,您应该看到/意识到 javascript 时间相对于实时时间也有这种准确性损失。\n 还有一个更大的问题:\n Date.time 与时钟同步,因此它不稳定。\n 鉴于您的如果样本圈数范围为几秒,您甚至可能会得到负圈数。

\n\n

使用专为计时目的而设计的不同计时器Performance.now可以最大限度地减少错误并解决时间弯曲问题。

\n
\n