chrono::duration的底层单位由模板参数Rep和Period共同决定,必须用duration_cast安全转换,避免手动count()运算导致精度丢失或溢出。

chrono::duration 的底层单位决定转换逻辑
std::chrono::duration 的实际存储值和单位是分离的,count() 返回的是“滴答数”,而单位由模板参数 Rep 和 Period 共同决定。直接对 count() 做整数除法或乘法会出错——比如把 milliseconds 的 count() 当成毫秒值再除以 1000,可能因截断丢失精度。
正确做法永远用 duration_cast 转换类型,让标准库自动处理周期换算:
auto ms = 1234ms; // std::chrono::milliseconds auto s = std::chrono::duration_cast<std::chrono::seconds>(ms); // 得到 1s auto us = std::chrono::duration_cast<std::chrono::microseconds>(ms); // 得到 1234000us
-
duration_cast是安全的:向下转换(如 ms → s)自动截断小数部分;向上转换(如 s → ms)补零 - 不要用
.count() / 1000手动算秒,尤其当原始类型是microseconds或nanoseconds时,极易溢出或精度丢失 - 如果需要带小数的秒(如 1.234),转成
double类型的duration:std::chrono::duration<double, std::ratio<1>>(ms)
避免隐式转换导致的精度丢失
写 auto t = 5s + 500ms; 看似自然,但 t 的类型是 std::chrono::seconds(因为 5s 占主导),500ms 会被截断为 0s。这不是 bug,而是 + 运算符重载的默认行为:结果类型取两个操作数中“精度更低”的那个(即周期更大的那个)。
- 显式指定目标类型可避免问题:
auto t = std::chrono::milliseconds{5s} + 500ms;→ 结果是5500ms - 或统一用高精度类型起步:
auto t = 5000ms + 500ms; - 检查类型:用
decltype(t)::period::num和decltype(t)::period::den查看实际周期(例如std::milli::num == 1, .den == 1000)
从 time_point 获取毫秒时间戳(自 epoch 起的 ms 数)
常见需求是获取当前时间的毫秒级 Unix 时间戳(long long 类型)。不能直接对 time_point 调用 count(),必须先转成对应 duration。
立即学习“C++免费学习笔记(深入)”;
auto now = std::chrono::system_clock::now();
auto ms_since_epoch = std::chrono::duration_cast<std::chrono::milliseconds>(
now.time_since_epoch()
).count(); // 返回 long long 类型的毫秒数
-
time_since_epoch()返回的是duration,不是数值,必须 cast 后才能count() - 如果用
std::chrono::steady_clock,它不保证从 epoch 开始计时,不能用于生成 Unix 时间戳 - 跨平台注意:
system_clock::time_point的 epoch 是 Unix epoch(1970-01-01),但 C++20 前不保证system_clock是稳态的,生产环境建议用file_clock(C++20)或第三方库校准
微秒转字符串时容易忽略的舍入问题
把 microseconds 值格式化为 “HH:MM:SS.mmm” 时,若直接用 count() % 1000 提取毫秒部分,会得到错误结果——因为微秒值除以 1000 的余数是“剩余微秒”,不是“毫秒小数位”。
- 正确方式:先 cast 到
milliseconds得整毫秒,再用原值减去它得到剩余微秒,然后除以 1000.0 得三位小数:(us.count() % 1000) / 1000.0 - 更稳妥:用
duration_cast<milliseconds>(us)和duration_cast<microseconds>(us % 1ms)分离整毫秒与余微秒 - printf 类格式化慎用
%lld直接输出count()——microseconds的count()类型可能是long long,也可能是long,取决于平台和标准库实现
最常被跳过的细节是:所有转换都依赖 Period 的编译期计算,duration_cast 不是运行时除法,而是常量表达式折叠。一旦写错模板参数(比如把 std::ratio<1, 1000> 写成 std::ratio<1000>),编译就报错——这反而是好事,说明类型系统在帮你拦住错误。


















