Double to Hex String and Back
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?
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.
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);
}
}