简体   繁体   English

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

[英]In C, How do I calculate the signed difference between two 48-bit unsigned integers?

I've got two values from an unsigned 48bit nanosecond counter, which may wrap. 我有一个来自无符号48位纳秒计数器的两个值,它可能会换行。

I need the difference, in nanoseconds, of the two times. 我需要两倍的差异,以纳秒为单位。

I think I can assume that the readings were taken at roughly the same time, so of the two possible answers I think I'm safe taking the smallest. 我想我可以假设读数是在大致相同的时间进行的,所以对于两个可能的答案,我认为我是最安全的。

They're both stored as uint64_t . 它们都存储为uint64_t Because I don't think I can have 48 bit types. 因为我认为我不能拥有48位类型。

I'd like to calculate the difference between them, as a signed integer (presumably int64_t ), accounting for the wrapping. 我想计算它们之间的差异,作为有符号整数(可能是int64_t ),计算包装。

so eg if I start out with 所以,例如,如果我开始

x=5

y=3

then the result of xy is 2 , and will stay so if I increment both x and y , even as they wrap over the top of the max value 0xffffffffffff 然后xy的结果是2 ,如果我增加xy ,它将保持y ,即使它们包裹在最大值0xffffffffffff的顶部

Similarly if x=3, y=5, then xy is -2, and will stay so whenever x and y are incremented simultaneously. 类似地,如果x = 3,y = 5,那么xy是-2,并且只要x和y同时递增,它就会保持不变。

If I could declare x , y as uint48_t , and the difference as int48_t , then I think 如果我可以声明xyuint48_t ,差值为int48_t ,那么我想

int48_t diff = x - y; 

would just work. 会工作的。

How do I simulate this behaviour with the 64-bit arithmetic I've got available? 如何使用我可用的64位算法模拟此行为?

(I think any computer this is likely to run on will use 2's complement arithmetic) (我认为任何可能运行的计算机都会使用2的补码算法)

PS I can probably hack this out, but I wonder if there's a nice neat standard way to do this sort of thing, which the next person to read my code will be able to understand. PS我可能会破解这个,但我想知道是否有一个很好的标准方法来做这种事情,下一个阅读我的代码的人将能够理解。

PPS Also, this code is going to end up in the tightest of tight loops, so something that will compile efficiently would be nice, so that if there has to be a choice, speed trumps readability. PPS此外,这段代码最终会出现在最严格的紧密循环中,所以有效编译的东西会很好,所以如果必须有选择,速度就会超过可读性。

You can simulate a 48-bit unsigned integer type by just masking off the top 16 bits of a uint64_t after any arithmetic operation. 您可以通过在任何算术运算后屏蔽uint64_t的前16位来模拟48位无符号整数类型。 So, for example, to take the difference between those two times, you could do: 因此,举例来说,为了获得这两次之间的差异,您可以:

uint64_t diff = (after - before) & 0xffffffffffff;

You will get the right value even if the counter wrapped around during the procedure. 即使计数器在程序中缠绕,您也会得到正确的值。 If the counter didn't wrap around, the masking is not needed but not harmful either. 如果计数器没有环绕,则不需要屏蔽但也不会有害。

Now if you want this difference to be recognized as a signed integer by your compiler, you have to sign extend the 48th bit. 现在,如果您希望编译器将此差异识别为有符号整数,则必须对第48位进行符号扩展 That means that if the 48th bit is set, the number is negative, and you want to set the 49th through the 64th bit of your 64-bit integer. 这意味着如果设置了第48位,则该数字为负,并且您要设置64位整数的第49位到第64位。 I think a simple way to do that is: 我认为一个简单的方法是:

int64_t diff_signed = (int64_t)(diff << 16) >> 16;

Warning: You should probably test this to make sure it works, and also beware there is implementation-defined behavior when I cast the uint64_t to an int64_t , and I think there is implementation-defined behavior when I shift a signed negative number to the right. 警告:您应该对此进行测试以确保其正常工作,并且当我将uint64_t转换为int64_t ,请注意存在实现定义的行为,并且当我将有符号的负数转移到右侧时,我认为存在实现定义的行为。 I'm sure a C language lawyer could some up with something more robust. 我确信一位C语言律师能够提供一些更强大的东西。

Update: The OP points out that if you combine the operation of taking the difference and doing the sign extension, there is no need for masking. 更新: OP指出,如果您结合使用差异和执行符号扩展的操作,则不需要屏蔽。 That would look like this: 这看起来像这样:

int64_t diff = (int64_t)(x - y) << 16 >> 16;
struct Nanosecond48{
    unsigned long long u48 : 48;
//  int res : 12; // just for clarity, don't need this one really
};

Here we just use the explicit width of the field to be 48 bits and with that (admittedly somewhat awkward) type you live it up to your compiler to properly handle different architectures/platforms/whatnot. 在这里,我们只使用字段的显式宽度为48位,并且使用它(当然有些尴尬)类型,您可以将其用于编译器以正确处理不同的体系结构/平台/诸如此类。

Like the following: 如下:

Nanosecond48 u1, u2, overflow;
overflow.u48 = -1L;
u1.u48 = 3;
u2.u48 = 5;
const auto diff = (u2.u48 + (overflow.u48 + 1) - u1.u48) & 0x0000FFFFFFFFFFFF;

Of course in the last statement you can just do the remainder operation with % (overflow.u48 + 1) if you prefer. 当然,在最后一个语句中,如果您愿意,可以使用% (overflow.u48 + 1)执行余数运算。

Do you know which was the earlier reading and which was later? 你知道哪个是早期阅读,哪个是后来的? If so: 如果是这样:

diff = (earlier <= later) ? later - earlier : WRAPVAL - earlier + later;

where WRAPVAL is (1 << 48) is pretty easy to read. 其中WRAPVAL (1 << 48)非常容易阅读。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM