繁体   English   中英

std :: chrono :: duration中的剩余时间测量溢出

[英]Overflow in std::chrono::duration for remaining time measurement

我想知道事件还剩下多少时间。 我为此使用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获得正确的“单位”数steady_clock::now() - startnow()溢出和下溢减法的情况下steady_clock::now() - start 10 - 250 = 10 + 256 - 255 = 16用于8位无符号数学。

但是AFAIK签名类型的上溢/下溢是未定义的行为。

我有什么想念的吗? 为什么持续时间,尤其是time_points是用有符号而不是无符号类型定义的?

这是一个简短的程序,您可以用来查看任何平台在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()没有溢出的任何平台和时钟都不太可能被广泛采用。

为什么持续时间,尤其是time_points是用有符号而不是无符号类型定义的?

  • 使durationtime_point减法不易出错。 是的,如果您减去unsigned_smaller - unsigned_larger,您会得到一个明确定义的结果,但是该结果不太可能是程序员期望的答案(在time_point +持续时间示例中除外)。

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

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

我没看到问题。 您选择的单位肯定不会溢出以满足您的需求吗?

在我的系统上(是的,我知道,但是)std :: chrono :: seconds被定义为int64_t,因此,假设选择了一个明智的纪元(!= big bang),它不会很快溢出。

其他的也一样,但是在十亿分之一秒内会擦除30位,从而有很大的溢出机会,其余的33位给/ only / 270年左右。

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

当然,您可以使用double来定义自己的持续时间,其中将范围转换为分辨率,并且我过去已经这样做,这是一种方便的打印小数秒时间戳的方式。

或者,如果您同时需要范围和分辨率,则可以定义一个更大的整数类。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM