From: Mikee on
Hi

I having trouble reading a binary file. The problem seems to occur
with reading some float values so I've made a small test file (from
SQL server using native file output) that contains just 5 floats.

-13.73482 , 20.689 , -99.99999 , 20.157 ,1 9.454

I read the file in and write out the values using

DataInputStream in = new DataInputStream(new
BufferedInputStream(new FileInputStream(filename)));
System.out.print(ByteSwapper.swap(in.readFloat())+" ");
System.out.print(ByteSwapper.swap(in.readFloat())+" ");
System.out.print(ByteSwapper.swap(in.readFloat())+" ");
System.out.print(ByteSwapper.swap(in.readFloat())+" ");
System.out.println(ByteSwapper.swap(in.readFloat())+" ");

The output is -13.734822 20.689 6.9055E-41 20.157 19.454

ie -99.99999 is now 6.9055E-41.

I'm trying to read in a much larger SQL outgest of mixed data types
integers, longs, doubles, floats and bytes and the only problem I've
spotted is floats of -99.9999 (which is a deafult value used for some
columns)

It was necessary to byte swap and ByteSwapper.swap(float) is something
I find on the web an dis below.
I tries other byte swap code with the same result.

Any ideas what I'm doing wrong with the 3rd value and presumably in
general.

Python code of

binaryFile = file('test.bin', 'rb')
while True:
rowBinary = binaryFile.read(20)
if not rowBinary:
break
print struct.unpack('<5f', rowBinary)
binaryFile.close()

correctly gives -13.734822273254395, 20.688999176025391,
-99.999992370605469, 20.156999588012695, 19.454000473022461

java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_12-b04

Thanks
Mike

public static float swap (float value)
{
int intValue = Float.floatToIntBits (value);
intValue = swap (intValue);
return Float.intBitsToFloat (intValue);
}

and

public static int swap (int value)
{
int b1 = (value >> 0) & 0xff;
int b2 = (value >> 8) & 0xff;
int b3 = (value >> 16) & 0xff;
int b4 = (value >> 24) & 0xff;

return b1 << 24 | b2 << 16 | b3 << 8 | b4 << 0;
}
From: Thomas Pornin on
According to Mikee <mikee.read(a)googlemail.com>:
> DataInputStream in = new DataInputStream(new
> BufferedInputStream(new FileInputStream(filename)));
> System.out.print(ByteSwapper.swap(in.readFloat())+" ");
> System.out.print(ByteSwapper.swap(in.readFloat())+" ");
> System.out.print(ByteSwapper.swap(in.readFloat())+" ");
> System.out.print(ByteSwapper.swap(in.readFloat())+" ");
> System.out.println(ByteSwapper.swap(in.readFloat())+" ");
>
[...]
> public static float swap (float value)
> {
> int intValue = Float.floatToIntBits (value);
> intValue = swap (intValue);
> return Float.intBitsToFloat (intValue);
> }

This is conceptually flawed. What you do, here, is the following:
1. read four bytes
2. interpret them as a float (in.readFloat())
3. convert them back to four bytes, as an "int" value
(Float.floatToIntBits())
4. byte-swap the bytes
5. finally reinterpret the bytes as a float (Float.intBitsToFloat())

Steps 2 and 3 basically cancel each other: step 3 undoes what step 2
did. The trouble is that this roundtrip (steps 2 and 3) may lose some
information, because some bit patterns encode a "NaN" (a special value
for floats) and Float.floatToIntBits() normalizes NaN values. This is
probably what happens to you: when read "byte-swapped", your -99.99999
becomes a pattern for a NaN, which gets normalized to another NaN,
thus changing the bit pattern.

You _might_ correct your code by using Float.float.toRawIntBits()
instead of Float.floatToIntBits(), but even if it works, it is
non-robust (it assumes that DataInputStream also does things that way).
A more correct way of doing things is to read the bytes as an "int"
directly, which basically removes steps 2 and 3 altogether. This would
look like this:

System.out.print(Float.intBitsToFloat(swap(in.readInt())) + " ");


--Thomas Pornin
From: Paul Cager on
On Jun 9, 11:14 am, Thomas Pornin <por...(a)bolet.org> wrote:
> According to Mikee  <mikee.r...(a)googlemail.com>:
>
> >            DataInputStream in = new DataInputStream(new
> > BufferedInputStream(new FileInputStream(filename)));
> >            System.out.print(ByteSwapper.swap(in.readFloat())+" ");
[...]
> You _might_ correct your code by using Float.float.toRawIntBits()
> instead of Float.floatToIntBits(), but even if it works, it is
> non-robust (it assumes that DataInputStream also does things that way).
> A more correct way of doing things is to read the bytes as an "int"
> directly, which basically removes steps 2 and 3 altogether. This would
> look like this:
>
>     System.out.print(Float.intBitsToFloat(swap(in.readInt())) + " ");

I haven't checked the API (http://java.sun.com/javase/6/docs/api/
index.html?java/nio/package-summary.html) but I seem to remember that
NIO allows you to read floats etc in the byte order of your choice.
That might avoid the need to do all the bit twiddling yourself.
From: Mikee on
Hi Thomas

Thanks for poitning out where I was going wrong.

>     System.out.print(Float.intBitsToFloat(swap(in.readInt())) + " ");

Yes that worked,

Thanks again
Mike
From: Mikee on
Thanks Paul

Currently part of the environment I'm working in is constrained to 1.5
but
I'lll check 1.6 out for future ref.

> I haven't checked the API (http://java.sun.com/javase/6/docs/api/
> index.html?java/nio/package-summary.html) but I seem to remember that
> NIO allows you to read floats etc in the byte order of your choice.
> That might avoid the need to do all the bit twiddling yourself.

Cheers
Mike