如何在Java中将IEEE-754二进制表示的字符串转换为float或double?

4

由于所有Java浮点数,即floatsdoubles,都以位形式内部表示,因此我希望找到一种高效的算法,将表示该floatdouble的比特串转换为相应的浮点数 - 我找不到内置的库函数,所以只能自己编写。

长度为32的二进制字符串表示float,而长度为64的二进制字符串将转换为double。所有floats都可以无损精度地转换为doubles。空格会被忽略。

示例

  • "0 10000000 10010010000111111011011"成为3.141592作为float
  • "1 11111111 00000000000000000000000"成为-infinity
  • "0 11111111 10010010000111111011011"成为一个floatNaN
  • "1 10000000000 0101101111110000101010001011000101000101011101101001"
    成为最接近-edouble值,即2.71828182845904509079559829843

到目前为止,我有这么多的代码:

public static double ieee(String binString) throws Exception {
    binString = binString.replace(" ", "");
    if (binString.length() == 32) {
        String exponentB = binString.substring(1, 9);
        String mantissaB = binString.substring(9, 32);
        int sgn = binString.charAt(0) == '0' ? 1 : -1;
        int exponent = Integer.parseInt(exponentB, 2) - 127; // Biased by 127
        double mantissa = 1 + Integer.parseInt(mantissaB, 2) / Math.pow(2, 23);

        if (exponent == 128 && mantissa == 1)
            return sgn == 1 ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY;
        if (exponent == 128 && mantissa != 0)
            return Double.NaN;
        if (exponent == -127)
            return sgn*Math.pow(2,-126)*(mantissa - 1);
        return sgn*Math.pow(2, exponent)*mantissa;
    }
    else if (binString.length() == 64) {
        String exponentB = binString.substring(1, 12);
        String mantissaB = binString.substring(12, 64);
        int sgn = binString.charAt(0) == '0' ? 1 : -1;
        int exponent = Integer.parseInt(exponentB, 2) - 1023; // Biased by 1023
        double mantissa = 1 + Long.parseLong(mantissaB, 2) / Math.pow(2, 52);

        if (exponent == 1024 && mantissa == 1)
            return sgn == 1 ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY;
        if (exponent == 1024 && mantissa != 0)
            return Double.NaN;
        if (exponent == -1023)
            return sgn*Math.pow(2,-1022)*(mantissa - 1);
        return sgn*Math.pow(2, exponent)*mantissa;
    }
    else {
        throw new Exception("Does not represent internal bits of a floating-point number");
    }
}

尽管我的代码目前能够正常工作,但在速度和代码量方面,将IEEE-754二进制表示字符串转换为其对应的floatdouble的最简洁或最快速的方法是什么?最有效的方法以及其效率和专业知识的良好解释是首选。


1
这个问题不是关于将“二进制”小数转换为浮点数的问题。 - Ṃųỻịgǻňạcểơửṩ
如果你想改进已经运作的代码,你可能应该尝试 https://codereview.stackexchange.com/ - VeeArr
@StephenC,对于双精度浮点数的情况怎么样?简化后的代码更有效吗? - Ṃųỻịgǻňạcểơửṩ
3个回答

6

这种方法可能更有效率。它肯定更简单和易于维护。

  1. 从字符串中删除空格
  2. 验证字符串长度...
  3. 将二进制字符串转换为int
  4. 调用Float.intBitsToFloat(int)将其转换为float

对于双精度浮点数,请使用long和等效的Double方法。


简化后的代码更有效吗?

唯一确定的方法是进行基准测试。但根据您的代码所做的事情,我认为是这样的。


在进行基准测试时,你的方法一定更快吗?我的“看起来效率低下”的算法似乎是一种“更加天真”的方法,用于转换IEEE754浮点表示。 - Ṃųỻịgǻňạcểơửṩ
2
  1. 随意编写自己的基准测试 :-)
  2. 是的。我的方法避免了所有这些混乱的东西。在底层,intBitsToFloat 将是一个简单的赋值。
- Stephen C

1

来自@StephenC的改进代码,将IEEE-754二进制表示转换为相应的浮点值只需要一行:

return Float.intBitsToFloat(Integer.parseUnsignedInt(binString, 2));
  • Integer.parseUnsignedInt(binString, 2)将二进制表示范围在0到232-1之间的无符号整数转换为int类型。使用parseInt(...)不能实现此功能,因为parseInt需要在其binString中显式指定符号,如果它代表一个负整数,则需要使用前导连字符而不是大于或等于231的值。同样,Long.parseUnsignedLong(binString, 2)适用于64位的情况。

  • Float.intBitsToFloat(int n)表示具有与n相同存储位的int值的浮点值。类似地,Double.longBitsToDouble(long n)适用于64位的情况。

  • 使用“方法组合”,此行首先将(无符号)二进制字符串转换为对应的整数,然后将其转换为具有相同存储位的浮点值。

最终代码如下:

public static double ieeeToFloat(String binString) throws Exception {
    binString = binString.replace(" ", "");
    /* 32-bit */
    if (binString.length() == 32) {
        return Float.intBitsToFloat(Integer.parseUnsignedInt(binString, 2));
    }
    /* 64-bit */
    else if (binString.length() == 64) {
        return Double.longBitsToDouble(Long.parseUnsignedLong(binString, 2));
    }
    /* An exception thrown for mismatched strings */
    else {
        throw new Exception("Does not represent internal bits of a floating-point number");
    }
}

-1
public class IEEE754ToFloatingValues {

public static double convertToInt(String mantissa_str) {
    
    int power_count = -1;
    double mantissa_int = 0.0;
    for (int i = 0; i < mantissa_str.length(); i++) {
        // System.out.print(Integer.parseInt(mantissa_str.charAt(i) + ""));
        mantissa_int += (Integer.parseInt(mantissa_str.charAt(i) + "") * Math.pow(2, power_count));
        power_count -= 1;
    }
    return mantissa_int + 1.0;
}

public static String convertToBinary(int i) {
    
    return Integer.toBinaryString(i + 0b10000).substring(1);
}

public static String decimalToHex(String decimal) {

    int i = Integer.parseInt(decimal);
    System.out.println("<<>>" + i);
    String my_hexdata = Integer.toHexString(i);

    System.out.println(my_hexdata);

    return String.valueOf(ReturnFloatingValue(my_hexdata));

}

public static double ReturnFloatingValue(String my_hexdata) {

    String myString = "";
    if (my_hexdata == null) {
        return -2.0;
    }
    if (my_hexdata.length() != 8) {
        myString = String.format("%1$-" + 8 + "s", my_hexdata).replace(' ', '0');

        System.out.println("My hex data after appending 0's is : " + myString);
    }

    String binary = "";
    for (int i = 0; i < myString.length(); i++) {
        int num = Integer.parseInt(myString.charAt(i) + "", 16);
        binary += convertToBinary(num);
    }

    System.out.println("Binary length is :  " + binary.length());

    System.out.println("Binary number is : " + binary);
    if (binary == null || binary.isEmpty()) {
        return -3.0;
    }
    String ieee_32 = binary.substring(2);

    ieee_32 = String.format("%1$-32s", binary).replace(' ', '0');

    long sign_bit = Long.parseLong(new StringBuilder().append(ieee_32.charAt(0)).toString());

    long exponent_bias = Long.parseLong(ieee_32.substring(1, 9), 2);
    long exponent_unbias = exponent_bias - 127L;

    System.out.println("Exponent unbias is  : " + exponent_unbias);
    String mantissa_str = ieee_32.substring(9);
    double mantissa_int = convertToInt(mantissa_str);
    double real_no = Math.pow(-1.0, (double) sign_bit) * mantissa_int * Math.pow(2.0, (double) exponent_unbias);

    System.out.println("Real no is : " + real_no);
    return real_no;
}

public static void main(String[] args) {

    //decimalToHex("16547");
}

}

我们欣赏代码,但不仅仅是代码的答案。请解释您的逻辑/方法。 - Kitswas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接