Pages

Friday, March 19, 2010

Microsoft Basic Floating Point vs IEEE 754

Why is it important for Metastock conversion?

Basically, this two methods will be used to read and write the Metastock data files.
A key part is the conversion to the Metastock format from the old Microsoft Basic floating point format to  IEEE 754 (used in Java).

In the Metastock system floating point numbers are represented in the old Microsoft Basic Floating point format

It consists of 4 eight bits bytes, called single precision. The layout of a MBF and IEEE754 number is:

MBF __________|__Byte 4 __|__Byte 3 __|__Byte 2 __|__Byte 1 __|
Position bit__| 31 ... 24 | 23 ... 16 | 15 .. . 8 | 7 . . . 0 |
Value in bit__| EEEE EEEE | XMMM MMMM | MMMM MMMM | MMMM MMMM |


IEEE 754 _____|__Byte 4 __|__Byte 3 __|__Byte 2 __|__Byte 1 __|
Position bit__| 31 ... 24 | 23 ... 16 | 15 .. . 8 | 7 . . . 0 |
Value in bit__| XEEE EEEE | EMMM MMMM | MMMM MMMM | MMMM MMMM |

Components:
X = sign bit
E = 8 bit exponent
M = 23 bit mantissa

So basically we need to convert from Metastock bytes to a float number and after we convert a float to Metastock bytes again.


CODE:
public class MSBasicFloatPointConverter {

    public static final float mbfByteToIeeeFloat(byte[] bytes) {

        final int BYTE_MASK = 0x0ff;
        final int MANTISSA_MASK = 0x007fffff;
        final int EXPONENT_MASK = 0x0ff; 
        final int SIGN_MASK = 0x080; 
        
        int intOne = (int) (bytes[0] & BYTE_MASK);
        int intTwo = (int) (bytes[1] & BYTE_MASK);
        int intThree = (int) (bytes[2] & BYTE_MASK);
        int intFour = (int) (bytes[3] & BYTE_MASK);

        int msf = intFour << 24 | intThree << 16 | intTwo << 8 | intOne;

        int mantissa = (msf & MANTISSA_MASK);
        int exponent = ((msf >> 24) & EXPONENT_MASK) - 2;
        int sign = (msf >> 16) & SIGN_MASK;

        mantissa |= exponent << 23 | sign << 24;
        float result = Float.intBitsToFloat(mantissa);

        return result < 0 ? 0 : result;    
    }

    public static final byte[] ieeeFloatToMBFByte(float value) {

        final int IEEE_SIGNAL_MASK = 0x80000000;
        final int IEEE_EXPONENT_MASK = 0x7f800000;
        final int IEEE_MANTISSA_MASK = 0x007fffff;

        int intIeee = Float.floatToIntBits(value);
        int ieeeSignal = intIeee & IEEE_SIGNAL_MASK;
        int ieeeExponent = intIeee & IEEE_EXPONENT_MASK;
        int ieeeMantissa = intIeee & IEEE_MANTISSA_MASK;


         int intMbf = Float.floatToIntBits(0f);
         intMbf = (ieeeExponent << 1) | (ieeeSignal >>> 8) | ieeeMantissa;
         return new byte[] {
                 (byte)((intMbf >> 0) & 0xff),
                 (byte)((intMbf >> 8) & 0xff),
                 (byte)((intMbf >> 16) & 0xff),
                 (byte)(((intMbf >> 24) & 0xff) + 2)          
         };    
    }
}
Test:
import static org.junit.Assert.assertTrue;

import java.util.Random;

import org.junit.Test;

public class ConvertMicrosoftBasicFloatTest {
    @Test
    public void agressiveTest() throws Exception {
        final int TEST_SIZE = new Double(Math.pow(2, 16)).intValue();
        Random rand = new Random();
        for (int i = 0; i < TEST_SIZE; i++) {
            float original = 1000 * rand.nextFloat();

            // converting float to bytes           
             byte[] bytes = MSBasicFloatPointConverter.ieeeFloatToMBFByte(original);

            // converting bytes to float
            float converted = MSBasicFloatPointConverter.mbfByteToIeeeFloat(bytes);

            // checking             
            assertTrue(original == converted);
        }
    }
}

No comments :