3

Since all Java floating-point numbers, which are floats and doubles, are internally represented as bits, I want to find an efficient algorithm to convert a String representing the bits of that float or double and convert it into its corresponding floating-point number - I couldn't find a built-in library function for it so I resorted to writing it by myself.

A binary String of length 32 represents a float, where as a binary String of length 64 will be converted to a double. All floats can be converted to doubles without loss of accuracy. Spaces are ignored.

Examples

  • "0 10000000 10010010000111111011011" becomes 3.141592 as a float.
  • "1 11111111 00000000000000000000000" becomes -infinity.
  • "0 11111111 10010010000111111011011" becomes a float NaN.
  • "1 10000000000 0101101111110000101010001011000101000101011101101001"
    becomes a double value closest to -e, which is 2.71828182845904509079559829843

So far I have this mass of a code:

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");
    }
}

Though my code works as of now, what is the neatest or fastest method to convert a IEEE-754 binary representation String to its float or double, in terms of speed and amount of code? The most efficient method with a good explanation of its efficiency and expertise is preferred.

3 Answers3

6

This approach is probably more efficient. It is certainly simpler and more maintainable.

  1. Remove spaces from string
  2. Validate string length ...
  3. Convert binary string to int
  4. Call Float.intBitsToFloat(int) to convert to an float.

For doubles, use long and the equivalent Double method.


Is the simplified code more efficient?

The only way to be sure is to benchmark it. But based on what your code is doing, I believe so.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • When benchmarking, is the your method definitely faster? My "inefficient-looking" algorithm looks like a "more naive" approach to convert an ieee754 float representation. – Ṃųỻịgǻňạcểơửṩ Jul 16 '18 at 23:04
  • 2
    1) Feel free to write your own benchmark :-) 2) Yes. My approach avoids all of that messy stuff. Under the hood `intBitsToFloat` will be a simple assignment. – Stephen C Jul 16 '18 at 23:05
0

From @StephenC, the improved code to convert the IEEE-754 binary representation to its corresponding floating-point value takes up just one line:

return Float.intBitsToFloat(Integer.parseUnsignedInt(binString, 2));
  • Integer.parseUnsignedInt(binString, 2) converts the unsigned int in binary digits from range 0 to 232-1 to the int representation. The parseInt(...) does not work because parseInt involves an explicit sign in its binString, and if it represents a negative int, a leading hyphen is required instead of a value that is 231 or greater. Similarly, Long.parseUnsignedLong(binString, 2) appiles for the 64-bit case.

  • Float.intBitsToFloat(int n) represents the float value with the same bits internally stored as the int value n. Similarly, Double.longBitsToDouble(long n) works for the 64-bit case.

  • Using "method composition", this line first converts the (unsigned) binary string into its corresponding int, and then converts it into the floating-point value with the same stored bits.

The final code would be

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");
}

}
Procrastinator
  • 2,526
  • 30
  • 27
  • 36