首页 文章

如果Java 's parseInt fails for valid IEEE-754 Binary, what API doesn' t?

提问于
浏览
2

还有另外一个thread与此相似,但它并不重复,所以请不要将其标记为此类 .

我有一个通信模拟,它接受测试数据 - 浮点数和整数 - 并将它们转换为二进制字符串,然后通过管道发送它们,并由另一端的监听器接收它们 . 然后他们'解包'并转换回数字 .

但是,使用Integer.parseInt()返回转换会抛出异常,即使消息中的二进制文件显然是正确形成的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格式,适用于Signed Double,而不是像Float这样的单一精度数据类型 .

我有一个解决方案,在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;

我肯定错过了什么 .

3 回答

  • 3

    "11000111001100101010010011101110" 是一个以1开头的32位二进制数 . 这意味着它可以表示2的补码中的负 int .

    但是, Integer.parseInt 不是补充表示 . 它期望标志用前导 - 表示,而不是用符号位表示,因为你可以在Javadoc中读到:

    int java.lang.Integer.parseInt(String s,int radix)throws 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的 unsigned 整数”,因此应使用 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);
    
  • 1

    我一定错过了什么 .

    你错过了 Integer.toBinaryString(integer) 的逆变换是 Integer.parseUnsignedInt(string, 2) ...而不是 Integer.parseInt(string) .

    至于为什么他们以这种方式设计API:这对设计师来说是一个问题 .

    但请记住, Integer API已经发展了很多年,并且进化的主要要求是变化应该向后兼容 . (他们可以在过去的20年里为客户付费 . )

    值得注意的是, parseUnsignedInt 仅在Java 1.8中添加 .

  • 0

    我的问题的答案似乎是,一个看似与其功能名称相反的方法,将完成这项工作 . 使用Signed Float和“Integer.parseUnsignedInt”的工作示例位于底部 .

    不幸的是,"Don't trust the JavaDoc"似乎更贴切'answer' . 如果"Integer.parseUnsignedInt(String, Radix)"用于转换浮点数的IEEE-754有符号二进制表示,那么它需要更好的名称和更好的JavaDoc .

    我感谢所有参与此次讨论的人 . 对于读者来说,JavaDoc通常不会告诉您:此方法是否在内部切断Sign-bit并将其余的String解析为UnsignedInt,然后应用符号位?这就是为什么有人将它命名为“parseUnsignedInt()”,尽管事实上它解析了签名值?也许 . 我们可以说的是,尽管方法名称,但它清楚地解析了负浮点值的已签名Int表示 .

    对于's trying this, the example below will generate an IEEE-754 32-bit Signed Binary String from a Float, conforming to the spec'的“binary32”格式的下一个人,并将其转换回您开始使用的相同的Signed Binary Float .

    public static void generatingFloatNumbers() {
        float flt = -45732.9287f;
        System.out.println("We start with:"+flt);
        int intFloat = Float.floatToRawIntBits(flt);
    
        String str = Integer.toBinaryString(intFloat);
        System.out.println("The binary-str for the int is: " + str);
        System.out.println("The binary-str length is: " + str.length() + " chars long.");
    
        System.out.println("------- Now do the Reverse -------");
    
        int revint = Integer.parseUnsignedInt(str, 2);
        float revFloat = Float.intBitsToFloat(revint);
    
        System.out.println("The Reverse Float is: "+revFloat);
    }
    

    输出:

    We start with:-45732.93
    The binary-str for the int is: 11000111001100101010010011101110
    The binary-str length is: 32 chars long.
    ------- Now do the Reverse -------
    The Reverse Float is: -45732.93
    

    进程以退出代码0结束

    这是一个有用的online validation工具,因此您可以使用它的IEEE格式可视化二进制文件并查看转换 .

    请注意,如果您从Double-to-Long-to-String开始,并使用Long.parseUnsignedLong进行转换以交换符合IEEE-754标准的“binary64 " format between processes in Binary, except that the output is in Scientific " x.yEn”符号,则情况也是如此 . 尝试打印它:

    public static void generatingDoubleNumbers2() {
        double dbl = -45732.9287f;
        System.out.println("We start with:"+dbl);
        long longFloat = Double.doubleToRawLongBits(dbl);
    
        String str = Long.toBinaryString(longFloat);
        System.out.println("The binary-str for the long is: " + str);
        System.out.println("The binary-str length is: " + str.length() + " chars long.");
    
        System.out.println("------- Now do the Reverse -------");
    
        double revDbl = Long.parseUnsignedLong(str, 2);
    
        System.out.println("The Reverse Float is: "+revDbl);
    }
    

    输出:

    We start with:-45732.9296875
    The binary-str for the long is: 1100000011100110010101001001110111000000000000000000000000000000
    The binary-str length is: 64 chars long.
    ------- Now do the Reverse -------
    The Reverse Float is: -4.5468537372761129E18
    
    Process finished with exit code 0
    

相关问题