std::chrono::round 对超出范围的值的行为是否符合预期?

Mad*_*Tux 5 c++ rounding undefined-behavior c++-chrono c++17

我目前正在将 a 舍入std::chrono::duration<float>std::chrono::duration<uint32_t>,当我给它超出范围的值时,它会舍入为 ,1s而不是4294967295s

看看标准,它说

template <class ToDuration, class Rep, class Period>   
constexpr ToDuration round(const duration<Rep, Period>& d);
Run Code Online (Sandbox Code Playgroud)

[...]
返回:其中可表示的值 tToDuration最接近d。[...]

这是我的确切代码:

#include <chrono>
#include <cstdio>
#include <limits>
#include <cstdint>

int main()
{
        std::chrono::duration<float> seconds{std::numeric_limits<float>::max()};
        printf("float:    %f\n", seconds.count());
        printf("uint32_t: %u\n", std::chrono::round<std::chrono::duration<uint32_t>>(seconds).count());
        printf(" int32_t: %d\n", std::chrono::round<std::chrono::duration<int32_t>>(seconds).count());
        printf("uint64_t: %lu\n", std::chrono::round<std::chrono::duration<uint64_t>>(seconds).count());
        printf(" int64_t: %ld\n", std::chrono::round<std::chrono::duration<int64_t>>(seconds).count());
}
Run Code Online (Sandbox Code Playgroud)

哪个输出

float:    340282346638528859811704183484516925440.000000
uint32_t: 1
 int32_t: -2147483647
uint64_t: 9223372036854775809
 int64_t: -9223372036854775807
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,其他整数类型的行为也很奇怪。与等人不同的是std::lroundstd::chrono::round如果浮点输入超出范围,则不会说任何未定义的内容。

我错过了什么吗?

(就上下文而言,我使用 x86_64 上的clang版本编译了此文件14.0.0-1ubuntu1.1,但我首先在使用 的 ARMv7 系统上注意到了该问题gcc。)

How*_*ant 3

duration<Rep, Ratio>是一个简单的薄包装纸Rep。在你的例子中Repfloat在参数rounduint32_t结果中。

作为一个薄包装器,duration不会改变Rep溢出和转换等操作的基本行为。这样做会增加开销,而这通常是不可取的。

如果需要特定行为(例如溢出检查),chrono则可以通过允许duration<safe_int>wheresafe_int是模拟整数算术但检查溢出的假设类类型来促进这一点。此类库的现实世界示例是存在的,并且不需要特殊调整即可与chrono.

正如在这个问题中所询问的float那样uint32_t,未定义的行为产生于floatuint32_t级别,而不是级别chrono具体来说,当转换float为 时uint32_t,如果 中的截断值float无法在 中表示,则行为未定义uint32_t

正如下面的评论中所讨论的,该标准是否真的这么说了,还有一个悬而未决的问题。为了进一步实现这一目标,感兴趣的各方应提交 LWG 问题:

http://cplusplus.github.io/LWG/lwg-active.html#submit_issue

然后 LWG 将决定是否存在错误,如果存在,如何最好地补救。

相关链接: