[英]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() - start
在now()
溢出和下溢减法的情况下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是用有符号而不是无符号类型定义的?
使duration
和time_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.