我有一个设备,它以整数秒和小数秒的时钟节拍报告时间。对于这个特殊的设备,时钟的工作频率为256MHz。我在duration和time_points中定义了一个自定义分辨率:
using Res = std::chrono::duration<uint64_t, std::ratio<1, 256'000'000>>;
(除非是相关的,否则我宁愿不讨论int和int)。
此分辨率足以表示自1970年以来的时间,在2763左右溢出。
,
#include <iostream>
#include <chrono>
int main() {
using Res = std::chrono::duration<uint64_t, std::ratio<1, 256'000'000>>;
using namespace std::chrono;
using TimePoint = time_point<system_clock, Res>;
auto s_now = system_clock::now();
auto simple = time_point_cast<Res>(s_now);
auto s_sec = duration_cast<seconds>(s_now.time_since_epoch());
auto hard = TimePoint(Res(s_sec));
hard += duration_cast<Res>(s_now.time_since_epoch() - s_sec);
std::cout << "s_now " << s_now.time_since_epoch().count() << "t" << s_sec.count() << "n"
<< "simple " << simple.time_since_epoch().count() << "t" << duration_cast<seconds>(simple.time_since_epoch()).count() << "n"
<< "hard " << hard.time_since_epoch().count() << "t" << duration_cast<seconds>(hard.time_since_epoch()).count() << "n"
;
return 0;
}
则simple
不等于hard
。输出可能像这样:
s_now 1628286679505051812 1628286679
simple 121693484773940438 475365174
hard 416841389953293263 1628286679
发生的是time_point_cast
溢出system_clock::duration
的int
Rep。
基于https://stackoverflow.com/a/51226923/9220132,这似乎是合法和道德的,但它并不好吃。这个限制是一个太容易产生bug的来源。
如果有一种方法可以让我定义高分辨率类型的强制转换的实现,这样我就可以防止溢出,那将是理想的。知道使用自定义助手函数而不是强制转换只是自找麻烦,因为有人会忘记一些时间,并且错误可能会被忽视到生产中。
我同意你碰到溢出。但这并不是因为system_clock::rep
是int
。事实上,你的system_clock::rep
是int64_t
,是的,它在这个操作中溢出。
你的system_clock::period
是nano
。将nano
转换为std::ratio<1, 256'000'000>
的转换因子为32/125。而32 * 1628286679505051812溢出了常见的rep
:uint64_t
。
有几种方法可以避免溢出并获得正确答案。你给我们看其中一个。另一种方法是使用128位算术,这在您的平台上可用:
using D = duration<__int128_t>;
auto s_now = system_clock::now() + D{0}; // s_now::rep is now 128 bits
将输出更改为:
s_now 1628286679505051812 1628286679
simple 416841389953293263 1628286679
hard 416841389953293263 1628286679
最后回答你的问题:不,不要重写duration_cast
。相反,创建your_cast
并做任何你想做的事情。c++ 17已经通过引入三种新的"风味"实现了这一点。duration_cast
/time_point_cast
,它只是改变默认的舍入模式:
template<class ToDuration, class Rep, class Period>
constexpr ToDuration floor(const duration<Rep, Period>& d);
template<class ToDuration, class Rep, class Period>
constexpr ToDuration ceil(const duration<Rep, Period>& d);
template<class ToDuration, class Rep, class Period>
constexpr ToDuration round(const duration<Rep, Period>& d);
template<class ToDuration, class Clock, class Duration>
constexpr time_point<Clock, ToDuration> floor(const time_point<Clock, Duration>& tp);
template<class ToDuration, class Clock, class Duration>
constexpr time_point<Clock, ToDuration> ceil(const time_point<Clock, Duration>& tp);
template<class ToDuration, class Clock, class Duration>
constexpr time_point<Clock, ToDuration> round(const time_point<Clock, Duration>& tp);
下面是这些函数的示例实现。只需复制一个,为your_cast
选择一个合适的名称,并实现它,但最适合你。