views:

288

answers:

5

I was recently asked to take over a project in which waveform data is sampled from a power converter and sent to an intelligent agent where calculations are done and the appropriate actions are taken based on the results.

The following (java)snippet is what the previous coder used to convert the byte array into an array of doubles:

        LSB = (0XFF & (int)ipPacket[index]);
        double temp = LSB/256.0;

        MSB = (byte)(0XFF & (byte)ipPacket[index+1]);
        double output = (double)(MSB + temp);

        ConvCurrentPhaseR[doubleArrayIndex] = (double)(output);

(Note: LSB is an integer and MSB is a byte)

I honestly have no idea how the previous coder came up with this conversion. It doesn't make any sense to me. Why divide LSB by 256? Even if the original data is a float (4 bytes) how are we getting a double (8 bytes) out of only 2 bytes? Could anyone possibly explain what is going on with this piece of code?

Thanks in advance for any help provided!

A: 

The key isn't that he is dividing by 256, but by 256.0. An arithmetic operation with an integer operand and a double/float operand will yield a double/float result. If both operands were integers, then the result would be an integer.

matt b
+1  A: 

From a quick read-over, it looks to me like the result is the same as:

output = (double)(ipPacket[index + 1] + (ipPacket[index] / 256.0));

This is assuming ipPacket is a byte array, and (0xFF & byte) == byte. He's effectively storing the decimal part of the double in ipPacket[index] as a multiple of 1/256, and storing the integer part in ipPacket[index+1].

This has a much smaller range than a regular double, which may be what you're confusing.

The effective range of values, assuming unsigned bytes, is a double from 0 to 255.99609375, with a limited range of represented decimal values.

Edit: The value 256 is not inclusive in this, since the unsigned byte would range from 0-255, the decimal area would range from 0 - (255/256 == .99609375)

Will Eddins
A: 

My guess is that the first byte contains the part after the decimal point as "x/256" and the second byte contains the part before the decimal point.

So, for 4.5, the first byte would be 128 (128/256 = 1/2 = 0.5), the second byte would be 4, and the result is the two added together.

Sean
A: 

Note to Guard's answer:

(0xFF & byte) == byte

This isn't true for Java, you would promote the sign bit into the resulting int or float value. The original code:

(0XFF & (int)ipPacket[index]);

strips off the sign bit, while:

(byte)(0XFF & (byte)ipPacket[index+1]);

explicitly keeps it so it is included in the resulting float. The resulting value of the conversion would be -127 to 127 plus 0 to 255/256.

rsp
A: 

Here is my guess into the fray... in some cases restating or consolidating what others have said but in context.

The source data is 16 bit fixed point format. This has a smaller range than float or double but has more precision than 16 bit floating point would have had. Here, 8 signed bits are given to the whole number portion and 8 bits are given to the decimal portion.

For those unfamiliar with fixed point representation, this is similar to keeping monetary values as integer "cents" while dividing by 100 to present display "dollars".

    LSB = (0XFF & (int)ipPacket[index]);
    double temp = LSB/256.0;

In the above, the first byte is turned into the decimal portion of the value using floating point division. LSB/256.0 will produce a double value between 0.0 and 0.99609375.

    MSB = (byte)(0XFF & (byte)ipPacket[index+1]);

The above code is supposed to be normalizing the second byte. If ipPacket is a byte[] array then this isn't doing anything as the sign bit was fine already. ipPacket[index+1] would have already been between -128 and 127. The code only makes sense if ipPacket is a non-byte array and even then it's a bit overzealous in its casting since the & 0XFF is going to promote the value to an integer anyway... right before casting it back to byte.

Given the unnecessary (double) cast in the last line of the original code, I'm going to suspect some of the above may be unneeded also.

If ipPacket is a byte[] array then:

MSB = ipPacket[index+1];

Should be fine. If ipPacket is not a byte[] array then the following should still be sufficient:

MSB = (byte)(0xFF & ipPacket[index+1]);

As said by others, this last bit then combines the whole number and decimal portions:

    double output = (double)(MSB + temp);
    ConvCurrentPhaseR[doubleArrayIndex] = (double)(output);

Though it is important to note that the decimal portion is added to the whole number portion. This means that MSB = -1 and LSB = 128 would result in -0.5 and not -1.5 as one might initially expect.

PSpeed