int x = something, y = something, z = something;
// Compare
int absall = (x > 0 ? x : -x) + (y > 0 ? y : -y) + (z > 0 ? z : -z);
int absall = abs(x) + abs(y) + abs(z);
// Negative absolute value
int nabs(int value) {
return -abs(value); // abs(INT_MIN) is undefined behavior.
}
int nabs(int value) {
return value < 0 ? value : -value; // well defined for all `int`
}
9条答案
按热度按时间xlpyo6sf1#
你提出的"条件abs"不等同于浮点数的
std::abs
(或fabs
),见例。输出:
给定
-0.0
和0.0
表示相同的实数"0",这个差异可能重要也可能不重要,这取决于结果的使用方式。然而,IEEE754规定的abs函数要求结果的符号位为0,这将禁止结果-0.0
。我个人认为任何用于计算一些"绝对值"的东西都应该与这种行为相匹配。对于整数,两个变量在运行时和行为上都是等效的。(Live example)
但是,由于
std::abs
(或fitting C的等价形式)是正确的,而且更容易阅读,因此您应该总是更喜欢这些形式。gijlo24d2#
首先想到的是可读性。
比较这两行代码:
ewm0tg9j3#
编译器最有可能在底层为这两个编译器做同样的事情--至少是一个现代的有能力的编译器。
但是,至少对于浮点数,如果要处理无穷大、非数字(NaN)、负零等所有特殊情况,最终将需要编写几十行代码。
而且,如果
abs
取绝对值,比阅读它小于零,求反更容易理解。如果编译器是“愚蠢的”,它很可能最终会为
a = (a < 0)?-a:a
做更糟糕的代码,因为它强制if
(即使它是隐藏的),这可能比该处理器上的内置浮点abs指令更糟糕(除了特殊值的复杂性之外)对于第二种情况,Clang(6.0-pre-release)和gcc(4.9.2)都会生成更差的代码。
我写了这个小例子:
clang为func 1生成以下代码:
g++函数1:
g++函数2:
请注意,第二种形式的两种情况都要复杂得多,在gcc的情况下,它使用了一个分支; Clang使用了更多的指令,但没有分支;我不确定在哪种处理器型号上哪个更快,但很明显,更多的指令很少更好。
vktxenjb4#
为什么要用abs()或fabs()来代替条件否定?
已经陈述了各种原因,但是考虑条件代码的优点,因为应该避免
abs(INT_MIN)
。当查找整数的 * 负 * 绝对值时,有充分的理由使用条件代码代替
abs()
当需要一个正的绝对值函数,并且
value == INT_MIN
是一个真实的可能性时,abs()
,尽管它的清晰度和速度都失败了。tuwxkamq5#
在给定的体系结构上,可能有比条件分支更有效的低级实现。例如,CPU可能有
abs
指令,或者有一种方法可以提取符号位而不需要分支开销。假设算术右移可以在数字为负数时用-1填充寄存器 r,或者在数字为正数时用0填充寄存器a a *。abs x
可以变成(x+r)^r
(从Mats Petersson的回答可以看出,g++实际上是在x86上实现的)。其他答案已经讨论了IEEE浮点的情况。
试图告诉编译器执行条件分支而不是信任库可能是过早的优化。
6tqwzwtp6#
假设您可以将一个复杂的表达式输入到
abs()
中,如果使用expr > 0 ? expr : -expr
编写代码,则必须将整个表达式重复三次,并且它将被计算两次。此外,两个结果(冒号前后)可能是不同的类型(如
signed int
/unsigned int
),这就禁止了在return语句中使用。当然,您可以添加一个临时变量,但这只能解决部分问题,无论如何也不会更好。qhhrdooz7#
......如果您将其制作为宏,则可能会有多个您可能不希望的求值(副作用)。请考虑:
并使用:
这将扩大到
函数调用不会有这种意想不到的副作用。
8ehkhllq8#
假设编译器无法确定两个abs()和条件否定试图实现相同的目标,条件否定编译为比较指令、条件跳转指令和移动指令,而abs()或者编译成支持实际绝对值指令的指令集中的实际绝对值指令,或者编译成保持所有内容相同的逐位指令,除了符号位。上面的每个指令通常是1个周期,因此使用abs()可能至少与条件否定一样快,或比条件否定更快(由于编译器可能仍然识别出在使用条件否定时您正试图计算绝对值,并且无论如何都生成绝对值指令)。即使编译后的代码没有变化,abs()仍然比条件否定更可读。
tnkciper9#
abs()背后的意图是“(无条件地)将这个数的符号设置为正数”,即使这必须根据数的当前状态作为条件来实现,但将其视为简单的“do this”可能更有用,而不是更复杂的“if... this... that”。