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

时间:2017-05-24 19:10:06

标签: c math unsigned signed

我有一个来自无符号48位纳秒计数器的两个值,它可以包裹。

我需要两次以纳秒为单位的差异。

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

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

我想计算它们之间的差异,作为有符号整数(大概是int64_t),说明包装。

所以,例如如果我开始

x=5

y=3

然后x-y的结果为2,如果我增加xy,它们将保持不变,即使它们包裹在最大值的顶部0xffffffffffff

类似地,如果x = 3,y = 5,则x-y为-2,并且只要x和y同时递增,它就会保持不变。

如果我可以将xy声明为uint48_t,将差异声明为int48_t,那么我认为

int48_t diff = x - y; 

会起作用。

如何用我可用的64位算法模拟这种行为?

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

P.S。我可以解决这个问题,但我想知道是否有一个很好的标准方法来做这种事情,下一个阅读我的代码的人将能够理解。

P.P.S此外,这段代码将以最严格的紧密循环结束,因此有效编译的东西会很好,所以如果必须有选择,速度将超过可读性。

3 个答案:

答案 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)非常容易阅读。