5

I'm working on a low latency system and need a solution to convert a double number to byte array but with precision for decimal numbers. I need to consider there should be less object creation within the solution. Also, I'm getting value as double.

My current implementation is somewhat like

int offset = 0;
double d = 1211.11;
long integerPart = (long) d;
byte[] b = new byte[16];

offset += addToByteArray(integerPart, b, offset); 
//implementation to add each digit to byte array, returns new offset

int lengthDecimal = 16 - offset;
if(lengthDecimal > 0)
b[offset++] = '.';
long tmp = 1;

for(int i=0; i<lengthDecimal; i++){
 tmp = tmp * 10;
 int digit = (int)(((long) (d * tmp)) % 10);
 b[offset++] = (byte) ('0' + digit);
}

Then the byte array is manipulated to trim later zeros and '.' if required.

The problem is if I check the byte array, I don't find the exact digits. Since, I'm using double, the precision of decimal point is lost while working.

I've searched and found everywhere to use BigDecimal. The use of BigDecimal seems to work fine but I'm worried about the performance and number of objects created as this part of the code is hit frequently.

I've also tried to work with String like this

String doubleNumber = Double.toString(d);
long fractionalPart = 0;

offset += addToByteArray(integerPart, b, offset); 
if(doubleNumber.substring(doubleNumber.indexOf(".")).length() > 0) {
  fractionalPart = Long.valueOf(doubleNumber.substring(doubleNumber.indexOf(".") + 1));
  b[offset++] = '.';
  offset += addToByteArray(fractionalPart, b, offset);
}

Please suggest me the best approach for the purpose.

darkapple
  • 539
  • 1
  • 7
  • 19
  • 2
    "I'm worried about the performance .." <-- Don't be. Either 1) Be lazy/uninformed and don't worry; or 2) *Profile the application* and know if there is an issue (or not). –  Dec 28 '12 at 00:03
  • Do you understand the difference between a `byte` and a `char`? It looks to me like you simply want to format a floating-point number into a String, and there are a half-dozen different ways to do that. – Hot Licks Dec 28 '12 at 00:09
  • Do you want raw bytes, or text? – clstrfsck Dec 28 '12 at 00:09
  • @msandiford raw bytes to be sent on wire. – darkapple Dec 28 '12 at 00:10
  • possible duplicate of [How can I convert a byte array into a double and back?](http://stackoverflow.com/questions/2905556/how-can-i-convert-a-byte-array-into-a-double-and-back) (and many others) – Brian Roach Dec 28 '12 at 00:10
  • "Raw bytes" implies sending the binary vs character representation of the number. Those are two entirely different things. Maybe you should state your *requirement* vs your assumed implementation. – Hot Licks Dec 28 '12 at 00:12
  • @HotLicks it will be character(ASCII) representation of the numbers. I'm bit confused here on binary vs character – darkapple Dec 28 '12 at 00:16
  • If it's to be character representation then you don't want `byte`, you want `char` or `String`. It may be that the final data needs to be converted to `byte` to be transmitted, but that's a separate step, performed on the bulk data, and usually done by the network API code. (And if you're "sending on a wire" then the performance of the data conversion is inconsequential in comparison -- just use a built-in formatter.) – Hot Licks Dec 28 '12 at 00:19

3 Answers3

10
byte[] bytes = new byte[8];
java.nio.ByteBuffer.wrap(bytes).putDouble(yourDouble);
izilotti
  • 4,757
  • 1
  • 48
  • 55
4

If you really want low latency, then the important thing is to avoid heap memory allocations (which cause GC pauses at some unspecified later point).

So I'm assuming that:

  • You have allocated your byte array in advance
  • You want to put a double in your byte array without doing any intermediate allocations

The trick is to use doubleToLongBits:

long v = Double.doubleToLongBits(myDouble);

Then write the long into the correct position in your byte array (presumably using 8 consecutive bytes). You might as well just do this the brute force way:

data[pos+7] = (byte)(v);
data[pos+6] = (byte)(v>>>8);
data[pos+5] = (byte)(v>>>16);
data[pos+4] = (byte)(v>>>24);
data[pos+3] = (byte)(v>>>32);
data[pos+2] = (byte)(v>>>40);
data[pos+1] = (byte)(v>>>48);
data[pos]   = (byte)(v>>>56);

Note we are using primitives at every point, so there are no memory allocations throughout this process.

mikera
  • 105,238
  • 25
  • 256
  • 415
-2

If you want a double formatted into an ASCII (character) string then String.format is probably the simplest approach that gives you control over the format:

String result = String.formatter("%5.3f", theDoubleValue);
Hot Licks
  • 47,103
  • 17
  • 93
  • 151