1

I have a binary file that contains 10-bit fixed point values that I need to convert to Java float. I'm almost certain that the "format" is x.xxxxxxxxx, where x is a bit. And I do think I understand the basics of doing this by hand.

I would have to do to the bits: x + 0.5*x + 0,25*x... etc. For example

1010110010 = 1×1 + 0×½ + 1×¼ + 0×⅛ + 1×¹⁄₁₆ + 1×¹⁄₃₂...

But I have no idea how to do this in Java. I can read the file only one BYTE at a time, one value would be reading 2 bytes = 16 bits.

The file is in LITTLE ENDIAN.

phuclv
  • 37,963
  • 15
  • 156
  • 475
tonihele
  • 49
  • 1
  • 6
  • possible duplicate of [Parsing string to double - java](http://stackoverflow.com/questions/13166386/parsing-string-to-double-java) – Philipp Sep 17 '14 at 18:30
  • 2
    Nope! Bits, not bytes or anything easy like that :) The "x"s in my "format", represent bits, 0/1 – tonihele Sep 17 '14 at 18:37
  • 1
    OK, I retracted the close vote, but you might want to edit your question, because it reads as if you mean `x.xxxxxxxxx` as an ASCII representation. You could, for example, post an example in form of a hex-dump and what value you expect it to decode to. – Philipp Sep 17 '14 at 18:41
  • You should give us more context, especially what kind of file this is. – Radiodef Sep 17 '14 at 19:09
  • Are bits 1-6 of float #2 packed into the same byte as bits 9 & 10 of float #1? That is, do the bits go `11111111 11222222 22223333 33333344 44444444`? Or is it `11111111 11------ 22222222 22------`, where the dashes are ignored? – Tim Sep 17 '14 at 19:15
  • 2
    If you have 10 bits, and the "binary point is between the first and second bits, that just means the number is divided by 2 to the 9th, or 512. So put the value in the low end of an int, cast to float, divide by 512, and you have your number. – Hot Licks Sep 17 '14 at 19:27

5 Answers5

2

If you have 10 bits, and the "binary point" is between the first and second bits, that just means the number is divided by 2 to the 9th, or 512. So put the value in the low end of an int, cast to float, divide by 512, and you have your number.

float finalAnswer = bitsInLowOrderEndOfInt / 512.0;
Hot Licks
  • 47,103
  • 17
  • 93
  • 151
  • You should add your explanation from the question comments to your answer. You're right, but what you've written is unapproachable. – Tim Sep 17 '14 at 19:33
  • I'm sorry for the question presentation. What does bitsInLowOrderEndOfInt represent? Meaning that I should read the 10 bits as unsigned int? 0-1023? – tonihele Sep 17 '14 at 19:43
  • you need to read the 10 bits as a binary value, and it's from 0-1023 – phuclv Sep 18 '14 at 05:42
1

Instead of computing each bit, it is more efficient to calculate the exponent.

public double parse (InputStream is) {
    long value = 0, factor = 0;
    for(;;) {
        int ch = is.read();
        if (ch == '.') {
            factor = 1;
        } else if(ch == '0' || ch = '1') {
            factor *= 2;
            value = value * 2 + ch - '0';
        } else {
            break;
        }
    }
    return factor == 0 ? value : (double) value / factor;
}
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
0

I would have to do to the bits: x,0.5*x+0.25*x.... etc.

That's actually not difficult in Java. Assuming you converted the bits into booleans, your code can look like this:

public double parse (boolean ... bits)
{
    double val = (bits[0] ? 1 : 0); // note to check that bits[0] exists
    for (int i = 1; i < bits.length; i++)
        val += (1.0 / 2*i) * (bits[i] ? 1 : 0) * Math.pow(2, -1*i);
    return val;
}
msrd0
  • 7,816
  • 9
  • 47
  • 82
0

The conversion from bytes to 10-bit raw ints could be like this:

Path file = Paths.get("....");
byte[] data = Files.readAllBytes(file);

// data is a sequence of 10 bit fixed-point floats
if (data.length % 5 != 0) {
    throw new IllegalArgumentException(
        "File length is not a multiple of 10 bits: " + file);
}
int bitCount = data.length * 8;
bitCount = bitCount - (bitCount % 10); // When without check.
int[] rawInts = new int[bitCount / 10];
for (int bitIx = 0; bitIx < bitCount; bitIx += 10) {
    int byteIx0 = bitIx / 8;
    int byte0 = data[byteIx0) & 0xFF;
    int byteIx1 = byteIx0 + 1;
    int byte1 = data[byteIx1) & 0xFF;
    int off = bitIx % 8;

    int rawInt = ((byte0 << off + 2) & 0x3FF)
        | (byte1 >> (8 - (2 + off));
    rawInts[bitIx / 10] = rawInt;
}

Now to the fixed point format: with 10 bits one has 210 numbers, hence 0 .. 1023. This suggests that the fixed point is based around 1000, maybe 9.99

int integralPart = rawInt / 100;
int decimals = rawInt % 100;

P.S. not tested the bit shifting, which by the way could also be little endian.

Joop Eggen
  • 107,315
  • 7
  • 83
  • 138
  • The file is actually little endian. That I know for a fact, yet I don't fully understand it in practice. – tonihele Sep 18 '14 at 05:25
  • Are you sure it is fixed-point? It looks more like BCD, packing 3 digits in 10 bits, with the extraneous values for other purposes: 1023 = decimal point, 1022 = minus or so. (My check on exact fitting a multiple of 10 bits is dumb in hindsight.) – Joop Eggen Sep 18 '14 at 08:11
  • I'm actually doing this reverse engineering of Dungeon Keeper 2 meshes and animations (https://github.com/tonihele/OpenDungeonKeeper). I have a community created documentation that states that these are 3D coordinates. Z, Y, X, each presented with 10-bits. And scaled with a float. In this case the float is 0.82xxx and the scaled vector coordinates should be small, less than 1. So far the documentation has been spot on. And I concluded that I just don't understand forming the vector components. I have only trial and error to verify this. – tonihele Sep 18 '14 at 09:59
  • https://github.com/tonihele/OpenDungeonKeeper/blob/master/src/toniarts/opendungeonkeeper/tools/convert/kmf/KmfFile.java - here is the magic, right around line 398. If that is any help or interest. – tonihele Sep 18 '14 at 10:05
  • So: `double coord = (0.82 * rawInt) / 1024;` (with double division). That would be at most 0.82; Maybe no division at all, 0.82 * 1024 ≈ 840. Good luck, I am adventuring the dungeon at work. – Joop Eggen Sep 18 '14 at 10:33
  • I have a feeling that I tried such a formula already. But I'll test in the evening, I'm also in a dungeon called work at the moment :) – tonihele Sep 18 '14 at 10:39
0

Alternatively just construct the float directly from the raw bits

Assuming the binary value is read into the low 10 bits of v

if (v == 0)
   return 0.0f;

// shift the mantissa to the correct position and drop the implicit 1 bit
int mantissa = (v << (23 - Integer.highestOneBit(v))) & 0x7ffff;
int exponent = (-9 + 127) << 23;

return Float.intBitsToFloat(mantissa | exponent);
phuclv
  • 37,963
  • 15
  • 156
  • 475