在C中,如何计算两个48位无符号整数之间的有符号差?

Joh*_*den 8 c math unsigned signed

我有一个来自无符号48位纳秒计数器的两个值,它可能会换行.

我需要两倍的差异,以纳秒为单位.

我想我可以假设读数是在大致相同的时间进行的,所以对于两个可能的答案,我认为我是最安全的.

他们都存储为uint64_t.因为我认为我不能拥有48位类型.

我想计算它们之间的差异,作为有符号整数(大概int64_t),考虑包装.

所以,例如,如果我开始

x=5

y=3
Run Code Online (Sandbox Code Playgroud)

然后结果x-y2,并且如果我增加两个,x并且y即使它们包裹在最大值的顶部时也会保持这样0xffffffffffff

类似地,如果x = 3,y = 5,那么xy是-2,并且每当x和y同时递增时将保持不变.

如果我可以宣布x,y作为uint48_t和差异int48_t,我想

int48_t diff = x - y; 
Run Code Online (Sandbox Code Playgroud)

会工作的.

如何使用我可用的64位算法模拟此行为?

(我认为任何可能运行的计算机都会使用2的补码算法)

PS我可能会破解这个,但我想知道是否有一个很好的标准方法来做这种事情,下一个阅读我的代码的人将能够理解.

PPS此外,这段代码最终会出现在最严格的紧密循环中,所以有效编译的东西会很好,所以如果必须有选择,速度就会超过可读性.

Dav*_*son 5

您可以通过uint64_t在任何算术运算之后屏蔽a的前16位来模拟48位无符号整数类型.因此,举例来说,为了获得这两次之间的差异,您可以:

uint64_t diff = (after - before) & 0xffffffffffff;
Run Code Online (Sandbox Code Playgroud)

即使计数器在程序中缠绕,您也会得到正确的值.如果计数器没有环绕,则不需要屏蔽但也不会有害.

现在,如果您希望编译器将此差异识别为有符号整数,则必须对第48位进行符号扩展.这意味着如果设置了第48位,则该数字为负,并且您要设置64位整数的第49位到第64位.我认为一个简单的方法是:

int64_t diff_signed = (int64_t)(diff << 16) >> 16;
Run Code Online (Sandbox Code Playgroud)

警告:您应该测试这一点以确保它有效,并且当我将其转换uint64_t为a时int64_t,请注意存在实现定义的行为,并且当我将有符号的负数转移到右侧时,我认为存在实现定义的行为.我确信一位C语言律师能够提供一些更强大的东西.

更新: OP指出,如果您结合使用差异和执行符号扩展的操作,则不需要屏蔽.这看起来像这样:

int64_t diff = (int64_t)(x - y) << 16 >> 16;
Run Code Online (Sandbox Code Playgroud)

  • 我刚才提出的另一个想法是将你的48位无符号整数向左移16,然后再用它们做任何事情,那么你的读数都是uint64_t数,它们包裹在预期的位置,你没有永久地改变任何已签名的号码. (2认同)