如何解决std::chrono::duration 与不同代表之间转换的意外错误
我注意到了意外行为,并将其范围缩小到从一个持续时间到另一个持续时间的转换。
#include <chrono>
#include <iostream>
#include <cmath>
using sec = std::chrono::seconds;
using fsec = std::chrono::duration<float>;
using dsec = std::chrono::duration<double>;
template<typename T>
auto count_intervals(T time_jump) -> uint32_t
{
constexpr auto interval = sec(10);
std::cout << "[debug] " << time_jump.count() << std::endl;
std::cout << "[debug] " << interval.count() << std::endl;
std::cout << "[debug] " << time_jump.count() / interval.count() << std::endl;
printf("[debug] %.10f\n",time_jump.count());
printf("[debug] %.10f\n",time_jump.count() / interval.count());
const uint32_t k = std::ceil(time_jump.count() / interval.count());
std::cout << "[debug] " << k << std::endl;
return k;
}
int main()
{
using clock_t = std::chrono::system_clock;
using time_point_t = typename clock_t::time_point;
using std::chrono::duration_cast;
const auto start_point = time_point_t();
const auto a = start_point + sec(25);
const auto b = start_point + sec(55);
std::cout << count_intervals<fsec>(b - a) << std::endl;
std::cout << count_intervals<fsec>(duration_cast<fsec>(b - a)) << std::endl;
const auto distance = duration_cast<std::chrono::duration<uint32_t>>(b - a);
std::cout << count_intervals<fsec>(distance) << std::endl;
std::cout << count_intervals<dsec>(b - a) << std::endl;
}
我不明白为什么 std::ceil
会四舍五入。当我使用 dsec
而不是 fsec
时,似乎一切都很好。问题是在 float
持续时间表示的情况下我的错误是什么。我以为我按预期使用 std::chrono::duration
但显然情况并非如此。我应该如何正确执行到 fsec
的转换?一般来说,我希望有一个几分之一秒的持续时间格式。
[debug] 30
[debug] 10
[debug] 3
[debug] 30.0000019073
[debug] 3.0000002384
[debug] 4
4
[debug] 30
[debug] 10
[debug] 3
[debug] 30.0000019073
[debug] 3.0000002384
[debug] 4
4
[debug] 30
[debug] 10
[debug] 3
[debug] 30.0000000000
[debug] 3.0000000000
[debug] 3
3
[debug] 30
[debug] 10
[debug] 3
[debug] 30.0000000000
[debug] 3.0000000000
[debug] 3
3
解决方法
在您的平台上,system_clock::time_point
是 time_point<system_clock,nanoseconds>
的类型别名。事实证明,这个细节很重要。
运行此测试的 float
版本时,第一步是将 b - a
转换为 duration<float>
以调用 count_intervals
。 b - a
的类型是 nanoseconds
。 b - a
的值为 30'000'000'000ns
。
存储整数 30'000'000'000
需要 25 位精度。 IEEE 浮点数具有 24 位精度。
将 30'000'000'000ns
转换为 duration<float>
的第一步是将数字 30'000'000'000
存储到 common_type
和 system_clock::rep
的 float
中,即 { {1}}。它只能大致做到这一点。它实际上存储了 float
,这是“向最近舍入,甚至平局”策略下最好的可表示值。然后将该近似值除以 30'000'001'024
(存储在 1'000'000'000
中),得到近似值 float
。它得到的确切值是 30
或 0x1.e00002p+4
从现在开始,计算与精确略有偏差。
您可以通过将 30.000001...
设置为 cout
模式,这将准确地输出浮点数来亲眼看到这一点:
hexfloat
和/或,将精度设置为 15(左右):
std::cout << std::hexfloat;
计算与 std::cout.precision(15);
的预期一致,因为 duration<double>
有 53 位精度,因此可以准确地存储 double
。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。