std::chrono::duration_cast - GCC实现是否存在错误?

6

我认为我已经在GCC的std::chrono::duration_cast实现中发现了一个bug。请有人确认一下吗?

测试代码:

using Ticks = std::chrono::duration<long long, std::ratio_multiply<std::chrono::nanoseconds::period, std::ratio<100>>>;

using dur = typename std::chrono::system_clock::duration;

int main() 
{

    std::chrono::time_point<std::chrono::system_clock> earliest {std::chrono::duration_cast<dur>(
    std::chrono::time_point<std::chrono::system_clock, Ticks>::max().time_since_epoch())};

    auto ticks = std::chrono::time_point<std::chrono::system_clock, Ticks>::max().time_since_epoch().count();
    std::cout << "Ticks: " << ticks << "\n";
    std::cout << sizeof(earliest.time_since_epoch().count()) << "\n";
    std::cout << "Microseconds: " << earliest.time_since_epoch().count() << "\n";

    std::time_t t = std::chrono::system_clock::to_time_t(earliest);
    std::cout << "earliest:\n" << std::ctime(&t);
}

使用clang 3.8的输出结果为:

Ticks: 9223372036854775807
8
Microseconds: 922337203685477580
earliest:
Sun Sep 14 02:48:05 31197

使用GCC 7.1输出结果为:

Ticks: 9223372036854775807
8
Microseconds: -100
earliest:
Thu Jan  1 00:00:00 1970

我错了吗?


clang 3.8 版本在我的电脑上与 gcc 的工作方式相同。 - BartekPL
忘了提到测试是在coliru平台上完成的。 - Valeriop84
1个回答

5
这并不是一个bug,你只是引起了有符号溢出,因此导致了未定义的行为。实际上,你的代码依赖于平台相关的假设(系统时钟周期和rep类型),这在gcc测试用例中恰好失败。
在撰写本文时,Coliru的GCC环境使用的system_clock具有long类型的纳秒持续时间周期,该类型的大小与long long相同。
因此,当你对time_point::max().time_since_epoch()进行duration_cast时,
你将一个每个周期为100纳秒的numeric_limits::max()周期的duration转换为long类型的纳秒duration,结果等同于numeric_limits::max()*100,这显然会产生溢出(你的二进制补码实现会发生包装,导致-100;无论如何,这仍然是未定义的行为)。
相反,在我的clang副本中,system_clock具有long long类型的微秒持续时间周期,导致持续时间转换等效于numeric_limits::max()/10。

ubsan可以在运行时检测有符号整数溢出。我的同事使用ubsan发现了一个非常相似(负)的溢出,即将std::chrono::microseconds::min()转换为纳秒。 - Arne Vogel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接