9

我正在尝试最近的std::chronoapi,我发现在 64 位 Linux 架构和 gcc 编译器上,time_pointduration类无法以最大分辨率(纳秒)处理操作系统的最大时间范围。事实上,这些类的存储似乎是 64 位整数类型,相比之下timespectimeval它们在内部使用两个 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 实现不适合存储更大的日期和更宽的时间跨度,从而为类似千年虫的错误奠定了基础。问题是否得到承认?是否有更好的实施或 API 改进的计划?

4

2 回答 2

14

这样做是为了让您获得最大的灵活性和紧凑的尺寸。如果需要超精细的精度,通常不需要很大的范围。如果你需要一个非常大的范围,你通常不需要非常高的精度。

例如,如果您以纳秒为单位进行交易,您是否经常需要考虑超过 +/- 292 年?如果你需要考虑一个更大的范围,那么微秒会给你 +/- 29.2万年

macOSsystem_clock实际上返回微秒,而不是纳秒。所以这个时钟从 1970 年开始可以运行 29.2 万年,直到它溢出。

Windowssystem_clock的精度为 100 ns 单位,因此范围为 +/- 29200 年。

如果几十万年还不够,试试毫秒。现在你的范围可达 +/- 2.92亿年。

最后,如果您只需要拥有超过几百年的纳秒精度,那么您<chrono>也可以自定义存储:

using dnano = duration<double, nano>;

这使您可以将纳秒存储为double. 如果您的平台支持 128 位整数类型,您也可以使用它:

using big_nano = duration<__int128_t, nano>;

哎呀,如果您为 编写重载运算符timespec,您甚至可以将用于存储(但我不推荐它)。

您还可以达到比纳秒更精细的精度,但这样做会牺牲范围。例如:

using picoseconds = duration<int64_t, pico>;

它的范围只有 +/- .292 年(几个月)。所以你必须小心。如果您有一个可以为您提供亚纳秒精度的源时钟,则非常适合计时。

观看此视频以了解更多信息<chrono>

为了创建、操作和存储范围大于当前公历有效期的日期,我创建了这个开源日期库,它通过日历服务扩展了该<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 一起引入。如果你想要更早,你可以从这里这里获取。

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

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

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

一旦你有秒(s),你可以将它添加到你的time_point.

于 2017-05-30T19:00:37.413 回答
0

std::chrono::nanoseconds是一个类型别名,std::chrono::duration<some_t, std::nano>其中some_t是一个有符号整数,存储空间至少为 64 位。这仍然允许至少 292 年的纳秒精度范围。

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

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

于 2017-05-30T19:07:56.130 回答