我正在尝试解决一个相当烦人的问题,我需要时间戳精度为亚毫秒的报告数据。问题是时间戳消息字段是typedefed双精度型。我只为单个接收客户端打包此消息,但此消息是标准化数据服务的一部分,我无法在短期内修改此字段。
我正在考虑将两个32位值(表示UTC秒和微秒)复制到一个double中的后果,如下所示。我并不喜欢这样,但它似乎确实有效。然而,我不明白的是,为什么当我颠倒秒和微秒时,它不起作用。如果我以另一种方式 Package 它,第二次总是正确的。但微秒时间在(+/-)1 - 500 us范围内随表面上的随机值变化
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之后,我更困惑了。看起来如果有任何位被错误地表示,那就是更高的位。
无论如何,我很乐意听到对这种行为的解释,以及如何可靠地完成这一任务的任何建议。
3条答案
按热度按时间abithluo1#
要了解为什么会发生这种情况,必须考虑
ts
在转换为double
之前的值。ts
的值最大为1000000·232。64位double
使用53位尾数存储存储值的数字。这意味着对于小于253的值,双精度将精确到最接近的整数,因此在转换为double
时不会丢失数据。对于较大的值,double
只能存储53位精度,从而妨碍了一些整数的表示。当您切换微秒和秒时,
ts
的值变为大约(16亿)·232,或大约262。由于在转换为double
时只存储53位二进制精度数字,因此该数字四舍五入到最接近的29=512,从而导致您看到的错误。如果我正确地理解了您要做的事情,您需要时间戳精确到微秒,那么您是否考虑过像这样以微秒为单位计算总时间戳?
这将保证微秒级的精度,而不会接近
double
的253极限。l0oc07j22#
IEEE-754双精度浮点数可以表示264个不同的值,但它的范围大约是2.210-308-1.810308。这个范围超过了264,因此显然它不能表示该范围内的每个不同值。它放弃了 precision 表示range。
一个简单的
static_cast
到double
(这是C风格的强制转换最终要做的)不足以将uint64_t
的位压缩到double
中,这将把uint64_t
的 numeric 值转换为double
,可能会损失所有的精度。你可以做的是将
uint64_t
的底层字节复制到double
的存储中。当在另一端解包值时,您可以反向执行相同的操作。
e1xvtsh33#
我对这个问题的理解是,我们需要一个定时器,它返回的
double
至少能以微秒级的分辨率返回系统时间。下面是为此目的编写的代码,我已经使用了二十多年,没有出现任何问题。以秒为单位返回系统时间,比微秒分辨率 * 目前 * 略细,微秒分辨率需要20位,从1970年1月1日至今的秒数目前约为16.67亿,因此适合31位。由于基于IEEE-754
binary64
的double
具有52 stored significand位,这允许舒适地存储自1月1日以来的微秒的总计数,1970年的这个时候和未来几十年。然而,如果需要将时间戳机制保存几个世纪,这种方法就 * 不 * 合适了,因为它最终会崩溃(由于
double
中可用于存储微秒计数的位数有限),除非Unix-y平台将系统时间保持的起点重新偏置到更晚的日期。