interpret 4 byte as 32-bit float (IEEE-754)
Bengt Richter
bokr at oz.net
Sun Jan 16 00:55:22 EST 2005
On Sat, 15 Jan 2005 11:00:36 -0800, Scott David Daniels <Scott.Daniels at Acm.Org> wrote:
>G.Franzkowiak wrote:
>> Scott David Daniels schrieb:
>>
>>> franzkowiak wrote:
>>>
>>>> I've read some bytes from a file and just now I can't interpret 4
>>>> bytes in this dates like a real value. An extract from my program:
>>>> def l32(c):
>>>> return ord(c[0]) + (ord(c[1])<<8) + (ord(c[2])<<16) +
>>>> (ord(c[3])<<24)
>>>> ...
>>>> value = l32(f.read(4)) <--- 3F 8C CC CD should be 1.11
>>>>
>>> OK, here's the skinny (I used blocks & views to get the answer):
>>>
>>> import struct
>>> bytes = ''.join(chr(int(txt, 16)) for txt in '3F 8C CC CD'.split())
>>> struct.unpack('>f', bytes)
>>>
>>> I was suspicious of that first byte, thought it might be an exponent,
>>> since it seemed to have too many on bits in a row to be part of 1.11.
>>>
>>> -Scott David Daniels
>>> Scott.Daniels at Acm.Org
>>
>>
>> Ok, I the string exist with "mystr = f.read(4)" and the solution for
>> this case is in your line "struct.unpack('>f', bytes)"
>> But what can I do when I want the interpret the content from the Integer
>> myInt (*myInt = 0x3F8CCCCD) like 4-byte-real ?
>> This was stored with an othes system in a binary file to
>> CD CC 8C 3F and now is it in python in value. The conversion is not
>> possible. It's right... one of this bytes is an exponent.
>> I want copy the memory content from the "value address" to "myReal
>> address" and use print "%f" %myReal.
>> Is myReal then the right format ?
>> What can I do with python, in FORTH is it simple
>> ( >f f. )
>>
>> gf
>>
>>
>>
>If you really want to do this kind of byte fiddling:
> http://members.dsl-only.net/~daniels/block.html
>
>Then:
> from block import Block, View
> b = Block(4) # enough space for one float (more is fine)
> iv = View('i', b) # getting to it as an integer
> fv = View('f', b) # same memory as floating point
> iv[0] = 0x3F8CCCCD # Here is a sample just using the integer
> print fv[0]
>
>On an Intel/Amd/Generic "PC" machine, you should get 1.1
>
Ok, for most bit patterns (except QNANs), you can do it in pure python:
----< i32as_single.py >------------------------------------
class NoQNAN(ValueError): pass # use to flag inability to return QNANs
def i32as_single(i):
"""
(UIAM in my interpretation of a 1995 Pentium Processor Developer's Manual)
This converts bits in a 32-bit integer as if they were bits
of a single-precision IEEE 754 floating point number to python float
+---+---+---+---+---+---+---+---+---+---+---+---+ ... +---+---+---+---+
| s | e e e e e e e eb| b b b ... b b b b0 |
+---+---+---+---^---+---+---+---^---+---+---+---^ ... ^---+---+---+---+
31 30 29 28 27 26 25 24 23 22 21 20 3 2 1 0
where s is the sign bit, and is the only thing that changes between + and -
and e..eb is an 8-bit biased (by 127) exponent, and eb is the hidden
unit 1 bit followed by 23 b..b0 significant "fraction" bits",
normalized so that the most significant bit is always 1 and therefore
doesn't have to be stored at eb, except that when all but the sign bit
are zero, eb is ignored and the value is then a signed zero value.
The binary fraction starting bit is after the hidden '1' bit eb at 23,
so viewing bits 0..23 as an integer, we have to divide by 2**23 (or
adjust the exponent) to get the 1.xxxx values when the official unbiased
exponent is zero (offset value 127). Thus 0x3f800000 is zero unbiased
exponent and no other bits, for a value of 1.0
A biased exponent of 255 signifies a NaN, and a biased exponent of
zero signifies a denormal (which doesn't have a hidden bit, and whose
unit bit is bit 22). Single precision denormals can be normalized
in python (double) float format, which is done for the return value.
"""
signbit = i&0x80000000
if not i&0x7fffffff: return signbit and -0.0 or 0.0 # if -0.0 available
biased_exp = (i>>23) & 0xff # bits 30-23
unitbit = biased_exp and 0x800000 or 0 # bit 23, or 0 for denormals
significand = i & 0x7fffff # bits 22-0
if biased_exp == 255:
if significand:
raise NoQNAN, "Sorry, can't generate QNAN from %08x" % i
return signbit and -1e9999 or 1e9999 # XXX s/b INF's for sure??
adjexp = (biased_exp or 1) - 127 - 23 # adjusted for denormal
posvalue = (significand + unitbit)*2.0**adjexp
return signbit and -posvalue or posvalue
def test():
import struct
num = 0
for sign in (0, 2*(-2**30)):
for i3 in xrange(128):
num3 = i3<<24
for i2 in xrange(256):
print '\r%08x'%(num,), # show progress
num2 = i2<<16
# skip mid byte of significand, make 0
# and twiddle only a few bits at the bottom
for num0 in xrange(8):
num = sign+num3+num2+num0
s = ''.join(map(chr,(num0, 0, i2,((sign and 0x80 or 0)+i3))))
try: ti32as = i32as_single(num)
except NoQNAN: continue
tstruct = struct.unpack('f', s)[0] # XXX '<f' => no INF ??
if ti32as != tstruct:
print '\n%x =>%r\n%r => %r' % (num, ti32as, s, tstruct)
if __name__ == '__main__':
test()
-----------------------------------------------------------
[21:47] C:\pywk\clp>i32as_single.py
C:\pywk\clp\i32as_single.py:31: FutureWarning: hex/oct constants > sys.maxint will return positi
ve values in Python 2.4 and up
signbit = i&0x80000000
7fff0007C:\pywk\clp\i32as_single.py:51: FutureWarning: %u/%o/%x/%X of negative int will return a
signed string in Python 2.4 and up
print '\r%08x'%(num,), # show progress
ff7f0007C:\pywk\clp\i32as_single.py:38: FutureWarning: %u/%o/%x/%X of negative int will return a
signed string in Python 2.4 and up
raise NoQNAN, "Sorry, can't generate QNAN from %08x" % i
fffe0007
Of course, the test() gives a clue how you might write the whole thing using struct
by just summing the four chr-ed extracted bytes from the input i ;-)
Anyway, a couple things (how to make a QNAN in python without calling struct, and '<' in struct):
>>> from i32as_single import i32as_single as i32f
>>> i32f(0x3f8ccccd)
1.1000000238418579
>>> i32f(0x00000000)
0.0
>>> i32f(0x7f800000)
1.#INF
>>> i32f(0xff800000)
-1.#INF
But I don't know how to build QNaNs:
>>> i32f(0x7f800001)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "i32as_single.py", line 38, in i32as_single
raise NoQNAN, "Sorry, can't generate QNAN from %08x" % i
i32as_single.NoQNAN: Sorry, can't generate QNAN from 7f800001
Whereas struct does:
>>> import struct
>>> struct.unpack('f','\x00\x00\x80\x7f')[0]
1.#INF
>>> struct.unpack('f','\x01\x00\x80\x7f')[0]
1.#QNAN
BTW, your example:
>>> struct.unpack('f','\xcd\xcc\x8c\x3f')[0]
1.1000000238418579
>>> i32f(0x3f8ccccd)
1.1000000238418579
But what does this mean? (I wanted to test with little endian unpacking, since
that is what I knew I created, but that isn't what '<' means?? ...)
>>> struct.unpack('f','\x00\x00\x80\x7f')[0]
1.#INF
>>> struct.unpack('<f','\x00\x00\x80\x7f')[0]
3.4028236692093846e+038
Regards,
Bengt Richter
More information about the Python-list
mailing list