我有一个来自无符号48位纳秒计数器的两个值,它可以包裹。
我需要两次以纳秒为单位的差异。
我想我可以假设读数是在大致相同的时间进行的,所以对于两个可能的答案,我认为我可以安全地采取最小的答案。
它们都存储为uint64_t
。因为我认为我不能拥有48位类型。
我想计算它们之间的差异,作为有符号整数(大概是int64_t
),说明包装。
所以,例如如果我开始
x=5
y=3
然后x-y
的结果为2
,如果我增加x
和y
,它们将保持不变,即使它们包裹在最大值的顶部0xffffffffffff
类似地,如果x = 3,y = 5,则x-y为-2,并且只要x和y同时递增,它就会保持不变。
如果我可以将x
,y
声明为uint48_t
,将差异声明为int48_t
,那么我认为
int48_t diff = x - y;
会起作用。
如何用我可用的64位算法模拟这种行为?
(我认为任何可能运行的计算机都会使用2的补码算法)
P.S。我可以解决这个问题,但我想知道是否有一个很好的标准方法来做这种事情,下一个阅读我的代码的人将能够理解。
P.P.S此外,这段代码将以最严格的紧密循环结束,因此有效编译的东西会很好,所以如果必须有选择,速度将超过可读性。
答案 0 :(得分:5)
您可以通过在任何算术运算后屏蔽uint64_t
的前16位来模拟48位无符号整数类型。因此,例如,为了获得这两次之间的差异,你可以这样做:
uint64_t diff = (after - before) & 0xffffffffffff;
即使计数器在程序中缠绕,您也会得到正确的值。如果计数器没有环绕,则不需要屏蔽,但也不会有害。
现在,如果您希望编译器将此差异识别为有符号整数,则必须 sign extend 第48位。这意味着如果设置了第48位,则该数字为负,并且您要设置64位整数的第49位到第64位。我认为一个简单的方法是:
int64_t diff_signed = (int64_t)(diff << 16) >> 16;
警告:您应该对此进行测试以确保其有效,并且当我将uint64_t
强制转换为int64_t
时,请注意存在实现定义的行为,并且我认为当我将有符号的负数转移到右侧时,存在实现定义的行为。我确信一位C语言律师能够提供更强大的东西。
更新: OP指出,如果您结合使用差异和执行符号扩展的操作,则无需屏蔽。这看起来像这样:
int64_t diff = (int64_t)(x - y) << 16 >> 16;
答案 1 :(得分:3)
struct Nanosecond48{
unsigned long long u48 : 48;
// int res : 12; // just for clarity, don't need this one really
};
这里我们只使用字段的显式宽度为48位,并且使用它(当然有些尴尬)类型,您可以将其用于编译器以正确处理不同的体系结构/平台/诸如此类。
如下所示:
Nanosecond48 u1, u2, overflow;
overflow.u48 = -1L;
u1.u48 = 3;
u2.u48 = 5;
const auto diff = (u2.u48 + (overflow.u48 + 1) - u1.u48) & 0x0000FFFFFFFFFFFF;
当然,在最后一个声明中,如果您愿意,可以使用% (overflow.u48 + 1)
进行余数操作。
答案 2 :(得分:2)
你知道哪个是早期阅读,哪个是后来的?如果是这样的话:
diff = (earlier <= later) ? later - earlier : WRAPVAL - earlier + later;
其中WRAPVAL
为(1 << 48)
非常容易阅读。