是否有一种可靠的方法来传递微秒时间?

问题描述 投票:0回答:2

我正在尝试解决一个相当烦人的问题,我需要带有亚毫秒精度时间戳的报告数据。问题是时间戳消息字段是类型定义的双精度值。我只是为单个接收客户端打包此消息,但此消息是标准化数据服务的一部分,我无法很快修改该字段。

我正在考虑将表示 UTC 秒和微秒的两个 32 位值复制为双精度值的后果,如下所示。我不太喜欢它,但它似乎确实有效。但是,我想不通的是,为什么我把秒和微秒对调时它不起作用。如果我以另一种方式打包它,第二次总是正确的,但微秒时间因 (+/-) 1 - 500us 之间看似随机的值而变化

timeval tv;
gettimeofday(&tv, nullptr);
std::cout << " Original seconds: " << tv.tv_sec << "\n";
std::cout << " Original useconds: " << tv.tv_usec << "\n";
uint64_t ts = ((tv.tv_usec << 32) | tv.tv_sec);
double dts = (double)ts;
unsigned useconds = ((uint64_t)dts >> 32);
unsigned seconds = ((uint64_t)dts & 0x00000000FFFFFFFF);
std::cout << " Final seconds: " << seconds << "\n";
std::cout << " Final useconds: " << useconds << "\n";
std::cout << "diff: " << useconds - tv.tv_usec << "\n";

看了IEEE-754,更糊涂了。似乎如果任何位会被歪曲,那将是更高的位。

无论如何,我很想听听对此行为的解释以及有关如何可靠地完成此行为的任何建议。

c++ time casting double bit-manipulation
2个回答
1
投票

要了解为什么会发生这种情况,您必须考虑

ts
在转换为
double
之前的值是多少。在这里的代码中,有

uint64_t ts = ((tv.tv_usec << 32) | tv.tv_sec);

ts
的值最多为1000000•232。一个 64 位的
double
使用一个 53 位的尾数来存储存储值的数字。这意味着对于小于 253 的值,double 将精确到最接近的整数,因此在转换为
double
时不会丢失任何数据。对于较大的值,
double
只能存储 53 位精度,从而无法表示一些整数。

当你切换微秒和秒时,

ts
的值变成大约(16亿)•232,或者大约262。因为在转换为
double
时只存储 53 位二进制精度数字,所以这个数字四舍五入到最接近的 29=512,导致您看到的错误。

如果我理解正确你想做什么,你需要时间戳的微秒精度,所以你有没有考虑过只计算微秒的总时间戳,像这样?

uint64_t ts = tv.tv_sec * 1000000 + tv.tv_usec);

这将保证微秒级的精度,而不会以完美的精度接近 double 的 2

53
极限。


0
投票

一个 IEEE-754 可以表示 264 不同的值,但它的范围大约是 2.2*10-308-1.8*10308。这是一个大于 264 的范围,因此显然它不能代表该范围内的每个不同值。它放弃了 precision 范围。

一个简单的

static_cast
double
(这是你的C风格演员最终会做的)不足以将一个
uint64_t
打包成一个
double
。这会将uint64_t
numeric
值转换为
double
,所有潜在的精度损失都需要。

你可以做的是实际将

uint64_t
的底层字节复制到
double
的存储中。例如:

double dts = std::bit_cast<double>(ts);

// or, pre-C++17
double dts;
std::copy(
    reinterpret_cast<unsigned char*>(&ts),
    reinterpret_cast<unsigned char*>(&ts) + sizeof(uint64_t),
    &dts
);

拆开另一边的值时,你可以反向做同样的事情。

© www.soinside.com 2019 - 2024. All rights reserved.