Algorithms for 6-byte ("real48") Borland Pascal Floating Point numbers
Martin Bless
m.bless at gmx.de
Sun Dec 16 18:54:49 EST 2001
More information about the Python-list mailing list
Sun Dec 16 18:54:49 EST 2001
- Previous message (by thread): reading 6-byte ("real48") Borland Pascal Floating Point numbers
- Next message (by thread): what is self._base?
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Below is a working example of 3 different ways I figured out to convert those 6-byte Borland Pascal floating points to 'normal' floats. Status: experimental. What do you think about it? The algorithms presented seem to hold at least for values from -1000.0 to +1000.0. But where are the pitfalls? I'd be very pleased if someone with more "real" and "numerical" understandig has a look at it. And yes, if the algorithm 'real48_to_double' seems ok, it would be wonderful to have this little bit shifting available via the 'struct' module. Getting greedy: Yes, both ways, pack and unpack... :-) Here's my 'real48.py': '''Unpack Borland Pascal 6-byte "real48" floating point number. Status: experimental Martin Bless (mb), m.bless at gmx.de, 2001-12-16, 2001-12-16 In the context of this program the data structure of a real48 number is assumed to be a six-byte string ('r48'): r48[0] r48[1] r48[2] r48[3] r48[4] r48[5] a0 a1 a2 a3 a4 a5 sffffff ffffffff ffffffff ffffffff ffffffff eeeeeeee s=sign, f=mantisse (39 bit), e=exponent (8bit, bias 129) order: S-F-E See http://www.merlyn.demon.co.uk/pas-type.htm#FF ''' import struct START_RANGE = -10 END_RANGE = +10 + 1 USE_INLINE_DATA = 1 # sampled real nums from file created by Borland Pascal pascal_data = ('' +'\x84\x00\x00\x00\x00\xa0' # -10.000000 +'\x84\x00\x00\x00\x00\x90' # -9.000000 +'\x84\x00\x00\x00\x00\x80' # -8.000000 +'\x83\x00\x00\x00\x00\xe0' # -7.000000 +'\x83\x00\x00\x00\x00\xc0' # -6.000000 +'\x83\x00\x00\x00\x00\xa0' # -5.000000 +'\x83\x00\x00\x00\x00\x80' # -4.000000 +'\x82\x00\x00\x00\x00\xc0' # -3.000000 +'\x82\x00\x00\x00\x00\x80' # -2.000000 +'\x81\x00\x00\x00\x00\x80' # -1.000000 +'\x00\x00\x00\x00\x00\x00' # 0.000000 +'\x81\x00\x00\x00\x00\x00' # 1.000000 +'\x82\x00\x00\x00\x00\x00' # 2.000000 +'\x82\x00\x00\x00\x00\x40' # 3.000000 +'\x83\x00\x00\x00\x00\x00' # 4.000000 +'\x83\x00\x00\x00\x00\x20' # 5.000000 +'\x83\x00\x00\x00\x00\x40' # 6.000000 +'\x83\x00\x00\x00\x00\x60' # 7.000000 +'\x84\x00\x00\x00\x00\x00' # 8.000000 +'\x84\x00\x00\x00\x00\x10' # 9.000000 +'\x84\x00\x00\x00\x00\x20' # 10.000000 +'\x84\x00\x00\x00\x00\xa0' # -10.0 +'\x84\x00\x00\x00\x00\x90' # -9.0 +'\x84\x00\x00\x00\x00\x80' # -8.0 +'\x83\x00\x00\x00\x00\xe0' # -7.0 +'\x83\x00\x00\x00\x00\xc0' # -6.0 +'\x83\x00\x00\x00\x00\xa0' # -5.0 +'\x83\x00\x00\x00\x00\x80' # -4.0 +'\x82\x00\x00\x00\x00\xc0' # -3.0 +'\x82\x00\x00\x00\x00\x80' # -2.0 +'\x81\x00\x00\x00\x00\x80' # -1.0 +'\x00\x00\x00\x00\x00\x00' # 0.0 +'\x81\x00\x00\x00\x00\x00' # 1.0 +'\x82\x00\x00\x00\x00\x00' # 2.0 +'\x82\x00\x00\x00\x00\x40' # 3.0 +'\x83\x00\x00\x00\x00\x00' # 4.0 +'\x83\x00\x00\x00\x00\x20' # 5.0 +'\x83\x00\x00\x00\x00\x40' # 6.0 +'\x83\x00\x00\x00\x00\x60' # 7.0 +'\x84\x00\x00\x00\x00\x00' # 8.0 +'\x84\x00\x00\x00\x00\x10' # 9.0 +'\x84\x00\x00\x00\x00\x20' # 10.0 ) # some helper functions def s2hex(s,delim=''): '''String to hex-string.''' l = ['0000', '0001', '0010', '0011', '0100', '0101', '0110', '0111', '1000', '1001', '1010', '1011', '1100', '1101', '1110', '1111'] result = map( lambda c,l=l: '%02x' % ord(c),s) result = delim.join(map( lambda c,l=l: '%02x' % ord(c),s)) return result def s2bin(s, byte_delim='', nibble_delim=''): '''String to bin-string. Example: print s2bin('AB') -> '0100000101000010' print s2bin('AB', '...') -> '01000001...01000010' print s2bin('AB', '...', '-') -> '0100-0001...0100-0010' ''' l = ['0000', '0001', '0010', '0011', '0100', '0101', '0110', '0111', '1000', '1001', '1010', '1011', '1100', '1101', '1110', '1111'] result = byte_delim.join(map( lambda c,l=l,d=nibble_delim: l[ord(c)/16]+d+l[ord(c)%16],s)) return result def bin2s(s, byte_delim='', nibble_delim=''): '''Reverse s2bin. Nothing checked.''' len1 = len(byte_delim) len2 = len(nibble_delim) L = [] while s: L.append(s[:4] + s[4+len2:8+len2]) s = s[8+len1+len2:] result = ''.join(map(lambda x:chr(int(x,2)),L)) return result def s_reverse(s): '''Reverse string. Is there something built in???''' L = map(lambda c:c,s) L.reverse() return ''.join(L) def bin(i): # by Andrew Gaul, c.l.p, 2001-05-10 l = ['0000', '0001', '0010', '0011', '0100', '0101', '0110', '0111', '1000', '1001', '1010', '1011', '1100', '1101', '1110', '1111'] s = ''.join(map(lambda x, l=l: l[int(x, 16)], hex(i)[2:])) if s[0] == '1' and i > 0: s = '0000' + s return s # now the working part def real48(r48): '''Return value of r48 - explicit calculation. Algorithm 'stolen' and adapted from: program sixbytes { jrs at merlyn.demon.co.uk >= 2001-11-25 } ; at http://www.merlyn.demon.co.uk/pas-type.htm#FF ''' a0,a1,a2 = ord(r48[0]), ord(r48[1]), ord(r48[2]) a3,a4,a5 = ord(r48[3]), ord(r48[4]), ord(r48[5]) sign = a0 / 128 exp = a5 - 129 # Q = 1.0/256 # Q / R2 = alternative if a5 == 0: R1 = 0.0 # R2 = 0.0 else: R1 = 1.0 + 2.0 * ((a0 % 128)+(a1+(a2+(a3+a4/256.0)/256.0)/256.0)/256.0)/256.0 # R2 = 1.0 + 2.0 * ((((a4*Q+a3)*Q+a2)*Q+a1)*Q+(a0 % 128))*Q if sign: R1 = -R1 # R2 = -R2 # info_tuple = sign, exp, 2.0**exp * R1, R1, R2 result = 2.0**exp * R1 return result def real48_to_single(r48): '''Convert r48 via struct as 'single float'. By mb. Experimental. What happens to precision? Does it hold for - at least - all possibilities of single floats? ''' exp = ord(r48[5]) if not exp: return 0.0 # adjust for exponent bias 127 of singles # instead of 129 of real48s exp = exp - 129 + 127 s = s2bin(r48[:6]) rearranged = s[0]+s2bin(chr(exp))+s[1:40] packed_single = bin2s(rearranged) result, = struct.unpack('>f', packed_single[0:4]) return result def real48_to_double(r48): '''Convert r48 via struct as 'double float'. By mb. Experimental. What happens to precision? Does it hold for possibilities of a real48? order S-E-F, s=sign, e=exponent, f=fraction part, 1-11-52 bits, exponent bias 1023 ''' exp = ord(r48[5]) if not exp: return 0.0 # adjust for exponent bias 1023 of doubles # instead of 129 of real48s. exp = exp - 129 + 1023 # we only want 11 bits bin_exp = s2bin(chr(exp/256)+chr(exp%256))[5:16] s = s2bin(r48[:6]) rearranged = s[0] + bin_exp + s[1:40] + '0'*(52-39) packed_double = bin2s(rearranged) result, = struct.unpack('>d', packed_double[0:8]) return result if __name__== "__main__": if USE_INLINE_DATA: position_adjust = 10 else: #file with 6-byte floats -1000.0 to +1000.0 pascal_data = open('real.txt','rb').read() position_adjust = 1000 for x in range(START_RANGE,END_RANGE): offset = (x + position_adjust) * 6 s, = unpack('6s',pascal_data[offset:offset+6]) # create some source data if 0: print " +'\\x%02x\\x%02x\\x%02x\\x%02x\\x%02x\\x%02x' # %5.1f" % \ (ord(s[0]),ord(s[1]),ord(s[2]),ord(s[3]), ord(s[4]),ord(s[5]), x*1.0) r48 = s_reverse(s) v = '%6d: ' % x if 0: print v, s2bin(r48,' ') if 1: fmt = '%5.1f ' print v, print fmt % real48(r48), print fmt % real48_to_single(r48), print fmt % real48_to_double(r48) ++++++++++++++ History: On 14 Dec 2001 22:18:22 +1100, nigel at pc714.maths.usyd.edu.au wrote: >Ron Reidy <rereidy at indra.com> writes: > >> Martin Bless wrote: >> > >> > I have to convert those "good (?) old" 6-byte, "real48" floating point >> > numbers used in Borland's Pascal to nowadays "normal" floating points. >> > >> > Has is already been done? >> > >> > Martin >> You will have to do this using pack(). >> -- >> Ron Reidy >> Oracle DBA >> Reidy Consulting, L.L.C. > >The little-known xdrlib in the standard python distro should do >part of the job. > >http://www.python.org/doc/current/lib/module-xdrlib.html > >From what I can see in RFC1014 (http://www.faqs.org/rfcs/rfc1014.html) >the XDR and IEEE formats coincide for floating point. The real48 format >(http://www.borland.com/techpubs/delphi/delphi5/oplg/memory.html) >looks similar enough to IEEE that a bit of byte-shifting should do >the conversion, and xdrlib.pack_double(data) gives you your number. > >nigel >( at maths. usyd. edu. au )
- Previous message (by thread): reading 6-byte ("real48") Borland Pascal Floating Point numbers
- Next message (by thread): what is self._base?
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
More information about the Python-list mailing list