9

It's fairly simple to convert a double to a hexadecimal string in Java. But how do I do the reverse? My code is below and I've noted where a NumberFormatException is thrown (about 2/3rds down).

public class HexToDoubleTest {
    public static void main( String args[] ) {

        // This is the starting double value
        double doubleInput = -9.156013e-002;

        // Convert the starting value to the equivalent value in a long
        long doubleAsLong = Double.doubleToRawLongBits( doubleInput );

        // Convert the long to a String
        String doubleAsString = Long.toHexString( doubleAsLong );

        // Print the String
        System.out.println( doubleAsString );

        // Attempt to parse the string back as a long
        // !!! This fails with a NumberFormatException !!!
        long doubleAsLongReverse = Long.parseLong( doubleAsString, 16 );

        // Convert the long back into the original double
        double doubleOutput = Double.longBitsToDouble( doubleAsLongReverse );

        // Confirm that the values match
        assert( doubleInput == doubleOutput );

    }
}

Using Double.valueOf fails in the same manner.

Edit: I've done a few searches on the web already and found some very inelegant solutions. For example: Using a BigInteger seems like overkill. There's got to be a better way!

Andy
  • 5,108
  • 3
  • 26
  • 37

5 Answers5

9

Why not use the methods provided in standard library: Double.valueOf and Double.toHexString

So a full round trip example would be

public static void main(String[] args){

    double doubleValue = -0.03454568;
    System.out.println("Initial double value is " + doubleValue);

    String hexStringRepresentation = Double.toHexString(doubleValue);
    System.out.println("Hex value is " + hexStringRepresentation);

    double roundtrippedDoubleValue = Double.valueOf(hexStringRepresentation);
    System.out.println("Round tripped double value is " + roundtrippedDoubleValue);
}

Nb Double.valueOf will give a boxed Double and Double.parseDouble will give a primitive double choose as appropriate.

or am I misunderstanding something?

Richard Tingle
  • 16,906
  • 5
  • 52
  • 77
posdef
  • 6,498
  • 11
  • 46
  • 94
  • This fails in the same exact manner: `NumberFormatException`. – Andy Mar 25 '11 at 15:14
  • 2
    @Andy: just to make sure, could you specify: a) the output of `String str = Double.toHexString(-9.156013e-002)` and b) `Double d = Double.valueOf(str)` – posdef Mar 25 '11 at 15:25
  • 1
    This seems to work for me, for very small numbers, very large number, negative and positive numbers. I think @Andy may not be reading exactly what you mean so I've made an edit to add examples of usage – Richard Tingle May 07 '18 at 11:41
3

The problem is that the input value is negative, and the javadoc for Long.toHexString() states "Returns a string representation of the long argument as an unsigned integer in base 16. The unsigned long value is the argument plus 2^64 if the argument is negative; otherwise, it is equal to the argument." However parseLong states "Parses the string argument as a signed long in the radix..."

So when you have a negative input, that 2^64 causes the NumberFormatException.

If the input is changed to

double doubleInput = 9.156013e-002;

the conversion works correctly without an exception. To deal with a negative input, a little additional processing is needed.

Here is a class that shows one way to do the conversion without using BigInteger or byte buffers:

public class Temp {
  public String getDoubleAsHexString(double input) {
    // Convert the starting value to the equivalent value in a long
    long doubleAsLong = Double.doubleToRawLongBits(input);
    // and then convert the long to a hex string
    return Long.toHexString(doubleAsLong);
  }

  public double convertHexStrToDouble(String input) {
    // convert the input to positive, as needed
    String s2 = preprocess(input);
    boolean negative = true;
    // if the original equals the new string, then it is not negative
    if (input.equalsIgnoreCase(s2))
      negative = false;

    // convert the hex string to long
    long doubleAsLongReverse = Long.parseLong(s2, 16);

    // Convert the long back into the original double
    double doubleOutput = Double.longBitsToDouble(doubleAsLongReverse);

    // return as a negative value, as needed
    if (negative)
      return -doubleOutput;

    return doubleOutput;
  }

  private String preprocess(String doubleAsHexString) {
    // get the first char and convert it to an int
    String s0 = doubleAsHexString.substring(0, 1);
    int int1 = Integer.parseInt(s0, 16);

    // if the int is < 8, then the string is not negative
    // and is returned without further processing
    if (int1 < 8)
      return doubleAsHexString;

    // otherwise subtract 8
    int1 = int1 - 8;
    s0 = Integer.toString(int1);

    // don't prepend a "0"
    if (int1 == 0)
      s0 = "";

    // return the string with a new inital char
    return s0 + doubleAsHexString.substring(1);
  }
}

And here is a junit test class:

public class TempTest {
  private Temp t;

  @Before
  public void setUp() throws Exception {
    t = new Temp();
  }

  @Test
  public void testConvertHexStrToNegativeDouble() {
      double doubleInput = -9.156013e-002;
      String hexStr = t.getDoubleAsHexString(doubleInput);
      double doubleOutput = t.convertHexStrToDouble(hexStr);
      assertEquals(doubleInput, doubleOutput, 0.0);
    }

  @Test
  public void testConvertHexStrToPositiveDouble() {
    double doubleInput = 9.156013e-002;
    String hexStr = t.getDoubleAsHexString(doubleInput);
    double doubleOutput = t.convertHexStrToDouble(hexStr);
    assertEquals(doubleInput, doubleOutput, 0.0);
  }
}
muki61
  • 41
  • 2
2

You can break the String into two halves and parse each one, but I think this is the simplest.

long doubleAsLongReverse = new BigInteger(doubleAsString, 16).longValue();

In Java 8, there is now a

long l = Long.parseUnsignedLong(doubleAsString, 16);

and to reverse this

String s = Long.toUnsignedString(l, 16);

These can be used in combination with the methods converting raw double to long etc.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
0

Example function to reverse double value:

public Double ReverseDouble( Double d) {

   byte[] bytes = new byte[8]; 
   ByteBuffer.wrap( bytes).putDouble( d); 

   for (int i=0;i<bytes.length/2;i++) {
       byte b = bytes[ i];
       bytes[ i] = bytes[ bytes.length -i -1];
       bytes[ bytes.length -i -1] = b;
   }

   return ByteBuffer.wrap( bytes).getDouble();
}
ShelS
  • 177
  • 1
  • 2
0

Conversion of HexString to Long.

public long ConvertHexToLong(String hexValue){
        return  new BigInteger(hexValue, 16).longValue();
     }
vinay shetty
  • 895
  • 1
  • 9
  • 15