无法复现Chrome中performance.now()的精度误差问题
问题
在Chromium浏览器中调用performance.now()时,会出现如下示例中的输出结果:
147.5 150.09999990463257 151.19999980926514 152.09999990463257 156.59999990463257 164.89999985694885 168.89999985694885 172.89999985694885 176.89999985694885 180.29999995231628 185 188.89999985694885 192.59999990463257 196.39999985694885 201.09999990463257 209 212.5 217.09999990463257 220.79999995231628
已知该误差由浮点精度问题导致,但无法在C或JavaScript中复现此类误差,测试的C代码产生的误差与Chromium中的完全不同:
int main() { float sum = 100.0; for (int i = 0; i < 1000; i++) { sum += 0.1; std::cout << std::fixed << std::setprecision(17); std::cout << "Actual sum: " << sum << std::endl; } return 0; }
请问该现象的具体成因是什么?如何通过代码复现Chromium中performance.now()的精度误差?
成因与复现方法
具体成因
- 计时逻辑本质差异:Chromium的
performance.now()并非通过循环累加固定时间间隔实现,而是读取系统高精度计时器(如Windows的QueryPerformanceCounter、Linux的CLOCK_MONOTONIC)返回的原始整数时间戳(单位多为纳秒或微秒),再将其转换为毫秒级的double浮点数。由于部分整数无法被二进制浮点数精确表示,转换时就会出现示例中的尾数位误差。 - 浮点数类型与误差来源不同:你测试用的C++代码使用单精度
float类型,而Chromium内部采用双精度double类型,两者的精度范围、误差模式完全不同;同时你的代码是累加本身就无法精确表示的0.1浮点数,和直接转换整数时间戳的误差来源完全无关,自然复现不了相同结果。
复现方法
JavaScript复现
模拟Chromium的整数转浮点数逻辑,直接将高精度整数时间戳转换为毫秒级double:
function simulatePerformanceNow() { // 模拟获取微秒级原始整数时间戳 const microseconds = BigInt(Date.now()) * 1000n + BigInt(Math.floor(Math.random() * 1000)); // 转换为毫秒级double return Number(microseconds) / 1000; } // 多次调用查看误差结果 for (let i = 0; i < 20; i++) { console.log(simulatePerformanceNow()); }
运行后会输出类似Chromium中的浮点误差,因为大整数转换为double时,部分数值无法精确表示,会产生对应的尾数位。
C++复现
改用double类型模拟高精度时间戳的单位转换逻辑,而非累加0.1:
#include <iostream> #include <chrono> #include <iomanip> #include <thread> int main() { auto start = std::chrono::high_resolution_clock::now(); for (int i = 0; i < 20; i++) { auto now = std::chrono::high_resolution_clock::now(); // 获取纳秒级整数时间差 long long nanoseconds = std::chrono::duration_cast<std::chrono::nanoseconds>(now - start).count(); // 转换为毫秒级double double ms = static_cast<double>(nanoseconds) / 1000000.0; std::cout << std::fixed << std::setprecision(17) << ms << std::endl; // 模拟微小延迟,让时间差变化更明显 std::chrono::microseconds delay(100); std::this_thread::sleep_for(delay); } return 0; }
这段代码通过系统高精度计时器获取纳秒级整数,再转换为毫秒级double,和Chromium内部performance.now()的核心逻辑一致,会产生和示例中类似的浮点误差。
内容的提问来源于stack exchange,提问作者BayanR
相关产品推荐
相关产品推荐

