tags:

views:

841

answers:

6

Hi

I am trying to convert scientific double to decimal double in java. I am sending a value from server (coded on C++) which is running on X86 (little endian) machine and I was using htonl, ntohl methods for convertion before sending data to client (coded on java). But now, I have to sent this value with no conversion like LE to BE. The coversion is being done on client (java) side. Other type can be converted correctly but for double, this is not present. When client receive a double type, this value can not be read correctly. Here is my java code for double conversion.

protected int readInt(InputStream stream) throws IOException {
    int i0 = read(stream);
    int i1 = read(stream);
    int i2 = read(stream);
    int i3 = read(stream);
     int i=(i0 << 24) + (i1 << 16) + (i2 << 8) + (i3 << 0);
    return i;
}
protected double readDouble(InputStream stream) throws IOException {
    int i0 = readInt(stream);
    int i1 = readInt(stream);
    return Double.longBitsToDouble(((long)i0 << 32) + (i1 & 0xffffffffL));      
}

After all of these steps, I got 9.534475227E-315 if I sent 0.3 from server.
Thanks and regards.

+2  A: 

It sounds like you are reading the value wrong from the client, but in any case, NumberFormat is your friend :) http://java.sun.com/j2se/1.5.0/docs/api/index.html?java/text/NumberFormat.html

EDIT: considering the code sample that you posted, I have to agree with @trashgod that your conversion code is flawed. Perhap DataInputStream can assist -> http://java.sun.com/javase/6/docs/api/java/io/DataInputStream.html

Yoni
The `order()` method of `java.nio.ByteBuffer` can be beneficial in this context, too. http://java.sun.com/javase/6/docs/api/java/nio/ByteBuffer.html
trashgod
A: 

Hi

If you sent 0.3 from server to client and got back 9.534475227E-315 the last thing you ought to do is convert that value to 0.3 again. The returned value is very close to 0 in f-p terms, and indicates some error in the sending and returning process.

I am puzzled at your question, I did not know that Java implemented a Decimal class but then my Java knowledge is old and scant. Do you perhaps mean a BigDecimal ? Or are you converting string formats of floating-point numbers, which would be a completely different kettle of fish ?

Regards

Mark

High Performance Mark
+1  A: 

Your conversion is flawed in several ways, including the use of signed arithmetic and incorrect bit values. You might study the format and look at the approach shown in this glossary entry.

trashgod
thanks trashgod. your link was realy helpfull for me. I solved the problem. I will write my solution to here.
Aykut
+1  A: 

Your use of the term "scientific notation" sorta suggests you are dealing with text data that looks like "3.0e-1". But I think I understand.

It seems like the problem is just reading binary data written in a non-java word order. Yet why are the integers written big-endian, yet the doubles written little-endian? And why are you reading in such a strange manner? Doesn't your compiler complain about the 'int'? It may have been hiding a problem. (EDIT: my error - I was stuck on 64 bits for the double)

It would be useful for everyone to see a hex dump of your data. Are the bytes flipped, or just the words?

Maybe this code will provide some inspiration. Please excuse the 'get-r-done' use of variables.

// convert CLI argument to double, write to file, and read back
// without args, default is "0.3"
// should try negative numbers!

import java.io.*;

public class BinaryFile {

    public static void main(String[] args) {

        String strFilePath = "WordFlippedDouble";
        boolean WRITEOP = true;
        double d, dd = 0.3;
        long ddFlip;

        if(args.length > 0) {
            dd = Double.valueOf(args[0]);
        }
        System.out.println("Starting with " + dd + " looks like " + 
            Long.toHexString(Double.doubleToLongBits(dd)));

        if(WRITEOP) {
            ddFlip = Double.doubleToLongBits(dd);
            ddFlip = (ddFlip<<32) | ((ddFlip>>32) & 0xFFFFFFFFL);
            System.out.println("WRITE: (flipped) looks like   " + Long.toHexString(ddFlip));
            try {
                FileOutputStream fout = new FileOutputStream(strFilePath);
                DataOutputStream dout = new DataOutputStream(fout);
                dout.writeLong(ddFlip);
                dout.close();
            } catch (Exception e) {
                System.out.println("ERROR: " + e.getMessage());
            }
        }

        if(false) return;                // testing

        try {
            FileInputStream fin = new FileInputStream(strFilePath);
            DataInputStream din = new DataInputStream(fin);
            ddFlip = din.readLong();
            d = Double.longBitsToDouble((ddFlip<<32) | ((ddFlip>>32) & 0xFFFFFFFFL));                                                     
            System.out.println("READ:  " + Long.toHexString(ddFlip) + " converts to " + 
                d + " DIFF: " + (dd-d));
            din.close();
        } catch(FileNotFoundException e) { 
            System.out.println("FileNotFoundException : " + e);
        }
        catch(IOException e) {
            System.out.println("IOException : " + e);
        }
    }
}
gary
Hmmm, I made an interesting slip-up, and duplicated it. It just happened not to make a difference due to the rules for promotion...
gary
let me explain why i have to do something like this.we are coding on solaris sparc os in c++ for server and client side of our program. but the client side has been coded with java in last mounths. Then we got a new request about supporting x86(including linux based and solaris x86 OSs) platforms. Then I started to port server side from solaris to linux and solaris sparc to solaris x86. When server side running on x86 machines we encuntered the endian issues.so, server side has 500000 lines of code and we decided to convert numerical datas according to system architecture on client(java) side.
Aykut
Your original question stated you were sending the data without conversion. Are all your servers sending the same format? A (short) data dump, with hex and numeric value, would help a lot. I can see no way to send 0.3 (0x3fd3333333333333) and get 9.534475227E-315 (0x0000000073066666) without some serious mix-up. I believe my program should help show the method of conversion, once you know what your are converting. I cleaned it up to remove some confusion.
gary
+1  A: 

Don't roll your own bit twiddling like that. It's in the standard API. See java.nio.ByteBuffer.

protected int readInt(InputStream stream) throws IOException {
    ByteBuffer buffer = ByteBuffer.allocate(Integer.SIZE / Byte.SIZE);
    //buffer.order(ByteOrder.LITTLE_ENDIAN); // optional
    stream.read(buffer.array());
    return buffer.getInt(0);
}
protected double readDouble(InputStream stream) throws IOException {
    ByteBuffer buffer = ByteBuffer.allocate(Double.SIZE / Byte.SIZE);
    //buffer.order(ByteOrder.LITTLE_ENDIAN); // optional
    stream.read(buffer.array());
    return buffer.getDouble(0);
}
finnw
A: 

Hi,

I solved the my question and thank to you all for your helps. Much appreciated. So here is the solution.

  protected double readDouble(InputStream stream) throws IOException {

    return Double.longBitsToDouble(((long) read(stream) & 0xff) 
                                 | ((long) (read(stream) & 0xff) << 8)
                                 | ((long) (read(stream) & 0xff) << 16)
                                 | ((long) (read(stream) & 0xff) << 24)
                                 | ((long) (read(stream) & 0xff) << 32)
                                 | ((long) (read(stream) & 0xff) << 40)
                                 | ((long) (read(stream) & 0xff) << 48)                     
                                 | ((long) (read(stream) & 0xff) << 56));  }

When I send a data from server in little endian format, I can convert incoming little endian formatted value to big endian formatted value with this code in java.

Aykut