为什么std :: chrono :: time_point不足以存储struct timespec?

时间:2017-05-30 18:36:49

标签: c++11 gcc visual-c++ chrono year2038

我正在尝试最近的std::chrono api,我发现在64位Linux体系结构和gcc编译器上,time_pointduration类无法处理最大时间范围操作系统的最大分辨率(纳秒)。实际上,与timespectimeval相比,这些类的存储似乎是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改进的计划?

2 个答案:

答案 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引入的。如果您之前需要,可以从herehere抓取。

我偏爱上面的减法操作,因为我发现它更具可读性。但这也有效(如果n不是负面的):

    auto s = duration_cast<seconds>(n);
    n %= 1s;

1s是在C ++ 14中引入的。在C ++ 11中,您将不得不使用seconds{1}

有秒(s)后,您可以将其添加到time_point

答案 1 :(得分:0)

std::chrono::nanosecondsstd::chrono::duration<some_t, std::nano>的类型别名,其中some_t是一个带有至少64位存储的signed int。这仍然允许至少292年的范围具有纳秒精度。

值得注意的是,标准中提到的具有此类特征的唯一整数类型是int(| _fast | _least64_t系列。

如果您的实施提供了一个,您可以自由选择更广泛的类型来代表您的时间。您可以进一步自由地提供一个命名空间,其中包含一组反映std::chrono比率的typedef,其中更宽的类型作为表示。