std :: chrono溢出::持续时间测量的持续时间

时间:2018-02-09 10:10:46

标签: c++ c++11 integer-overflow chrono

我想知道,事件发生还剩多少时间。我正在使用boost :: chrono或std :: chrono。

基本上算法是这样的:

using std::chrono; // Just for this example
auto start = steady_clock::now();
seconds myTotalTime(10);
while(true){
  auto remaining = start + myTotalTime - steady_clock::now();
  seconds remainingSecs = duration_cast<seconds>(remaining);
  if(remainingSecs.count() <= 0) return true;
  else print("Remaining time = " + remainingSecs.count()); // Pseudo-Code!
}

现在可以(理论上)发生以下情况:start接近时钟周期的末尾(IMO没有概念0意味着什么,所以它可能是任意的。) /> 然后start + myTotalTime可能会溢出,减法可能会下溢。

即使像steady_clock::now() - start这样简单的事情也可能下流。

对于无符号类型,这不是问题。如果它们是未签名的,则标准保证在溢出steady_clock::now() - start的情况下我仍然获得now()的正确数量的“单位”以及8位无符号的下溢减法:10 - 250 = 10 + 256 - 255 = 16数学。

但签名类型的AFAIK over / underflow是未定义的行为。

我错过了什么吗?为什么使用signed而不是unsigned类型定义持续时间,尤其是time_points?

2 个答案:

答案 0 :(得分:4)

这是一个简短的程序,您可以使用它来查看steady_clock中任何平台剩余的范围(相对于现在):

#include <chrono>
#include <iostream>

int
main()
{
    using namespace std::chrono;
    using namespace std;
    using days = duration<int, ratio<86400>>;
    using years = duration<double, ratio_multiply<ratio<146097, 400>, days::period>>;
    cout << years{steady_clock::time_point::max() -
                  steady_clock::now()}.count() << " years to go\n";
}

这将输出steady_clock将溢出的年数。在几个不同的平台上运行几次之后,你会感到温暖的模糊感觉,除非你的程序运行超过几百年,否则你不必担心。

在我的平台上(这很常见),steady_clock正在以纳秒为单位测量自启动以来的时间。

对我来说,这个程序输出:

292.127 years to go

任何平台和任何不能代表now()而没有溢出的时钟都不太可能被广泛采用。

  

为什么用signed而不是unsigned类型定义持续时间,尤其是time_points?

  • 使durationtime_point减法更容易出错。是的,如果你减去 unsigned_smaller - unsigned_larger ,你会得到一个明确定义的结果,但这个结果可能不是程序员所期望的答案(例子除外) of time_point +你给的持续时间。)

  • 因此,1970年之前的日期时间可以用system_clock::time_point表示。虽然未指定,但system_clock正在衡量Unix Time(使用各种精确度),这是事实上的标准。这需要保留负值以表示1970-01-01 00:00:00 UTC之前的时间。我目前正试图将现有做法标准化。

相反,使用足够数量的位指定有符号整数表示,以使溢出成为罕见问题。纳秒精度保证在+/- 292年的范围内。较粗的精度将具有更大的范围。当此默认值不足时,允许使用自定义表示。

答案 1 :(得分:2)

我没有看到问题。你选择的单位不能满足你的需要吗?

在我的系统上(是的,我知道,但是)std :: chrono :: seconds定义为int64_t,所以假设选择了一个合理的纪元(!=大爆炸),它不会很快溢出。< / p>

其他的也是如此,但是以纳秒为单位擦掉了30位,很有可能出现溢出,剩下的33位给出/只有/ 270年左右。

time_point内部使用持续时间,因为它是自纪元以来的持续时间。原则上,如果您的纪元是1970年(Unix),您可能希望在此之前参考日期,因此必须进行签名。如果您的纪元是1600(Windows),您显然不能使用纳秒来表示当前时间点,这是一个可移植性问题。

您可以使用double来定义自己的持续时间,当然,其中范围被交换为分辨率,并且我在过去这样做是打印十进制第二时间戳的便捷方式。

如果您需要范围和分辨率,也可以定义更大的整数类。