typedef union _mbf_dbl_t
{
double d;
unsigned short i[4];
unsigned long l[2];
} Mbfd_t;
int DMsbinToIeee( double *source, double *dest )
// ---[comments reformatted for line breaks. - felixk]---
// Description:
// This function was written by Felix A. Kasza [MVP on MSLANG
// Compuserve forum] as a sample of what is required to convert
// a Microsoft binary format double precision number to an ieee
// format double.
// Parameters:
// source - pointer to MBF format double to convert
// dest - pointer to buffer for ieee result
// Return:
// 0 - conversion was successful
// 1 - overflow
// Shared Data:
// none
// Use of Other Routines:
// none
// Assumptions/Cautions:
// The routine assumes that the MBF double is normalized.
//
// Some precision may be lost because the ieee format uses fewer
// bits for the mantissa.
{
int shifts;
Mbfd_t ie; // will hold converted value
Mbfd_t mb; // temporary storage for MBF value
// Copy the number to be converted to a union for easier handling.
mb.d = *source;
// I special-case all-zero because the exponent should not
// be biased. Rather, 0 is represented by a pattern of all
// binary zeroes.
if ( mb.l[0] == 0L && mb.l[1] == 0L ) {
*dest = 0.0;
return 0;
}
// get exponent value
ie.i[3] = (unsigned short) mb.i[3] >> 8;
// ie.i[3] now has an exponent still biased by 0x80
// adjust by -1 to compensate for 0.111... vs. 1.111...
ie.i[3] += 894; // convert to 1023 bias (+1023 - 128 - 1)
// left-adjust, leaving one bit for the sign
ie.i[3] <<= 4; // 00000xxxxxxxxxxxx ==> 0xxxxxxxxxxx0000
// next line ORs the sign bit into the word containing the exp.
ie.i[3] |= ( mb.i[3] & 0x0080 )? 0x8000: 0x0000;
// next line gets four bits of mantissa which will fit into the
// exponent word
ie.i[3] |= (unsigned short) ( mb.i[3] >> 3 ) & 0x000f;
// now we have 6-3/8 bytes mantissa left to shoehorn into 6 bytes.
// we do that by right-shifting the entire mbf by three bits and
// masking off what we don't need. this would be easier if C
// had a roll-through-carry operator.
for ( shifts = 0; shifts < 3; shifts ++ )
{
mb.l[0] >>= 1; // shift low doubleword
// the next line examines the lowest bit of the high dword
// which must become the highest bit of the low dword. if
// it is set, the low dword gets ORed with a 1 in the highest
// bit position. I wish we'd be doing this in the assembly
// section ...
mb.l[0] |= ( mb.l[1] & 0x00000001L )? 0x80000000L: 0L;
// shift the high dword now. the lowest bit gets lost which is
// why we checked it above.
mb.l[1] >>= 1;
}
// now, ignore the highest 16 bits (which in the IEEE number we
// already filled) and transfer the remaining 48 bits
ie.i[2] = mb.i[2];
ie.i[1] = mb.i[1];
ie.i[0] = mb.i[0];
// THAT'S IT!
*dest = ie.d;
return 0;
} // End DMsbinToIeee()
This is likely some simple bit manipulation. However, it require that you know the format of the dmsbin format.
Do you have any documentation of it anywhere or can you find such info?
All floating point formats are based on the following format:
a double or floating point value is divided into three sections:
MSB LSB
|s|exponent|mantissa..............|
The most significant bit is the sign bit, if 0 the number is positive and if 1 the number is negative. This should be the same in both formats.
exponent is the next field counting form the most significant bits. This field is used to hold the exponent of the number. This value is a biased integer and different floating point formats differ in how many bits they use for this field and in the value of the bias. There are also typically two special values for exponent (all bits 0 and all bits 1) that has special meaning and deserve special attention. If the format does not treat the form with all bits 1 special then that in itself is special. (they normally do).
The remainder of the number is the mantissa and again the different formats differ in how many bits they include in the mantissa but they also differ in wether they include the most significant bit or not.
The point is that mantissa is interpreted differently depending on if the exponent is special (all bits 0 or all bits 1) or regular (mix of 0 and 1).
For regular mantissa, the mantissa is often adjusted to be a number such that 0.5 <= Mantissa < 1.0
Since the mantissa is always >= 0.5 and always less than 1 then in binary form it is always written as 0.1xxxxx since that first digit after the binary point (like decimal point but we're talking binary representation here) is always 1 there's no reason why you should store it. However, some formats do store the value while other formats do not. Those who do not has of course one extra bit of precision compared to those that always include that 1 bit.
The mantissa itself is stored as an integer M which is scaled so that M * 2^scale for a fixed scale factor such that 0 <= M < 0.5 you then add 0.5 and get the value in the aforementioned range 0.5 <= mantissa < 1.0 to compute the value that the floating point number represent. The scale is of course negative since the integer is usually a very high value.
The exponent is stored as a biased integer. This means that the value is in the range 0 <= exponent < pow(2,number of bits for exponent)
However, we also want to store negative values and so about half of that range is used to store negative values. The solution is to bias the value so that an exponent of 0 isn't stored as 0 (all bits 0) but rather as a value BIAS. An exponent of 1 is stored as BIAS + 1 etc. An exponent of -5 is then stored as BIAS - 5. Since BIAS is a very high value you can store a wide range of positive and negative exponents this way.
So, now you know how floating point values are stored, how to decode them? I can give you the parameters for the IEEE decoding used on the PC - the one that C++'s double on the PC is encoded in.
1 sign bit - 0 means positive 1 means negative.
exponent - 9 bits and bias is 0x0ff. or 255. A value of 0x001 (minimum regular value) in this field indicate an exponent of -254 while a value of 0x1fe means an exponent of -1. 0x1ff means an exponent of 0, 0x200 means an exponent of 1 and 0x3fe (maximum regular value) means an exponent of +255.
The value 0 is special and if mantissa is also 0 then this encodes the double value 0. I.e. 0.0 == all bits 0. However the value 1000000...00 would also be -0.0 is also 0.
If mantissa is not 0 then the value hold a unormalized value which is a way to represent very small values (very close to 0) Essentially the value is computed using the exponent as a regular exponent but the mantissa do not get the usual 0.5 added to it so the mantissa is very small.
The exponent value of all bits 1 (0x3ff) is also special and is used to encode INFINITY and not-a-number.
If mantissa is 0 the value is infinity if I remember correctly, the sign bit counts and we differ between -INF and +INF.
If mantissa is not 0 the value is 'not-a-number'. I think the mantissa bits is ignored (as long as they are not all 0) and when setting is set to a fixed pattern.
The remaining 54 bits holds the mantissa and when the exponent is regular you get the extra 0.5 so the most significant bit of the mantissa is not stored.
Once you figure out the format of the old visual basic it is then possible to convert the values. Probably extend or shrink the exponent, add zero bits at end of mantissa or shrink the mantissa. Maybe remove the top bit of the mantissa which isn't used in the IEEE format (it is always 1 and is implied). etc etc
>>However, it require that you know the format of the dmsbin format
Err, there's already a detailed description of this format in the above link :o)
But, as we are already repeating, I might it post here as well also...
The MS binary format (MBF) consists of an 8bit binary exponent in the
first byte (unsigned, exponent biased by 80h), followed by a sign bit,
followed by a 55bit or 23bit mantissa (depending on whether we talk
double or single precision). The first digit of the full 56bit
mantissa would always be a binary 1, so they used this bit position
for the sign of the number. The mantissa itself is unsigned (IOW,
stored as sign and absolute value, not in 2's complement).
Here is what 1.5 looks like in 8byte MBF (hex):
81 40 00 00 00 00 00 00
81 says the exponent is 1, so we have to multiply the mantissa by 2^1
== 2. 40h is 01000000 binary; the first bit is the sign (0 ==
positive). Let's remember that and put the implied "1" back in:
11000000 (followed by 6 bytes of 00000000)
That's the mantissa. The binary point is to the left, so what we have
here is 1 half plus 1 fourth plus 0 eighths plus 0 sixteenths ... The
mantissa is therefore 3 fourths, or 0.75 in decimal. Multiplied by the
2^1, that gives 1.5.
Now for the IEEE side of things. We'll only look at 8byte reals (the
familiar "double" type). IEEE format sacrifices precision for extended
range; it uses an 11bit exponent, biased by 1023, and accordingly has
only 52bits of mantissa (53 if we count the implied "1" bit). Also, it
has the mantissa's sign in front, as the most significant bit of the
most significant byte. (Reminds me -- all values I show here start
with the highest byte and end with the lowest byte, contrary to the
memory representation of things.) Another, minor, difference is that
the binary point is not before the implied "1" bit but _after_ it.
That means we'll have to adjust the exponent bias by 1.
The sequence of steps needed to convert an MBF number to a double is
therefore:
- get the sign bit, stick it into the double
- get and unbias the exponent, bias by 1023, right-adjust into 11bit
field in the double
- get and truncate the mantissa (excluding the MBF sign), left-align
into the mantissa field in the double
0
duncanlatimerAuthor Commented:
jkr, you are great, I was in a real pickle and you came through fast!
0
Featured Post
Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.
typedef union _mbf_dbl_t
{
double d;
unsigned short i[4];
unsigned long l[2];
} Mbfd_t;
int DMsbinToIeee( double *source, double *dest )
// ---[comments reformatted for line breaks. - felixk]---
// Description:
// This function was written by Felix A. Kasza [MVP on MSLANG
// Compuserve forum] as a sample of what is required to convert
// a Microsoft binary format double precision number to an ieee
// format double.
// Parameters:
// source - pointer to MBF format double to convert
// dest - pointer to buffer for ieee result
// Return:
// 0 - conversion was successful
// 1 - overflow
// Shared Data:
// none
// Use of Other Routines:
// none
// Assumptions/Cautions:
// The routine assumes that the MBF double is normalized.
//
// Some precision may be lost because the ieee format uses fewer
// bits for the mantissa.
{
int shifts;
Mbfd_t ie; // will hold converted value
Mbfd_t mb; // temporary storage for MBF value
// Copy the number to be converted to a union for easier handling.
mb.d = *source;
// I special-case all-zero because the exponent should not
// be biased. Rather, 0 is represented by a pattern of all
// binary zeroes.
if ( mb.l[0] == 0L && mb.l[1] == 0L ) {
*dest = 0.0;
return 0;
}
// get exponent value
ie.i[3] = (unsigned short) mb.i[3] >> 8;
// ie.i[3] now has an exponent still biased by 0x80
// adjust by -1 to compensate for 0.111... vs. 1.111...
ie.i[3] += 894; // convert to 1023 bias (+1023 - 128 - 1)
// left-adjust, leaving one bit for the sign
ie.i[3] <<= 4; // 00000xxxxxxxxxxxx ==> 0xxxxxxxxxxx0000
// next line ORs the sign bit into the word containing the exp.
ie.i[3] |= ( mb.i[3] & 0x0080 )? 0x8000: 0x0000;
// next line gets four bits of mantissa which will fit into the
// exponent word
ie.i[3] |= (unsigned short) ( mb.i[3] >> 3 ) & 0x000f;
// now we have 6-3/8 bytes mantissa left to shoehorn into 6 bytes.
// we do that by right-shifting the entire mbf by three bits and
// masking off what we don't need. this would be easier if C
// had a roll-through-carry operator.
for ( shifts = 0; shifts < 3; shifts ++ )
{
mb.l[0] >>= 1; // shift low doubleword
// the next line examines the lowest bit of the high dword
// which must become the highest bit of the low dword. if
// it is set, the low dword gets ORed with a 1 in the highest
// bit position. I wish we'd be doing this in the assembly
// section ...
mb.l[0] |= ( mb.l[1] & 0x00000001L )? 0x80000000L: 0L;
// shift the high dword now. the lowest bit gets lost which is
// why we checked it above.
mb.l[1] >>= 1;
}
// now, ignore the highest 16 bits (which in the IEEE number we
// already filled) and transfer the remaining 48 bits
ie.i[2] = mb.i[2];
ie.i[1] = mb.i[1];
ie.i[0] = mb.i[0];
// THAT'S IT!
*dest = ie.d;
return 0;
} // End DMsbinToIeee()