我正在尝试最近的std::chrono
api,我发现在64位Linux体系结构和gcc编译器上,time_point
和duration
类无法处理最大时间范围操作系统的最大分辨率(纳秒)。实际上,与timespec
和timeval
相比,这些类的存储似乎是64位整数类型,内部使用两个64位整数,一个用于秒,一个用于纳秒:
#include <iostream>
#include <chrono>
#include <typeinfo>
#include <time.h>
using namespace std;
using namespace std::chrono;
int main()
{
cout << sizeof(time_point<nanoseconds>) << endl; // 8
cout << sizeof(time_point<nanoseconds>::duration) << endl; // 8
cout << sizeof(time_point<nanoseconds>::duration::rep) << endl; // 8
cout << typeid(time_point<nanoseconds>::duration::rep).name() << endl; // l
cout << sizeof(struct timespec) << endl; // 16
cout << sizeof(struct timeval) << endl; // 16
return 0;
}
在64位Windows(MSVC2017)上情况非常相似:存储类型也是64位整数。在处理稳定(又称单调)时钟时这不是问题,但存储限制使得不同的API实现不适合存储更大的日期和更宽的时间跨度,从而为类似Y2K的错误创造了基础。这个问题得到了承认吗?是否有更好的实施或API改进的计划?
答案 0 :(得分:13)
这样做是为了让您获得最大的灵活性和紧凑的尺寸。如果您需要超精密的精度,通常不需要非常大的范围。如果您需要非常大的范围,通常不需要非常高的精度。
例如,如果您以纳秒为单位进行广告投放,您是否经常需要考虑超过+/- 292年?如果你需要考虑一个大于这个范围的范围,那么微秒就会给你+/- 292 千年。
macOS system_clock
实际上返回微秒,而不是纳秒。因此,从1970年开始,时钟可以运行29.2万年,直到它溢出。
Windows system_clock
的精度为100 ns,因此范围为+/- 29.2万年。
如果还有几十万年还不够,那就试试几毫秒。现在你达到了+/- 292 百万年的范围。
最后,如果你 让纳秒精度超过几百年,<chrono>
也允许你自定义存储:
using dnano = duration<double, nano>;
这使您可以将纳秒存储为double
。如果您的平台支持128位整数类型,您也可以使用它:
using big_nano = duration<__int128_t, nano>;
哎呀,如果你为timespec
编写重载操作符,你甚至可以使用那个来存储(我不推荐它)。
你也可以达到精确到纳秒的精度,但你会牺牲这样做的范围。例如:
using picoseconds = duration<int64_t, pico>;
此范围仅为+/-。292年(几个月)。所以你做必须要小心。如果你有一个能够提供亚纳秒精度的源时钟,那么它非常适合计时。
有关<chrono>
的详细信息,请查看this video。
为了创建,操作和存储范围大于当前公历日历有效性的日期,我创建了这个open-source date library,它使用日历服务扩展了<chrono>
库。此库将年份存储在带符号的16位整数中,因此其范围为+/- 32K年。它可以像这样使用:
#include "date.h"
int
main()
{
using namespace std::chrono;
using namespace date;
system_clock::time_point now = sys_days{may/30/2017} + 19h + 40min + 10s;
}
<强>更新强>
在下面的评论中,询问如何将duration<int32_t, nano>
“标准化”为秒和纳秒(然后将秒添加到time_point)。
首先,我会警惕将纳秒填充到32位。范围只有+/- 2秒。但这就是我如何将这样的单位分开:
using ns = duration<int32_t, nano>;
auto n = ns::max();
auto s = duration_cast<seconds>(n);
n -= s;
请注意,这仅在n
为正时才有效。要正确处理否定n
,最好的办法是:
auto n = ns::max();
auto s = floor<seconds>(n);
n -= s;
std::floor
是随C ++ 17引入的。如果您之前需要,可以从here或here抓取。
我偏爱上面的减法操作,因为我发现它更具可读性。但这也有效(如果n
不是负面的):
auto s = duration_cast<seconds>(n);
n %= 1s;
1s
是在C ++ 14中引入的。在C ++ 11中,您将不得不使用seconds{1}
。
有秒(s
)后,您可以将其添加到time_point
。
答案 1 :(得分:0)
std::chrono::nanoseconds
是std::chrono::duration<some_t, std::nano>
的类型别名,其中some_t
是一个带有至少64位存储的signed int。这仍然允许至少292年的范围具有纳秒精度。
值得注意的是,标准中提到的具有此类特征的唯一整数类型是int
(| _fast
| _least
)64_t
系列。
如果您的实施提供了一个,您可以自由选择更广泛的类型来代表您的时间。您可以进一步自由地提供一个命名空间,其中包含一组反映std::chrono
比率的typedef,其中更宽的类型作为表示。