如何在无符号操作数之间进行带符号比较?

How to perform signed comparison between unsigned operands?

我声明了 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 可能会导致溢出.

由于您使用的是 stdint include,您可以将操作数转换为 64 位有符号值,然后进行比较,不会有任何右侧项变为负数的风险,我们必须强制转换左操作数为有符号整数,以避免在比较 signed/unsigned:

时出现 undefined/implementation 行为
if ((int64_t)empty_bucket < ((int64_t)base_bucket + ((int64_t)hop_size - 1) - (int64_t)ht_size))

总结一下:

  • 没有溢出的风险(我可能在右侧投得太多了)
  • 签名实体之间的比较
  • 不利的一面是,64 位转换可能会对 32 位架构的性能产生负面影响
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 不同时都为零,则此方法有效。

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

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