3

I got Python float value and I need to convert it in to Microsoft Basic Float (MBF) format. Luckily, Got some code from internet that does the reverse.

def fmsbin2ieee(self,bytes):
    """Convert an array of 4 bytes containing Microsoft Binary floating point
    number to IEEE floating point format (which is used by Python)"""
    as_int = struct.unpack("i", bytes)
    if not as_int:
        return 0.0
    man = long(struct.unpack('H', bytes[2:])[0])
    exp = (man & 0xff00) - 0x0200
    if (exp & 0x8000 != man & 0x8000):
        return 1.0
        #raise ValueError('exponent overflow')
    man = man & 0x7f | (man << 8) & 0x8000
    man |= exp >> 1
    bytes2 = bytes[:2]
    bytes2 += chr(man & 255)
    bytes2 += chr((man >> 8) & 255)
    return struct.unpack("f", bytes2)[0]

Now I need to reverse this process, but no success yet. Any help please.

MPelletier
  • 16,256
  • 15
  • 86
  • 137
naren
  • 31
  • 2

4 Answers4

4

If you're going to perform these conversions while running under Windows, faster might be to download and install mbf2ieee.exe and call the CVS function offered by the resulting Mbf2ieee.dll (e.g. via [ctypes][2]).

If you're keen to do it in pure Python, I think (but I can't test, having no MBF numbers at hand) that the following might work (I just ported it to Python from C code here):

def mbf2ieee(mbf_4bytestring):
  msbin = struct.unpack('4B', mbf_4bytestring)
  if msbin[3] == 0: return 0.0

  ieee = [0] * 4
  sign = msbin[2] & 0x80
  ieee_exp = msbin[3] - 2
  ieee[3] = sign | (ieee_exp >> 1)
  ieee[2] = (ieee_exp << 7) | (msbin[2] & 0x7f)
  ieee[:2] = msbin[:2]

  return struct.unpack('f', ieee)[0]

If this has problems, can you give some examples of input values and expected results?

Edit: if it's the reverse function you want, it should be:

def float2mbf4byte(f):
  ieee = struct.pack('f', f)
  msbin = [0] * 4
  sign = ieee[3] & 0x80

  msbin_exp = (ieee[3] << 1) | (ieee[2] >> 7)
  # how do you want to treat too-large exponents...?
  if msbin_exp == 0xfe: raise OverflowError
  msbin_exp += 2

  msbin[3] = msbin_exp
  msbin[2] = sign | (ieee[2] & 0x7f)
  msbin[:2] = ieee[:2]
  return msbin
Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
  • Ummmmmm ..... this is bassackwards ... The OP wants to go from python float (8-byte IEEE float) to MS Basic float. – John Machin Feb 15 '10 at 23:42
  • @John, guess I got confused by the code he posted, which does mbf to ieee -- let me add the reverse function to my answer, then. – Alex Martelli Feb 16 '10 at 00:15
  • @Alex: Going from 8-byte IEEE to 4-byte MS through 4-byte IEEE may lose more precision than is lost by going directly -- have you considered this? – John Machin Feb 16 '10 at 01:47
  • @John, considering that we're just copying bits I don't see where the precision would be lost -- the first `struct.pack` is supposed to perform the proper rounding. – Alex Martelli Feb 16 '10 at 02:15
  • @Alex: glad that you've considered that. Now consider these (1) say the code is for Python 3.x, not 2.x (2) It won't work on a bigendian machine (3) there are two places where you should be using `bytes()`: `return bytes(msbin)` in the 2nd function and `return struct.unpack('f', bytes(ieee))[0]` in the 1st function (4) it would be better in the 2nd function if you special-cased input zero to return an all-bits-zero answer instead of bytes([0,0,0,2]) – John Machin Feb 16 '10 at 03:56
  • @John, taking one thing at a time, what do you believe could possibly be the issue with your point (1)? Please elucidate. Re (2), the format strings do need a '>' prefix to work on big-endian CPUs. Re (3), where does the OP specify they want `bytes` or any other specific data type as a result, exactly? Re (4), I did mention _exactly_ what C code I was porting, so I'm giving the same results as it would -- if you know the MBF format and that there's a bug in that C code, why not post your own answer instead of hassling me so tediously? I did say I have no MBF-format values to test. – Alex Martelli Feb 16 '10 at 06:05
0

Well, I tried float2mbf4byte() and made 2 modifications:

  1. converting negative values is now working,
  2. for Python 2, ieee should be list of int's, not string

The snippet:

def float2mbf4byte(f):
    ieee = [ord(s) for s in struct.pack('f', f)]
    msbin = [0] * 4

    sign = ieee[3] & 0x80
    ieee[3] &= 0x7f

    msbin_exp = (ieee[3] << 1) | (ieee[2] >> 7)
    # how do you want to treat too-large exponents...?
    if msbin_exp == 0xfe: raise OverflowError
    msbin_exp += 2

    msbin[3] = msbin_exp
    msbin[2] = sign | (ieee[2] & 0x7f)
    msbin[:2] = ieee[:2]
    return msbin

def ieee2fmsbin(f):
    return struct.pack('4B', *float2mbf4byte(f))
akjoshi
  • 15,374
  • 13
  • 103
  • 121
0

The mbf2ieee failed to convert properly, try this one:

import struct

def float2mbf4byte(f):
    ieee = struct.pack('f', f)
    msbin = [0] * 4
    sign = ieee[3] & 0x80

    msbin_exp = (ieee[3] << 1) | (ieee[2] >> 7)
    # how do you want to treat too-large exponents...?
    if msbin_exp == 0xfe: raise OverflowError
    msbin_exp += 2

    msbin[3] = msbin_exp
    msbin[2] = sign | (ieee[2] & 0x7f)
    msbin[:2] = ieee[:2]
    return msbin

def mbf2ieee(mbf_4bytestring):
    msbin = struct.unpack('4B', mbf_4bytestring)
    if msbin[3] == 0: return 0.0

    ieee = [0] * 4
    sign = msbin[2] & 0x80
    ieee_exp = msbin[3] - 2
    ieee[3] = sign | (ieee_exp >> 1)
    ieee[2] = (ieee_exp << 7) | (msbin[2] & 0x7f)
    ieee[:2] = msbin[:2]

    return struct.unpack('f', bytearray(ieee))[0]


print(mbf2ieee(bytearray(float2mbf4byte(52400126))))

... and you will get:

builtins.ValueError: byte must be in range(0, 256) because the right answer is [0,E4,47,9A], but float2mbf4byte gives [0, 228, 19527, 76] . 3rd element is not a byte size value

Valio
  • 31
  • 9
0
#Converter between MSF and IEEE
import struct

def fmsbin2ieee(b):
    as_int = struct.unpack('i', b)
    if not as_int:
        return 0.0
    man = int(struct.unpack('H', b[2:])[0])
    if not man:
        return 0.0
    exp = (man & 0xff00) - 0x0200
    man = man & 0x7f | (man << 8) & 0x8000
    man |= exp >> 1

    bytes2 = bytes([b[0], b[1], (man & 255), ((man >> 8) & 255)])
    return struct.unpack('f', bytes2)[0]

def float2mbf4byte(f):
  ieee = struct.pack('f', f)
  msbin = [0] * 4
  sign = ieee[3] & 0x80

  msbin_exp = (ieee[3] << 1) | (ieee[2] >> 7)
  # how do you want to treat too-large exponents...?
  if msbin_exp == 0xfe: raise OverflowError
  msbin_exp += 2

  msbin[3] = msbin_exp
  msbin[2] = sign | (ieee[2] & 0x7f)
  msbin[:2] = ieee[:2]
  return bytes(msbin)

if __name__ == '__main__':
    key = bytes([0x13, 0x00, 0x10, 0x10])
    print(type(key))
    print(key)

    # 4 bytes MBF to float IEEE
    flIEEE = fmsbin2ieee(key)
    print(type(flIEEE))
    print("ieee=", flIEEE)

    # float IEEE to 4 bytes MBF
    msf = float2mbf4byte(flIEEE)
    print(type(msf))
    print(msf)
Duc Toan Pham
  • 474
  • 6
  • 6
  • Hi, thanks for the answer, but please explain a bit more what you did. That way, both the OP and future users can understand what you're proposing. – Diego Ramirez Jan 13 '22 at 19:14
  • Because of complexity of explaining bit processing and Microsoft & IEEE number formats, I included the unit test. Personally, others should not waste time to dive deeply on Microsoft Format which is not popular nowadays. You can use these helper functions enough. – Duc Toan Pham Apr 03 '22 at 03:42