【问题标题】:"Symmetrical difference" for unsigned ints - assumed rollover无符号整数的“对称差” - 假设翻转
【发布时间】:2020-09-16 21:39:54
【问题描述】:

我创建了一个名为symmetricDelta() 的简单函数,它“对称地”计算valueprevious 之间的增量。我的意思是:考虑一个数字线,例如0ULLONG_MAX,在这里你连接了数字线的左端和右端......为了确定“对称”增量,假设如果 value - previous 小于跨度的一半,则变化为正,否则假设变化是负面的,我们绕着数字线。

在下面查看uint64_ts 的简单版本:

int64_t symmetricDelta(uint64_t value, uint64_t previous) {
    if (value-previous < (1ULL << 63)) {
        uint64_t result = value - previous;
        return result;
    } else {
        uint64_t negativeResult = previous - value;
        return -1 * negativeResult;
    }
}

用法:

    uint64_t value = ULLONG_MAX;
    uint64_t previous = 0;

    // Result: -1, not UULONG_MAX
    cout << symmetricDelta(value, previous) << endl;

演示:https://onlinegdb.com/BJ8FFZgrP

其他值示例,为简单起见假设为uint8_t 版本:

symmetricalDifference(1, 0) == 1
symmetricalDifference(0, 1) == -1
symmetricalDifference(0, 255) == 1
symmetricalDifference(255, 0) == -1
symmetricalDifference(227, 100) == 127
symmetricalDifference(228, 100) == -128

我的问题是:我所说的“对称减法”是否有一个“官方”名称?这感觉就像可能已经在 C++ STL 中实现的那种东西,但我什至不知道要搜索什么......

【问题讨论】:

  • -ULLONG_MAX1
  • 你能举一个例子来说明一些值以及函数应该为这些值产生什么吗?我不太了解您要达到的目标。
  • @NathanOliver:你说得对,我可以选择一个不那么琐碎的例子(从技术上讲,-ULLONG_MAX 是 UDB,如果你在有符号的计算中进行,但要点是)。更新示例。
  • 基本上min(abs(a-b),abs(b-a))是你在做的?
  • 现在我很困惑乌干达开发银行是如何参与其中的。

标签: c++ stl subtraction integer-overflow underflow


【解决方案1】:

是的。名称是减法模2^64。它与你的机器对指令所做的相同

int64_t symmetricDelta(uint64_t value, uint64_t previous) {
    return (int64_t)(value-previous);
}

在 C 和 C++ 中,无符号算术被定义为环绕,有效地将可表示数字范围的末端连接成一个圆圈。这是有符号整数的 2 补码表示的基础:您的 CPU 只需声明一半的数字圆被解释为负数。这部分是无符号的上部,-1 对应于最大可表示的无符号整数。只是因为0 在圈子上是下一个。

旁注:
这允许 CPU 使用完全相同的电路进行有符号和无符号运算。 CPU 仅提供add 指令,无论数字应解释为有符号还是无符号,都可以使用该指令。加法、减法和乘法也是如此,它们都以无符号指令的形式存在。只有除法是在有符号和无符号变体中实现的,比较指令/CPU 提供的标志位也是如此。

旁注2:
上述情况并不完全正确,因为现代 CPU 将饱和算术作为其矢量单元(AVX 等)的一部分。因为饱和算术意味着将结果剪裁到可表示范围的末端而不是环绕,所以这种剪裁取决于假设数字圈被破坏的位置。因此,饱和算术指令通常存在于有符号和无符号变体中。

结束不必要的背景漫谈……

因此,当您在无符号表示中减去两个数字时,结果是您必须采取的无符号步数才能从减数中到达被减数。通过将结果重新解释为有符号整数,您将一条长路线(绕圆超过一半)解释为相反方向的相应短路线。


有一个陷阱:1 &lt;&lt; 63 不可表示。它正好在数字圆的零的对面,并且由于它的符号位被设置,它被解释为-(1 &lt;&lt; 63)。如果您尝试否定它,则位模式不会改变一位(就像-0 == 0),因此您的计算机会愉快地声明- -(1 &lt;&lt; 63) == -(1 &lt;&lt; 63)。这对你来说可能不是问题,但最好意识到这一点,因为它可能会咬你。

【讨论】:

  • 感谢@cmaster-reinstate-monica!事实上,我在我的代码中利用了无符号整数翻转,但我没有看到树木的森林——我忘记了我可以将它转换为有符号的结果,而不是那个丑陋的if-statement。不需要 STL 函数 ;-)
  • 是的,我已经接受了,如果数字恰好是所选无符号整数类型跨度的一半(例如,128 代表 uint8_t 或 @987654332 @ for a uint64_t)——在我的用例中不是问题,但对后代来说是一个很好的指针。
猜你喜欢
  • 2014-04-22
  • 2021-09-09
  • 2013-01-13
  • 1970-01-01
  • 2013-02-16
  • 2012-01-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多