C++:无符号64位整数与有符号64位整数的差以有符号64位整数表示

4

我正在尝试编写一个C++函数,它接受两个64位无符号整数,并返回它们的有符号64位整数差。由于溢出情况的存在,这似乎有点复杂 - 由于输入是两个无符号正整数,如果它们之间的绝对差大于最大有符号值(INT64_MAX),则差不能通过有符号整数传输。因此,我编写了以下实现,并想知道首先,它在功能上是否正确,其次,是否有更简单的实现方法。任何建议都将不胜感激。谢谢!(我将用异常替换assert,现在只是暂时使用!)

int64_t GetDifference(uint64_t first, uint64_t second) {
  uint64_t abs_diff = (first > second) ? (first - second): (second - first);    
  uint64_t msb_abs_diff = (abs_diff >> (sizeof(abs_diff)*8 - 1)) & 1;
  assert(msb_abs_diff == 0);
  int64_t diff = first - second;
  return diff;
}

好的,感谢所有的回答!我更担心的是功能上的正确性,但我想这方面应该没问题了。所有的改进都是有效的,虽然并没有根本上的不同,但我会将它们合并进去。 - Abhi
任何一个好的编译器都应该能够优化这样的代码,只要确保它是正确的。 - Luka Rahne
4个回答

6
对我来说,这似乎是一种更简单和更易读的实现。
int64_t GetDifference(uint64_t first, uint64_t second) {
    uint64_t abs_diff = (first > second) ? (first - second): (second - first);
    assert(abs_diff<=INT64_MAX);
    return (first > second) ? (int64_t)abs_diff : -(int64_t)abs_diff;
}

4

有三个小问题:

  • sizeof(abs_diff)*8 - 1可以被文字表述63替代,不会失去可移植性(实际上,这会更加可移植,因为有些平台 char 不是8位宽)。
  • & 1不需要,因为移位的结果始终是一个比特位。
  • 你可以通过减法只计算一次来推导出diff,而不必再重复减去abs_diff

除此之外,这段代码在我看来完全正确。


sizeof(abs_diff)*CHAR_BIT - 1 更具可移植性。 - phuclv

4

这样更短,可能更快。

int64_t GetDifference(uint64_t first, uint64_t second)
{
  int64_t diff = first - second;
  bool overflowed = (diff < 0) ^ (first < second);
  assert(!overflowed);
  return diff;
}

一款优秀的优化编译器应该能够注意到diff < 0是负标志,以及first < second是前一个表达式的进位标志。比较这两个标志是检测溢出的经典测试。
即使编译器没有检测到,也需要较少的操作。
但我更喜欢这种方式的最大原因是,没有神奇的数字。

谢谢。我明白你的意思。如果使用int/uint而不是int64_t/uint64_t,那么这个方案就可以实现。 - Abhi
没有魔法数字,但你依赖于“未定义行为”,也就是将一个无符号数强制转换为整数表示时会发生溢出并循环的事实。 - Triskeldeian
@Triskeldeian:这是“实现定义”,而不是“未定义”。 - Ben Voigt

2
这个怎么样:
int64_t GetDifference(uint64_t first, uint64_t second) {
    int64_t diff = (int64_t)(first - second);
    assert first >= second && diff >= 0 || first < second && diff < 0;
    return diff;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接