【问题标题】:How to perform signed comparison between unsigned operands?如何在无符号操作数之间执行有符号比较?
【发布时间】:2018-02-16 17:40:00
【问题描述】:

我已经声明了 4 个无符号变量:

uint32_t empty_bucket;
uint32_t base_bucket;
uint32_t hop_size;
uint32_t ht_size;

我要执行签名条件检查:

if (empty_bucket < base_bucket + (hop_size - 1) - ht_size)

知道base_bucket + (hop_size - 1) - ht_size 可能是负值。操作数的正确转换是什么来执行这个单独的操作?

注意:base_bucket + (hop_size - 1) - ht_size 可能与 -2^32 非常接近,因此转换为带符号的 32 位 int32_t 可能会导致溢出。

【问题讨论】:

  • 不清楚你在问什么。根据定义,无符号整数不能变为负数。差异也不会变成负数。 无符号整数的上溢/下溢 回绕,但结果仍然是正数(阅读模数运算)。如果将表达式的操作数强制转换为有符号,则会调用未定义的行为。
  • @Olaf 确实有道理。 base_bucket + (hop_size - 1) - ht_size 可能是否定的。读作:数学上这个表达式可以是负数,但在 C 中,因为我使用无符号类型,所以我得到无符号模数行为。如何使我的 C 代码表现得像数学模型?
  • @bolov:是的,但前提是int 大于uint32_t。否则不能。你想回顾一下关于算术运算符的章节。但是为什么你有一个 sock-puppet 帐户呢?
  • @Olaf 袜子木偶账号?什么意思?
  • 是否有某些原因不能将条件更改为:if (empty_bucket + ht_size &lt; base_bucket + (hop_size - 1))

标签: c casting integer unsigned signed


【解决方案1】:

由于您使用 stdint 包含,您可以将操作数转换为 64 位有符号值,并进行比较,没有任何右侧项变为负数的风险,我们必须将左侧操作数转换为有符号整数以避免比较有符号/无符号时的未定义/实现行为:

if ((int64_t)empty_bucket < ((int64_t)base_bucket + ((int64_t)hop_size - 1) - (int64_t)ht_size))

总结一下:

  • 没有溢出的风险(我可能在右侧施放过多)
  • 已签名实体之间的比较
  • 不利的一面是,64 位转换可能会对 32 位架构的性能产生负面影响

【讨论】:

  • 这正是我在发布之前的想法和尝试,我得到了我想要的(负值),但我认为基于更好的理解可能有更好的方法。跨度>
  • 当人们听到“对性能的负面影响”时,他们大多是下意识的反应,并且过于重视它。除非我们谈论的是一些有限的嵌入式系统,否则使用 64 位操作数而不是 32 位操作数几乎没有影响,尤其是当我们没有乘法或除法时。
  • 在大多数较小的系统上使用 64 位整数是不利的。应该尽可能避免(而且似乎是)。
  • @bolov 我添加了“在 32 位架构上”。
  • @bolov 确切地说,我正在使用 64 位台式机,这些小性能伤害对我来说没什么大不了的。实际上,我正在实现 Hopscotch hashmap 的环绕版本,并希望测量阴影版本和位图版本之间的性能。
【解决方案2】:
if (base_bucket + hop_size > ht_size + 1
    && empty_bucket < base_bucket + (hop_size - 1) - ht_size)

第一行检查我们要执行的比较的右侧是否确实是一个正整数。这是通过检查所有正值(base_buckethop_size)是否大于所有负值(- 1- ht_size)来完成的。它在不使用减法的情况下执行此操作,因此使用无符号整数是安全的。

@David Bowling 建议

if (empty_bucket + ht_size < base_bucket + (hop_size - 1)) 

这个想法基本相同,以确保比较的双方总是积极的。如果base_buckethop_size 不是同时为零,则此方法有效。

对于这两种解决方案,理论上仍然可能会溢出,您必须使用实际值进行检查。如果有溢出,请使用更大的类型。

请忽略我之前提到的短路评估,因为它不相关。如果整数大小是“正常的”,例如。 16、32 或 64 位,这应该可以工作。

【讨论】:

  • 请详细说明为什么会有警告。
  • 比较无符号和有符号?
  • 如果int 大于uint32_t,无论如何都会强制转换为int。否则都是unsigneduint322_t 是什么。我没有仔细检查它,但是是的,这可能是这样。不过,我会检查边界情况(最后一个很好地用于单元测试 - 是的)。
  • 是的,如果 int 是 32 位,则不允许提升,因为签名的 int 不能保存所有值。我提到的警告在这里真的不相关,我的错误。
  • 我仍然有添加的问题。如果没有来自 OP 的更多信息/保证,这里仍然可以进行 wrap(并且 hop_size - 1)也可以进行 wrap。
猜你喜欢
  • 2021-10-31
  • 2014-10-15
  • 2011-07-21
  • 1970-01-01
  • 1970-01-01
  • 2016-08-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多