Byte array to floating point such as FPE2 in Java Apple's System Management Controller uses FPE2 and SP78 as floating-point formats.

While writing a Java library that that interfaces with the SMC, I ported someone's C function to convert read a FPE2-formatted byte array as a float. 

public static float strtof(byte[] bytes, int size, int e) {
    int total = 0;
    // Add up bits to left of fractional bits
    for (int i = 0; i &lt; size; i++) {
        if (i == (size - 1))
            total += (bytes[i] &amp; 0xff) &gt;&gt; e;
        else
            total += (bytes[i] &amp; 0xff) &lt;&lt; (size - 1 - i) * (8 - e);
    }
    // Mask fractional bits and divide
    return total + (bytes[size - 1] &amp; (1 &lt;&lt; e) - 1) / (float) (1 &lt;&lt; e);
}


The bytes and size parameters come from reading a structure in Apple's SMC interface.  The array is a byte[32] value in a structure, where a second part of the structure states how many of the elements of the array are significant.  In the use case, getting fan speed, bytes[0] and bytes[1] will be populated with data (the other 30 bytes are ignored) and size will have a value of 2.  

In the given code, the value of e represents how many rightmost bits out of the size bytes are for the fractional part.  If e is 2 (such as for FPE2), then the final 2 bits can have values 0, 1, 2, or 3 representing the fractions 0.0, 0.25, 0.5, and 0.75.  The remaining leading bits are interpreted as the integer part.

Example input: 


bytes = { 0x00, 0x05, 0x00, 0x00, ... } // First 2 bytes binary 101
size = 2
e = 2


Example output: 1.25 // Integer portion is 1, fraction is 1/4.



Did I do it right (assuming arguments are already validated: e will never be more than 8, and size will never be more than 4)?  Points of concern are order of operation, what happens bitwise when I promote a byte to an int and then shift it.

While this function converts an FPE2 format number, I'm trying to make it a bit more general.  A similar format, SP78, is used for temperature.  The rightmost 8 bits (divided by 256) are the fractional portion, the left 8 bits are a signed byte.  (This method is not intended to handle the signed calculation but this is given as a reference.)
Documentation

I took me a while to understand and research the algorithm and the purpose of the function parameters. It would be better to write some Javadoc for reviewers and future readers (which may include yourself).

Identifiers

You should use variable and function identifiers that explain their purpose (barring obvious cases like i for a loop iterator).


strtof is a function defined since C89 and does something very different from your function; that's not a good name here. What about FPxToFloat because the function converts data in a variant of the FP* formats (x instead of *) to a float?
e is the number of bits in the integral part of the number. How about integerBits instead?
total – My first thought was “total of what?”. I usually go for a name like result in these cases since it holds the function result (or an intermediate result on the way to the final result) which should be well documented somewhere nearby.


Limitations


Why is size restricted to ≤ 4 bytes and integerBits restricted to ≤ 8. You say you want a more generic solution. I don't see a reason why size can't be 17 and integerBits 49.
Currently the encoded number needs to start at offset 0 inside the bytes array. Since Java doesn't have C's pointer arithmetic it would be useful to offer an offset parameter like the read and write functions in java.io.

You can still offer a convenience function with less boiler plate:

public static float FPxToFloat(byte[] bytes, int integerBits) {
    return FPxToFloat(bytes, 0, bytes.length, integerBits);
}



Complexity

Your implementation seems overly complicated and suffers from a number of unnecessary limitations (see first point in the section above). What about this:

double result = 0;
int exponent = integerBits - Byte.SIZE;
for (; offset &lt; size; offset++, exponent -= Byte.SIZE) {
    result += Math.scalb(bytes[offset] &amp; 0xFF, exponent);
}
return result;


scalb avoids multiplicative floating-point operations and overflow of integers that shall later be converted to floating-point. The only optimization I can think of is to work on long instead of byte if possible to use the available processor word size better (to achieve that one can wrap byte[] inside a java.nio.ByteBuffer and use a LongBuffer view onto it).

Full program (Ideone is currently acting up here so a Gist it has to be for now.)