chapter 3-6

文章目录

  • 3.检测两个整数是否有相反的符号
  • 4.计算整数的绝对值(abs)而不使用分支
  • 5.计算两个整数的最小值或最大值而不使用分支
  • 6.确定一个整数是否是2的幂

3.检测两个整数是否有相反的符号

//检测两个整数是否有相反的符号
int x, y;               // 要比较符号的输入值
bool f = ((x ^ y) < 0); // 当且仅当 x 和 y 有相反的符号时为 true

4.计算整数的绝对值(abs)而不使用分支

int v;           // 我们要找到 v 的绝对值
unsigned int r;  // 结果存储在这里
int const mask = v >> sizeof(int) * CHAR_BIT - 1;

r = (v + mask) ^ mask;
专利变体:
r = (v ^ mask) - mask;

在某些计算机上,没有整数绝对值指令(或者编译器未使用它们)。在分支操作昂贵的机器上,上述表达式可能比明显的方法更快,即 r = (v < 0) ? -(unsigned)v : v,尽管操作的数量相同。
在2003年3月7日,Angus Duggan指出,1989年的ANSI C规范将有符号右移的结果定义为特定实现,因此在某些系统上这种技巧可能不起作用。我已经阅读到,ANSI C不要求值以二进制补码形式表示,所以可能由于这个原因在某些使用一的补码的旧机器上也不起作用。在2004年3月14日,Keith H. Duggar向我发送了上述的专利变体,它优于我最初想出的这个表达式,r = (+1 | (v >> (sizeof(int) * CHAR_BIT - 1))) * v,因为没有使用乘法。不幸的是,这种方法在2000年6月6日由Vladimir Yu Volkonsky获得了美国专利,并由Sun Microsystems分配。在2006年8月13日,Yuriy Kaminskiy告诉我,该专利可能无效,因为该方法在专利申请之前就已被发布,例如在1996年11月9日Agner Fog的《如何为Pentium处理器进行优化》中。Yuriy还提到,这份文件在1997年已经翻译成俄语,Vladimir可能已经阅读了。此外,互联网档案馆也有一个与之相关的旧链接。在2007年1月30日,Peter Kankowski与我分享了一个由Microsoft的Visual C++编译器输出启发的abs版本。它在这里作为主要解决方案展示。在2007年12月6日,Hai Jin抱怨说结果是有符号的,因此当计算最小值的绝对值时,它仍然是负数。在2008年4月15日,Andrew Shapira指出,明显的方法可能会溢出,因为它当时缺少(unsigned)类型转换;为了最大的可移植性,他建议使用(v < 0)?(1 + ((unsigned)(-1-v))):(unsigned)v。但是在2008年7月9日,Vincent Lefèvre引用ISO C99规范说服我删除它,因为即使在非二进制补码的机器上,-(unsigned)v也会得到正确的结果。对-(unsigned)v的求值首先将v的负值通过添加2N转换为无符号值,得到了我称之为U的2s补码表示。然后,U被否定,得到了所需的结果,-U = 0 - U = 2N - U = 2N - (v+2N) = -v = abs(v)。

5.计算两个整数的最小值或最大值而不使用分支

int x;  // 我们要找到 x 和 y 的最小值
int y;
int r;  // 结果存储在这里

r = y ^ ((x ^ y) & -(x < y)); // 最小值(min(x, y))

这段代码用于找到整数 x 和 y 的最小值,将结果存储在 r 中。它不使用分支语句,而是使用位运算来实现。这种方法可以在一些极少数的机器上比明显的方法更快,即 r = (x < y) ? x : y,即使它涉及两个额外的指令。通常来说,明显的方法是最好的。这种方法之所以有效,是因为如果 x < y,则 -(x < y) 将全部是1,所以 r = y ^ (x ^ y) & ~0 = y ^ x ^ y = x。否则,如果 x >= y,则 -(x < y) 将全部是0,所以 r = y ^ ((x ^ y) & 0) = y。

要计算最大值,只需使用以下代码:

r = x ^ ((x ^ y) & -(x < y)); // 最大值(max(x, y))

还有一种快速且不太精确的版本,如果你知道 INT_MIN <= x - y <= INT_MAX,则可以使用以下方法,因为 (x - y) 只需评估一次:

r = y + ((x - y) & ((x - y) >> (sizeof(int) * CHAR_BIT - 1))); // 最小值(min(x, y))
r = x - ((x - y) & ((x - y) >> (sizeof(int) * CHAR_BIT - 1))); // 最大值(max(x, y))

需要注意,1989 ANSI C 规范没有明确定义有符号右移的结果,所以这些代码不是可移植的。如果溢出会引发异常,那么 x 和 y 的值应为无符号或应该在做减法时转换为无符号,以避免不必要的异常,但右移需要有符号操作数以在负数时产生全1位。

这段代码有一些限制和不确定性,需要小心使用,并且可能不适用于所有情况

6.确定一个整数是否是2的幂

unsigned int v; // 我们要确定 v 是否是2的幂
bool f;         // 结果存储在这里

f = (v & (v - 1)) == 0;

需要注意的是,这段代码会错误地将0视为2的幂。如果要避免这种情况,可以使用以下修改的版本:

f = v && !(v & (v - 1));

这个修改版会排除0,只有正整数且是2的幂的情况才会被视为是2的幂。

你可能感兴趣的:(位,c++)