签名或无符号整数是否应用于大小?

时间:2017-11-14 10:33:38

标签: c++

标准库vector::size()提供size_t,无符号数。在其中一次CppCon会谈中,我听到有人(Chandler Carruth?)说这很不幸,而且应该使用签名整数。

背景是没有为有符号整数定义溢出,因此编译器有更多的余地。在一次演讲中,Carruth展示了bzip2中uint8_t作为for循环索引如何在x86上创建的机器指令多于int8_t,因为它必须使用掩码和移位显式模拟溢出。< / p>

在我现在使用的代码中,有一些严格要求的大小。这些表示为size_t。这看起来不错,因为这表明他们不能消极。另一方面,不需要定义的模运算,因此只要有符号整数足够大(我们去200),无符号整数就会有我们想要的算术错误的接口。

在代码中的某个时刻,存在从0到此大小的循环。然后减去循环索引并获取绝对值。

当我用更现代的GCC 7编译它时,它无法解决std::abs的正确重载,因为size_t - size_t显然具有暧昧的价值。我已将代码更改为在循环索引中使用int

for (int t1 = 0; t1 < Lt; t1++) {
  for (int t2 = 0; t2 < Lt; t2++) {

现在abs(t1 - t2)工作得很好。但比较t1 < Lt会发出警告,因为它是有符号和无符号数字之间的比较。

什么是正确的方法?

  1. 使用无符号整数进行非负的整数,然后在需要减法时使用static_cast<int>()
  2. 对于循环索引使用有符号整数,但对于容器的大小使用无符号整数。然后在比较中使用static_cast<int>
  3. 只需在任何地方使用有符号整数。当其他库返回无符号整数时,只需在那里使用static_cast<int>即可满足警告。

1 个答案:

答案 0 :(得分:2)

“在一次演讲中,Carruth展示了作为bzip2中for循环索引的uint8_t如何在x86上创建比int8_t更多的机器指令,因为它必须使用掩码和移位显式模拟溢出。”

好吧,如果您可以使用任何一种类型,则for-range必须限制为[0, 127]。然后使用int作为索引类型。 按照定义是基本数学运算的自然类型,通常可以很好地映射到CPU寄存器。

使用针对最小存储优化的类型将不会生成最快的数学,不会。这并不奇怪。根据这些有缺陷的设置,您无法得出有关已签名与未签名的结论。

size_t - size_t提供不明确的值”

嗯,它没有,但确实使用了模运算。 size_t(1)-size_t(2)==size_t(-1),但size_t(-1)是可能的最大值。这直接来自模块化数学的定义:x-1 < x,除非x-1包裹因为x==0。 (或等效x+1>x,除非x+1==0

因此,调用abs(size_t(x))也毫无意义,因为每个size_t值都是正数。将有符号整数与size_t进行比较同样充满了意想不到的后果。明确的演员阵容很好,因为它们可以清楚地表明后果。

但是没有通用的解决方案来自动确定应该应用哪个演员表。如果可以发明机械规则,我们可以将该规则留给编译器。我们没有,因为我们做不到。作为程序员,您必须以数字方式考虑每个案例。