首页 > 解决方案 > 如果 Java 的 parseInt 对于有效的 IEEE-754 二进制文件失败,那么哪些 API 不会?

问题描述

还有另一个类似的帖子,但它不是重复的,所以请不要这样标记。

我有一个通信模拟,它获取测试数据——浮点数和整数——并将它们转换为二进制字符串,然后通过管道发送它们,并由另一端的侦听器接收。然后将它们“解包”并转换回数字。

但是,使用 Integer.parseInt() 进行的转换会引发异常,即使消息中的 Binary 显然是正确形成的 IEEE-754 binary32 单精度格式。

    float flt = -45732.9287f;
    int intFloat = Float.floatToRawIntBits(flt);
    String str = Integer.toBinaryString(intFloat);

    System.out.println("------- Now do the Reverse -------");

    int revint = Integer.parseInt(str, 2);

    .... Throws java.lang.NumberFormatException: For input string: "11000111001100101010010011101110"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)

使用没有 Radix 的 parseInt() 会引发类似的异常:

    "int revint = Integer.parseInt(str);" throws an exception too, "java.lang.NumberFormatException: For input string: "11000111001100101010010011101110"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)".

因此,这不仅仅是 Radix 使用问题。

使用 Long 和 BigInt 转换回来是不正确的,因为它们是 64 位 IEEE-754 格式,适用于带符号的双精度数据类型,而不是浮点数等带符号的单精度数据类型。

我有一个解决方案可以消除 Java 转换方法中的不对称性,但没有原生 API 让我觉得不协调:

    System.out.println("------- Now do the Reverse -------");

    int revSign = str.substring(0,1).equals("1") ? -1: 1;
    System.out.println("The passed-in sign is:"+revSign);

    int revint = Integer.valueOf(str.substring(1), 2);

    float revFloat = Float.intBitsToFloat(revint)*revSign;

我肯定错过了什么。

标签: javafloating-pointbinaryieee-754signed

解决方案


"11000111001100101010010011101110"是一个从 1 开始的 32 位二进制数。这意味着它可以表示int2 的补码中的负数。

但是,Integer.parseInt不要期望它的输入是 2 的补码表示。它希望符号用前导表示-,而不是用符号位表示,正如您可以在 Javadoc 中阅读的那样:

int java.lang.Integer.parseInt(String s, int radix) 抛出 NumberFormatException

将字符串参数解析为第二个参数指定的基数中的有符号整数。字符串中的字符必须都是指定基数的数字(取决于 java.lang.Character.digit(char, int) 是否返回非负值),除了第一个字符可能是 ASCII 减号 '-' ('\u005Cu002D') 表示负值或 ASCII 加号 '+' ('\u005Cu002B') 表示正值。返回结果整数值。

因此,它被视为"11000111001100101010010011101110"32 位正数,超出了该int类型的范围。

以下可用于重新计算 的值intFloat

// replace the leading 1 with - if it's a sign bit
str = (str.length () == 32 && str.charAt (0) == '1') ? ('-' + str.substring (1)) : str;
int revint = Integer.parseInt(str, 2);
// for negative number, we must compute the 2's complement of the number returned
// by parseInt
if (revint < 0) {
  revint = -1 * (revint + Integer.MAX_VALUE + 1);
}

现在两者intFloatrevint将等于-952982290

编辑:

Java 确实有你需要的 API,只是你没有使用正确的方法。由于toBinaryString(int i)返回“整数参数的字符串表示形式为基数为2 的无符号整数”,您应该使用Integer.parseUnsignedInt(String, int)它来恢复传递给的值toBinaryString

float flt = -45732.9287f;
int intFloat = Float.floatToRawIntBits(flt);
String str = Integer.toBinaryString(intFloat);
System.out.println("------- Now do the Reverse -------");
System.out.println (str);
int revint = Integer.parseUnsignedInt(str, 2);

推荐阅读