二进制转十进制

245 阅读5分钟

二进制转十进制

问题背景

本问题要求实现一个程序,该程序能够接收一个以字符串形式表示的二进制数,并将其转换为对应的十进制整数。

核心规则:二进制补码

需要特别注意的是,输入的二进制字符串代表的是一个整数的**二进制补码(Two's Complement)**形式。这是计算机系统中表示有符号整数(正数、负数和零)的标准方式。

  • 对于32位整数

    • 如果最高位(从左数第1位,或从右数第32位)是 0,则该数表示一个正数或零
    • 如果最高位是 1,则该数表示一个负数

任务要求

给定一个二进制字符串,请将其从二进制补码形式转换成标准的十进制整数。


输入格式

  • 一个字符串,代表一个二进制数。

    • 字符串中仅包含字符 '0''1'
    • 用例保证:输入字符串转换后的结果总是在 32位有符号整数 的范围内(即从 -2,147,483,6482,147,483,647)。

输出格式

  • 一个十进制整数,表示输入二进制字符串所对应的数值。

资源限制

  • 时间限制: C/C++ 1000ms, 其他语言 2000ms
  • 内存限制: C/C++ 64MB, 其他语言 128MB

样例说明

样例 1

  • 输入: "00011"

  • 输出: 3

  • 解释:

    1. 二进制 00011 的最高有效位是 0,表示这是一个正数。

    2. 其值按标准二进制转十进制计算:

      (0⋅24)+(0⋅23)+(0⋅22)+(1⋅21)+(1⋅20)=0+0+0+2+1=3

    3. 因此,结果为 3

样例 2

  • 输入: "11111111111111111111111111111111"

  • 输出: -1

  • 解释:

    1. 这是一个长度为 32 的二进制字符串,其最高位(最左边的位)是 1
    2. 根据二进制补码规则,这表示一个负数。
    3. 在32位有符号整数的补码表示法中,全为 1 的二进制数 1111...1111 (共32个1) 正好代表十进制整数 -1
    4. 因此,结果为 -1

这个问题的关键点在于理解题目中提到的“二进制字符串表示的是整数的补码形式”。这意味着一个32位的二进制串,如果其最高位(第32位,从右数)是1,那么它代表一个负数。

解决这个问题的最简洁、最健壮的方法是利用Java的内置解析函数,但需要一个小技巧来正确处理32位负数的情况:

  1. 使用 Long.parseLong: 我们不直接使用 Integer.parseInt,因为它在解析一个32位且最高位为1的二进制串时,会认为这是一个超出int正数范围的大数,从而抛出异常。相反,我们使用 Long.parseLong,它可以无误地将这个32位二进制串解析成一个正的long值。
  2. 类型转换: 将上一步得到的 long 值强制转换为 int。在Java中,这种从长类型到短类型的转换(窄化转换)会截取低位。对于一个32位的二进制补码表示,这个截取操作恰好能得到其对应的有符号int值。例如,二进制的 1111...1111(32个1)被解析为 long 类型的 2^32 - 1,当它被强制转换为 int 时,其二进制表示不变,而这正是 int 类型中 -1 的补码表示。

这种方法巧妙地利用了Java的类型转换规则来完成补码的解释。

public class BinaryConverter {
    /**
     * 主方法,将一个表示二进制补码的字符串转换为其对应的十进制整数.
     *
     * @param binaryStr 一个仅包含 '0' 和 '1' 的二进制字符串
     * @return 转换后的十进制整数
     */
    public int binaryToDecimal(String binaryStr) {
        
        // --- 核心逻辑:利用 Long.parseLong 和类型转换为 int 来处理32位补码 ---

        // 步骤 1: 使用 Long.parseLong 将二进制字符串按基数2进行解析。
        // 我们选择解析为 long 类型而不是 int 类型,是为了避免处理32位负数时出现 NumberFormatException。
        // 例如,对于二进制 "11111111111111111111111111111111":
        // - 如果使用 Integer.parseInt(s, 2),它会尝试解析一个非常大的正数,这会超出 int 的最大值,从而抛出异常。
        // - 而 Long.parseLong(s, 2) 可以无误地将其解析为一个正的 long 值 (2^32 - 1)。
        long longValue = Long.parseLong(binaryStr, 2);

        // 步骤 2: 将解析出的 long 值强制转换为 int。
        // 这是本算法最巧妙的一步。在Java中,从 long 到 int 的窄化原始类型转换会丢弃高32位,只保留低32位。
        // 这个行为与计算机处理二进制补码的方式完全一致。
        // - 对于正数(如 "00011" -> 3L),转换后仍然是 3。
        // - 对于负数(如 "1111...1111" -> 4294967295L),其二进制的低32位与 int 类型中 -1 的补码表示完全相同,
        //   因此强制转换后会得到正确的结果 -1。
        return (int) longValue;
    }
}