C语言中的有符号操作和无符号操作

9

非常简单的问题:

我有一个程序需要对int和long long进行大量的数学计算。为了多容纳一位,我将long long变成了无符号数,因为我只处理正数并且能够获得更多的值。

奇怪的是,这给我的性能提升了15%,我确认这只是通过将所有long long变成无符号数实现的。

这可能吗?使用无符号数的数学运算真的更快吗?我记得读过没有区别,并且编译器会自动选择最快的方式,无论是有符号还是无符号。这个15%的提升真的是因为将变量变成无符号的吗,或者它可能是我代码中其他受影响的东西吗?

如果真的是因为将变量变成无符号的,那么我应该尽量将所有东西(包括ints)变成无符号的吗,因为我从不需要负数,每秒都很重要,如果我能节省时间的话。


1
生成的汇编代码说了什么? - Ignacio Vazquez-Abrams
我不知道如何访问或理解它。 - Jeff
1
通常情况下,使用一个额外的精度位并没有太多好处。如果你离边界这么近,很快你就会再次用完所有的位数。现在可以开始考虑一个新的解决方案了。 - Bo Persson
4个回答

12

在一些操作中,有符号整数的速度更快,而在其他操作中,则是无符号整数更快:

  • 在C语言中,可以假定有符号整数操作不会发生溢出。编译器将利用此功能进行循环优化,例如可以类似地对比较进行优化(如果您不希望这种情况出现,这也可能导致微妙的错误)。

  • 另一方面,无符号整数则没有这种假设。但是,不必处理符号对于某些操作来说是一个巨大的优势,例如:除法计算。除以2的幂次方的无符号除法只需要进行简单的移位操作,但是(根据取整规则),对于负数可能存在条件性的偏差。

就个人而言,我习惯于仅在真正需要有符号值的情况下才使用有符号整数。这主要是为了保持正确性,而非追求效率。

您可能会发现,在long long中会放大这种影响,这是因为(我猜测)在64位中。通常情况下,CPU不会使用单指令处理这些类型(在32位模式下),因此有符号操作的轻微复杂性将更加明显。


2
long long 在 x86 和 x86_64 上是 64 位的。 - Conrad Meyer
所有x86_64 ABI实现都是这样吗? - John Ripley
据我所知,是的。一些罕见的64位机器可能会使用128位的long long,但不包括x86_64。 - R.. GitHub STOP HELPING ICE

2

编译器关注变量是否有符号有三种情况:

  1. 当变量转换为更长的类型时
  2. 当应用比较运算符(大于、小于等)时
  3. 当可能发生溢出时

在某些机器上,将带符号变量转换为更长的类型需要额外的代码;而在其他机器上,转换可以作为“加载”或“移动”指令的一部分执行。

一些机器(主要是小型嵌入式微控制器)需要更多的指令来执行带符号比较,而无符号比较则需要较少的指令,但大多数机器都提供完整的带符号和无符号比较指令。

当无符号类型发生溢出时,编译器可能需要添加代码以确保实际发生了定义行为。对于带符号类型,则不需要此类代码,因为在缺乏此类代码的情况下可能发生的任何事情都是标准允许的。


当无符号类型发生溢出时,编译器可能需要添加代码以确保实际发生定义行为。这是否与某些特定架构相关? - martinkunev
1
@martinkunev:这在一台Univac补码机器上很相关,其编译器最后更新大约在2004年左右。编译器可以配置为使用奇数模数处理无符号数学,或者添加代码以在发生环绕时调整结果。 - supercat

2

在32位处理器上,64位整数操作是模拟的;使用unsigned而不是signed意味着模拟库不必额外工作以传播进位比特等。


1
编译器不会选择是无符号还是有符号。但是,理论上,“无符号与无符号”比“有符号与有符号”更快。如果你真的想减慢速度,你可以选择“有符号与无符号”。更糟糕的是:“浮点数与整数”。当然,这取决于处理器。

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