2^n - 1 不会溢出 long

2^n - 1 without overflowing a long

这是一个 C89 项目,其中 LONG_IS_64BIT 定义为(且仅当)long 是 64 位,即包含从 -2^63 开始的所有整数-1 到 2^63-1。否则(根据 C 标准)它包含从 -2^31-1 到 2^31-1 的所有整数。

我有一个数字 n 如果定义了 LONG_IS_64BIT 则它保证在 0 到 63(含)之间,否则为 0 到 31(含)。我想计算 2^n-1,它适合 long.

目前代码有 (1L<<n) - 1,但在极有可能 long 正好是 32 位或 64 位的情况下,这是未定义的行为。 (在这部分程序中 n==63 几乎是不可能的,但在 32 位计算机上 n==31 肯定会发生。)这样做的正确方法是什么?

我想我可以只测试 n==31n==63,但这感觉很老套。

如果您知道 (1L<<n)-1 的数学值适合 long,您可以通过计算值减一然后加一来确保不会溢出,而不是计算值加一,然后减一。

n == 0 ? 1 : ((1L<<n-1)-1<<1)+1

这很复杂,如果 n == 0,需要特殊的外壳来避免左移负值,但至少它可以为您提供所需的值。

或者,您可以使用右移:

#ifdef LONG_IS_64BIT
0x7FFFFFFFFFFFFFFF>>(63-n)
#else
0x7FFFFFFF>>(31-n)
#endif

如果它可能比您预期的要大,您不能在此处使用 LONG_MAX

但实际上,@melpomene 对使用 unsigned long 的评论应该足够好。在编写标准时,它具有与 long 相同数量的值位的平台已经不常见了。如果您已经假设 long 正好有 32 位或正好有 64 位,您可能不应该担心更深奥的实现。