• [随笔]Integer.parseInt转换二进制数为int整数异常


    查看下面的Java示例,首先将java中的负整数转换为二进制类型:

    1. int n = -255;
    2. String nb = Integer.toBinaryString(n);
    3. System.out.println(n + "二进制表示:" + nb);

    控制台输出一个32位的二进制串:

    -255二进制表示:11111111111111111111111100000001

    然后尝试将这个二进制串原封不动的转换回去:

    1. int pnb1 = Integer.parseInt(nb, 2);
    2. System.out.println(nb + "使用parseInt转换:" + pnb1);

    这时控制台报错,抛出NumberFormatException异常

    1. Exception in thread "main" java.lang.NumberFormatException: For input string: "11111111111111111111111100000001"
    2. at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
    3. at java.lang.Integer.parseInt(Integer.java:583)
    4. at com.itbo.test.Test0903.main(Test0903.java:14)

    查看Integer.parseInt()源码:

    1. public static int parseInt(String s, int radix)
    2. throws NumberFormatException
    3. {
    4. /*
    5. * WARNING: This method may be invoked early during VM initialization
    6. * before IntegerCache is initialized. Care must be taken to not use
    7. * the valueOf method.
    8. */
    9. if (s == null) {
    10. throw new NumberFormatException("null");
    11. }
    12. if (radix < Character.MIN_RADIX) {
    13. throw new NumberFormatException("radix " + radix +
    14. " less than Character.MIN_RADIX");
    15. }
    16. if (radix > Character.MAX_RADIX) {
    17. throw new NumberFormatException("radix " + radix +
    18. " greater than Character.MAX_RADIX");
    19. }
    20. int result = 0;
    21. boolean negative = false;
    22. int i = 0, len = s.length();
    23. int limit = -Integer.MAX_VALUE;
    24. int multmin;
    25. int digit;
    26. if (len > 0) {
    27. char firstChar = s.charAt(0);
    28. if (firstChar < '0') { // Possible leading "+" or "-"
    29. if (firstChar == '-') {
    30. negative = true;
    31. limit = Integer.MIN_VALUE;
    32. } else if (firstChar != '+')
    33. throw NumberFormatException.forInputString(s);
    34. if (len == 1) // Cannot have lone "+" or "-"
    35. throw NumberFormatException.forInputString(s);
    36. i++;
    37. }
    38. multmin = limit / radix;
    39. while (i < len) {
    40. // Accumulating negatively avoids surprises near MAX_VALUE
    41. digit = Character.digit(s.charAt(i++),radix);
    42. if (digit < 0) {
    43. throw NumberFormatException.forInputString(s);
    44. }
    45. if (result < multmin) {
    46. throw NumberFormatException.forInputString(s);
    47. }
    48. result *= radix;
    49. if (result < limit + digit) {
    50. throw NumberFormatException.forInputString(s);
    51. }
    52. result -= digit;
    53. }
    54. } else {
    55. throw NumberFormatException.forInputString(s);
    56. }
    57. return negative ? result : -result;
    58. }

    发现抛出异常的代码如下:

    1. if (result < multmin) {
    2. throw NumberFormatException.forInputString(s);
    3. }

    分析源码可知,对于Integer.parseInt()方法,它是按照符号的"+-"来确定转换后整数的正负,如果转换的参数没有符号则默认转换后的整数符号为正。需要注意的是Java中的数值类型都是有符号的,不存在无符号的数,它们的取值范围是固定的,不会随着硬件环境或操作系统的改变而改变。对于int整型来说取值范围固定为 [-2^31, 2^31-1] (-2,147,483,648 ~ 2,147,483,647)。

    在操作系统里面定义二进制数的最高位代表符号位,但在计算机系统里面二进制负数是按照其补码存储的,这里可以忽略符号位。例如32位的-255在计算机中二进制表示为11111111111111111111111100000001,其实际代表的无符号数为4,294,967,041(已超出int整型取值范围),二者互为模2^32的补数。

    回到parseInt()方法,对于参数-255的二进制串进行转换时正是按照其所代表的无符号数的数值进行转换的,由于没有提供+、-符号所以符号取正,但是它所代表的无符号数已经超出了int整型的取值范围,所以会抛出转换异常。

    为了解决这个问题我们可以使用取值范围更大的Long(长整型)64位二进制补码整数:

    1. long pnb2 = Long.parseLong(nb, 2);
    2. System.out.println(nb + "使用parseLong转换:" + pnb2);
    3. System.out.println("long型强制转换为int型: " + (int)pnb2);

    long长整型占8个字节,变量pnb2对应的二进制表示为:

    00000000 00000000 00000000 00000000 11111111 11111111 11111111 00000001

    由于int整型只占4个字节,在强制转换为int整型时只截取低32位字节变为:

    11111111 11111111 11111111 00000001

    11111111111111111111111100000001 是-255的补码,因此强制转换后的值为-255,控制台输出:

    1. -255二进制表示:11111111111111111111111100000001
    2. 11111111111111111111111100000001使用parseLong转换:4294967041
    3. long型强制转换为int型: -255

    对于Java 8,它引入了支持unsigned无符号数操作的新API:Integer.parseUnsignedInt() 

    1. int pnb3 = Integer.parseUnsignedInt(nb, 2);
    2. System.out.println(nb + "使用parseUnsignedInt转换:" + pnb3);

    控制台输出:

    1. -255二进制表示:11111111111111111111111100000001
    2. 11111111111111111111111100000001使用parseUnsignedInt转换:-255

    下面贴出Integer.parseUnsignedInt()的源码:

    1. public static int parseUnsignedInt(String s, int radix)
    2. throws NumberFormatException {
    3. if (s == null) {
    4. throw new NumberFormatException("null");
    5. }
    6. int len = s.length();
    7. if (len > 0) {
    8. char firstChar = s.charAt(0);
    9. if (firstChar == '-') {
    10. throw new
    11. NumberFormatException(String.format("Illegal leading minus sign " +
    12. "on unsigned string %s.", s));
    13. } else {
    14. if (len <= 5 || // Integer.MAX_VALUE in Character.MAX_RADIX is 6 digits
    15. (radix == 10 && len <= 9) ) { // Integer.MAX_VALUE in base 10 is 10 digits
    16. return parseInt(s, radix);
    17. } else {
    18. long ell = Long.parseLong(s, radix);
    19. if ((ell & 0xffff_ffff_0000_0000L) == 0) {
    20. return (int) ell;
    21. } else {
    22. throw new
    23. NumberFormatException(String.format("String value %s exceeds " +
    24. "range of unsigned int.", s));
    25. }
    26. }
    27. }
    28. } else {
    29. throw NumberFormatException.forInputString(s);
    30. }
    31. }

    可以发现parseUnsignedInt()方法中也是利用parseLong的结果强制转换为int整型作为return值。

    参考文章

  • 相关阅读:
    Paddle模型性能分析工具Profiler:定位瓶颈点、优化程序、提升性能
    旋转之后截取图像
    Java —— 多态
    带拐友们认识docker的数据管理
    一定能用到的简单但实用的五种按钮样式(HTML+CSS步骤详解,含详细注释)
    MATLAB算法实战应用案例精讲-【回归算法】逐步式回归(Stepwise Regression)(附MATLAB、Java、Python和R语言代码)
    Android UI 冻结处理方法
    神器 CodeWhisperer
    让人上瘾的新一代开发神器,彻底告别Controller、Service、Dao等方法
    LangChain 安全特性全解析与实践指南
  • 原文地址:https://blog.csdn.net/w47_csdn/article/details/126693683