【问题标题】:2^n - 1 without overflowing a long2^n - 1 不溢出 long
【发布时间】:2016-10-02 07:04:58
【问题描述】:

这是一个 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,但在longs 很可能正好是32 位或64 位的情况下,这是未定义的行为。 (在这部分程序中n==63 几乎是不可能的,但在 32 位计算机上n==31 肯定会发生。)正确的方法是什么?

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

【问题讨论】:

  • 如果您希望它真正可移植,请使用硬编码常量进行 ifdef 检查。
  • @2501 是的,这就是我判断我所处的情况的方式。代码中到处都是#ifdef LONG_IS_64BIT 测试。但是我写什么而不是(1L<<n)-1?我可以使用n==31 ? 0x7FFFFFFF : (1LL<<n)-1(在另一种情况下也是如此),但这是我能做的最好的吗?
  • “否则(按照 C 标准)它包含从 -2^31 到 2^31-1 的所有整数。” -- 不,C 标准不保证这一点。它保证至少 32 位,但如果正好是 32 位,则值可能会从 -(2^31-1) 变为 2^31-1。这些实现非常罕见。您是否正在寻找可以 100% 便携使用的东西,或者适合您将要处理的实现的东西?如果是后者,它们是哪些?
  • 取决于你的情况下best的含义。
  • (long)((1UL << n) - 1)

标签: c undefined standards integer-overflow c89


【解决方案1】:

如果您知道(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 位,您可能不应该担心更深奥的实现。

【讨论】:

  • 当类型已知为 32 位或 64 位时,我不确定我是否理解 LONG_MAX 如何成为 larger than you expect
  • @4386427 long 不知道是 32 位还是 64 位,至少不是基于问题中的信息。根据问题中的信息,仅知道基于标准的至少 32 位,或者如果定义了特定的宏,则至少知道 64 位。但我承认 OP 很可能已经假设它是 32 位或 64 位。
  • @hvd 我知道LONG_MAX 不可信。 :)
猜你喜欢
  • 2021-05-27
  • 1970-01-01
  • 2022-01-04
  • 1970-01-01
  • 2011-01-29
  • 1970-01-01
  • 2015-10-08
  • 1970-01-01
相关资源
最近更新 更多