【问题标题】:C find maximum two's complement integerC找到最大的二进制补码整数
【发布时间】:2018-10-17 15:45:03
【问题描述】:

我的任务是找到最大的二进制补码整数,或 TMax。我对如何做到这一点完全不知所措。我知道正确的值是 0x7fffffff 或 2147483647,但我不知道究竟如何得到这个结果。这是 32 位整数的最大数量。我不能使用函数或条件,最多可以使用 4 个操作。任何人都可以尝试帮助向我解释这一点吗?我知道找到某个位数的最大数的方法是 2^(bits - 1) - 1,所以 2^(31) - 1 = 2147483647

【问题讨论】:

  • 他的问题允许使用班次。其实我就是这么回答的。
  • 我看了你提到的问题。对不起,这不是同一个问题。
  • 什么不使用INT_MAX 最大intINTMAX_MAX 最大“整数”?
  • 可能唯一正确的答案是1

标签: c binary twos-complement


【解决方案1】:

假设您知道您的机器使用二进制补码表示,那么您将以符合标准的方式这样做:

unsigned int x = ~0u;
x >>= 1;
printf("max int = %d\n", (int)x);

通过使用unsigned int,您可以防止因右移负值而导致的任何实现定义的行为。

【讨论】:

  • 作为独立表达式:(int)(~0u >> 1)
  • C 语言标准允许 int 小至 16 位。
  • @anatolyg 你使用的是他使用的相同表达方式,但结合使用。
  • @DrM 使用 long int 感觉如何
  • 这做出了非常合理的假设,即UINT_MAX/2 == INT_MAX。虽然这很常见,但 C 只需要 UINT_MAX >= INT_MAX。 §6.2.5 9. 还有。这个答案根本不需要依赖二进制补码。
【解决方案2】:

求最大补码整数

int TMax = -1u >> 1-1u/2 足以当INT_MAX == UINT_MAX/2 找到最大int

即使int 被编码为 2 的补码或现在罕见的 1 的补码或符号幅度,这也“有效”。


更好用

#include <limits.h>
int TMax = INT_MAX;

其他技巧可能涉及未定义、实现定义、未指定的行为,这些在 C 中最好避免。

【讨论】:

    【解决方案3】:

    在给定整数数据类型或给定位数的情况下,您可能会在两种情况下寻找最大正数。这也是两种解决方案。

    填充并向右移动

    使用与所需二进制补码数据类型的大小完全匹配的整数数据类型,您可以通过以下方式解决问题

    (unsigned 'type') ^0)>>1 
    

    或等价的,

    (unsigned 'type') ^0)/2.
    

    例如,在 short 为 16 位的机器上,

    (unsigned short) ^0   ==>  0xFFFF  (65535)
    ((unsigned short) ^0 ) >> 1  ==>  0x7FFF  (32767)
    

    在 32 位数据类型上,此方法为我们提供 0x7FFFFFFF (2147483647)。

    在 C 中,整数类型只有最小大小,c.f. int 可以是 16 位、32 位或更大。但是,计算中使用的字长必须与预期目标的字长完全匹配。

    另外,请注意数据必须是无符号类型。有符号类型的右移通常实现为符号扩展移位(符号位被复制到结果中)。

    只设置符号位并减1

    第二种技术适用于等于或大于所需二进制补码字长的位数的任何字长,是

    (unsigned integer_type) 1<<(n-1)-1
    

    例如,在任何大于或大于 16 的整数字长中,我们可以找到 16 的 TMAX 为

    (unsigned integer_type) 1<<15  ==>  binary 1000 0000 0000 0000  (0x8000)
    (unsigned integer_type) (1<<15 - 1) == > 0111 1111 1111 1111 (0x7FFF)
    

    这很强大,几乎适用于任何提供足够字长的场景。

    如果计算中的字长是目标的字长,则计算的数据类型必须是无符号的。对于较大的字长,这不是必需的。

    示例

    在第一个示例中,我们展示了第二种方法适用于 32 位,使用 long 或 long long 类型。

    #include <stdio.h>
    
    int main() {
    
      printf( "%ld\n", (long) ( ( ((unsigned long) 1)<<31 ) - 1 ) );
      printf( "%lld\n", (long long) ( ( ((unsigned long long) 1)<<31 ) - 1 ) );
    
    }
    

    输出:

    2147483647
    2147483647
    

    这里我们展示了第一种方法,从所有位集右移,当 int 不完全是 32 位时会失败,如前所述,这在 C 中是不保证的。

    #include <stdio.h>
    
    int main() {
    
      printf( "from long long %lld  (%zu bits)\n", ( (unsigned long long) ~0 )>>1,
          sizeof(unsigned long long)*8 );
    
      printf( "from long %ld  (%zu bits)\n",  ( (unsigned long) ~0 )>>1,
          sizeof(unsigned long)*8  );
    
      printf( "from int %d  (%zu bits)\n",  ( (unsigned int) ~0 )>>1,
          sizeof(unsigned int)*8  );
    
      printf( "from short %d  (%zu bits)\n",  ( (unsigned short) ~0 )>>1,
          sizeof(unsigned short)*8  );
    }
    

    输出:

    from long long 9223372036854775807  (64 bits)
    from long 9223372036854775807  (64 bits)
    from int 2147483647  (32 bits)
    from short 32767  (16 bits)
    

    再次提醒,C 语言只保证任何整数数据类型的最小大小。 int 可以是 16 位或 32 位或更大,具体取决于您的平台。

    【讨论】:

    • " 公式 1 不。当然在绝大多数平台上,但UINT_MAX == INT_MAX 是可能的-即使它只适用于墓地大铁。
    • 重新发表您的第一条评论:真的吗?这对双补码有何作用?根据定义,INT_MAX 似乎必须小于 UINT_MAX,除非硬件介入干预。我已经构建了很多硬件,但我认为这不会那么容易发生。
    • sizeof(...)*8 最佳匹配 "%zu",而不是 "%lu"
    • C 具有“有符号整数类型的非负值范围是相应无符号整数类型的子范围”§6.2.5 9 我采用“子范围”来暗示重叠范围。即使使用旧机器,我也只在最广泛的类型上看到过这种情况,而不是int
    • 重新发表您的第三条评论,很好。我会检查它并酌情修复它。但无论如何,这不是重点。
    【解决方案4】:

    感谢大家的帮助!原来我不能使用宏、无符号或长整数。我来到了这个解决方案:

    ~(1 << 31)
    

    这会产生正确的输出,所以我将保留它!

    【讨论】:

    • 此代码涉及整数溢出,可能会以意想不到的方式表现(未定义的行为)。在游戏规则内(没有unsigned 算术)这是可以的,但如果不受此类规则的约束,最好不要使用此代码。
    • 你真的应该再读一遍我的回答。我对其进行了修改,以更清晰地解释所有这些是如何工作的。您在这里写的确实不是正确答案,并且在某些计算机上会失败。
    猜你喜欢
    • 2015-06-08
    • 1970-01-01
    • 2013-03-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-26
    相关资源
    最近更新 更多