1.11M Members

floating point arithmatic, WTF am I doing wrong?

 
0
 

lol don't take any offence to the title XD
just being funny :P

anyways...
I'm having a problem with my function...

it's supposed to allow you to read/write float values in any specified byte length.
but there's a problem with the output value >_>

first off though I must state that the equation for processing the exponent field works perfectly for the pre-defined float types:
[ S, E, M ]
[ 1, 3, 4 ] - 8bit
[ 1, 5, 10 ] - 16bit
[ 1, 8, 23 ] - 32bit
[ 1, 11, 52 ] - 64bit

going along with these standards, IDK if the odd types such as 24bit or 48bit are correct.

anyways...
on with the code :P

def f(byte_cnt,val):
    e=int(((byte_cnt*8)-1)/(byte_cnt+1)+(byte_cnt/2 if byte_cnt>2 else 0))

    S,E,M=(val>>((byte_cnt*8)-1))&1,(val>>((byte_cnt*8)-(e+1)))&int('1'*e,2),val&int('1'*((byte_cnt*8)-(e+1)),2)

    print str(byte_cnt*8)+'bits [[1],['+str(e)+'],['+str(((byte_cnt*8)-1)-e)+']]: '+\
          str(S)+' '+('0'*(e-len(bin(E).replace('0b',''))))+bin(E).replace('0b','')+' '+\
          ('0'*(((byte_cnt*8)-(e+1))-len(bin(M).replace('0b',''))))+bin(M).replace('0b','')

    return (pow(-1,S)*(pow(2.0,(E-int('1'*(e-1),2)))*float(('1.' if E!=0 else '0.')+str(M))) if E<int('1'*e,2) else 'NAN') #problem on this line

usage:
f( byte_length, int(float_hex,16) )

IDK what I'm doing wrong, as I've followed the notes of about 8 sites D:
the returned binary values are correct, but the float is not...

anyone mind lending a hand?? :/
many thanx in return :)

 
0
 

We need test cases input/correct output to help you out, if I catch your meaning, mantissa has assumed 1 as first number except when exponent is 0. What is offset of exponent. I would rewrite your nice formulas by writing one function to pop n bits out from least significant side and working only with numbers, not strings.

def push_bits(n, bits, value):
    return value << n | bits

def pop_bits(n, value):
    return value >> n, value & ((1<<n) -1)

value = 0b11
value = push_bits(3, 0b101, value)
print bin(value)

bits, rest = pop_bits(3, value)
print bin(bits), bin(rest)
 
0
 

yeh... I've tried to keep string usage to a minimum >_<
just couldn't find an easy way to do float('1.'+str(mantissa)) w/o string usage

I'm afraid I'm not quite sure of your meaning to the offset of the exponent :/

e = length of exponent field
shift = (byte_size*8) - (1+e)

E = (val>>shift) & int('1'*e,2)

I already know the ints returned from the input value are correct,
as the binary values are regenerated from the ints (S, E, and M).

NTM I've tested a few examples and compaired my binary values with the example's representations.

here's a few of my tests with the comparison floats:
(what the returned values should be)

f(1,69) = 2.625

f(1,211) = -4.75

f(2,49152) = -2.0

f(2,15361) = 1.0009765625

f(4,1065353216) = 1.0

f(4,3299092992) = -1313.3125

EDIT: only -2.0 and 1.0 are correct

 
0
 

if it helps you out, these were the best notes I could find as to floating-point notation and encoding.
http://en.wikipedia.org/wiki/Half-precision_floating-point_format

MAN do I hate this editor >:O
(I have a connection that cuts every 5 seconds after connecting)
^this editor sometimes misses a paragraph or 2 >_<

 
0
 

I have a starting point using module bitstring (from pypi) and the wikipedia page

import bitstring

test_data = [ 
    ("0 01111 0000000000", 1),
    ("0 01111 0000000001", 1.0009765625),
    ("1 10000 0000000000", -2),
    ("0 11110 1111111111", 65504),
    ("0 00001 0000000000", 2.0 ** (-14)),
    ("0 00000 1111111111", 2.0**(-14) - 2.0**(-24)),
    ("0 00000 0000000001", 2.0**(-24)),
    ("0 00000 0000000000", 0.0),
    ("1 00000 0000000000", -0.0),
    ("0 11111 0000000000", float("infinity")),
    ("1 11111 0000000000", -float("infinity")),
    ("0 01101 0101010101", 1.0/3),
]

fmt = ['uint:1', 'uint:5', 'uint:10']

for u, res in test_data:
    a, b, c = (int(x, 2) for x in u.split())
    s = bitstring.pack(fmt, a, b, c)
    print s.bin
    s, e, m = s.unpack(fmt)
    if e:
        v = 2 ** (e - 25) * (1024 + m)
    else:
        v = 2 ** (-24) * m
    if s:
        v = -v
    print repr(v), repr(float(res))

"""my output -->
0011110000000000
1.0 1.0
0011110000000001
1.0009765625 1.0009765625
1100000000000000
-2.0 -2.0
0111101111111111
65504 65504.0
0000010000000000
6.103515625e-05 6.103515625e-05
0000001111111111
6.097555160522461e-05 6.097555160522461e-05
0000000000000001
5.960464477539063e-08 5.960464477539063e-08
0000000000000000
0.0 0.0
1000000000000000
-0.0 -0.0
0111110000000000
65536 inf
1111110000000000
-65536 -inf
0011010101010101
0.333251953125 0.3333333333333333
"""
 
0
 

Here Gribouillis data and formula for decision of value used with my push_bits and pop_bits if you need to avoid external dependencies. I also left as formulas some Gribouillis constants for easier adaption later for other lengths

def push_bits(n, bits, value):
    return value << n | bits

def pop_bits(n, value):
    return value >> n, value & ((1<<n) -1)

def b(n):
    return ('0' * 16 + bin(n)[2:])[-16:]

def f(val):
    bias, exp, mant = 15, 5, 10

    v, m = pop_bits(mant, val)
    v, e = pop_bits(exp, v)
    v, s = pop_bits(1, v)

    if e == (1<<exp)-1:
        if m:
            return float('NaN')
        else:
            return float('-inf') if s else float('+inf')

    if e:
        v = 2 ** (e - bias - mant) * ((1<<mant) + m)
    else:
        v = 2 ** (1 - bias - mant) * m

    return -v if s else v

for t in [15360, 15361, 49152, 31743, 1024, 1023, 1, 0, 32768, 31744, 64512, 13653]:
    print b(t), repr(f(t))
 
0
 

ahah!
I think I just got what I needed:

    if e == (1<<exp)-1:
        if m:
            return float('NaN')
        else:
            return float('-inf') if s else float('+inf')

    if e:
        v = 2 ** (e - bias - mant) * ((1<<mant) + m)
    else:
        v = 2 ** (1 - bias - mant) * m

    return -v if s else v

but hang on, lemme verify first...

EDIT:
btw, I believe 'pow(-1,s)*v' is faster than '-v if s else v'

 
0
 

pyTony =D
you've done it again ^_^

>>> f(1,69)
8bits [[1],[3],[4]]: 0 100 0101
2.625
>>> f(1,211)
8bits [[1],[3],[4]]: 1 101 0011
-4.75
>>> f(2,49152)
16bits [[1],[5],[10]]: 1 10000 0000000000
-2.0
>>> f(2,15361)
16bits [[1],[5],[10]]: 0 01111 0000000001
1.0009765625
>>> f(4,1065353216)
32bits [[1],[8],[23]]: 0 01111111 00000000000000000000000
1.0
>>> f(4,3299092992)
32bits [[1],[8],[23]]: 1 10001001 01001000010101000000000
-1313.3125
>>> 

thank you :)
that was all I needed.

 
0
 

now I just need to get floats back into 3 ints

 
0
 

You might find it interesting to look for the hex representation of the float by it's hex() method. Or then it could be just confusing.

 
0
 

I have :P
I'd rather use reverse engineering instead of trying to work with that mess >_<

 
0
 

I once wrote this function to get the ieee754 representation of a float

def ieee754(x):
    """Return a string of 0 and 1 giving the ieee754 representation of the float x
    """
    import struct
    from binascii import hexlify
    p = struct.pack("d", x)
    s = bin(int(b"1" + hexlify(p), 16))[3:]
    return " ".join(reversed([s[i:i+8] for i in xrange(0, len(s), 8)]))

The following test shows that the 16 bits version is very close to the 64 bits version. It means that you should be able to find the 3 numbers easily

0011110000000000
00111111 11110000 00000000 00000000 00000000 00000000 00000000 00000000
1.0 1.0
0011110000000001
00111111 11110000 00000100 00000000 00000000 00000000 00000000 00000000
1.0009765625 1.0009765625
1100000000000000
11000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
-2.0 -2.0
0111101111111111
01000000 11101111 11111100 00000000 00000000 00000000 00000000 00000000
65504 65504.0
0000010000000000
00111111 00010000 00000000 00000000 00000000 00000000 00000000 00000000
6.103515625e-05 6.103515625e-05
0000001111111111
00111111 00001111 11111000 00000000 00000000 00000000 00000000 00000000
6.097555160522461e-05 6.097555160522461e-05
0000000000000001
00111110 01110000 00000000 00000000 00000000 00000000 00000000 00000000
5.960464477539063e-08 5.960464477539063e-08
0000000000000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
0.0 0.0
1000000000000000
10000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
-0.0 -0.0
0111110000000000
01000000 11110000 00000000 00000000 00000000 00000000 00000000 00000000
65536 inf
1111110000000000
11000000 11110000 00000000 00000000 00000000 00000000 00000000 00000000
-65536 -inf
0011010101010101
00111111 11010101 01010100 00000000 00000000 00000000 00000000 00000000
0.333251953125 0.3333333333333333

Apparently, removing bits [2, 8[ and truncating brings back the 16 bits float.

 
0
 

Happy log(n, 2)ing then!

 
0
 

can it write 24bit floats?? :P

that's what I'm trying to do...
write floats of the specified byte length

 
0
 

Here is something of "More than you ever wanted to know about floating point numbers" http://www.quadibloc.com/comp/cp02.htm Not helping so much at task at hand but looks really comprehensive computer science basics document for interest and fun.

 
0
 

To post a workin version of multiprecission code for benefit of others, here my function with bits argument instead of bytes (to avoid those 8*byte_cnt parts) Hope it is without bugs, looks to work with the test cases. (result for f(0x7ffeffffffffffffffffffffffffffff, 128) is correct but not maybe what you would expect)

def push_bits(n, bits, value):
    return long(value << n) | long(bits)

def pop_bits(n, value):
    return value >> n, value & ((1<<n) -1)

def b(n, c=16):
    if n < 0:
        # two's complement
        n = ((2**c-1) ^ n) + 1
    return ('0' * c + bin(n)[2:])[-c:]

def f(val, bits=16):
    if bits == 80:
        exp = 15
    else:
        byte_cnt = bits // 8
        exp = (byte_cnt * 8 - 1)//(byte_cnt + 1) + (byte_cnt > 2) * byte_cnt // 2
    mant = bits - 1 - exp
    bias = (1<<(exp-1))-1
    print exp, mant, bias

    v, m = pop_bits(mant, val)
    v, e = pop_bits(exp, v)
    v, s = pop_bits(1, v)

    if e == bias:
        if m:
            return float('NaN')
        else:
            return float('-inf') if s else float('+inf')

    if e:
        v = 2 ** (e - bias - mant) * ((1<<mant) + m)
    else:
        v = 2 ** (1 - bias - mant) * m

    return -v if s else v

for t in [15360, 15361, 49152, 31743, 1024, 1023, 1, 0, 32768, 31744, 64512, 13653]:
    print b(t), repr(f(t))
 
0
 

how does yor code calculate the length of the exponent??

you saw my code, but does yours return the same bit-fields as mine??
http://lh3.ggpht.com/-modh5yIANmA/T6CDQ-67LHI/AAAAAAAADyQ/snPDH-mtSZM/s812/floats.PNG
^just an earlier test I did while developing the equasion :P

but then again, IDK if the format standards for the bit fields may change compaired to mine >_>
(since IEEE 24bit hasn't been dev'd yet)

 
0
 

Your formula, only simplified the integer division and tried to deal with any length not only fixed number of bytes, exactly your results come better with formula:

byte_cnt = bits // 8
exp = ((byte_cnt * 8) - 1)//(byte_cnt + 1)+ (byte_cnt > 2) * byte_cnt // 2

I fixed it in my post.

 
0
 

btw, I believe 'pow(-1,s)*v' is faster than '-v if s else v'

Interesting, but I can not reproduce this result, maybe you could send your data?

import random
import time

ints = [random.randint(0, 1<<62) for count in range(10**5)]
signs = [random.randint(0, 1) for count in range(10**5)]

print('starting test run')
t0= time.clock()
signed1 = [(-1)**s*v for s, v in zip(signs, ints)]
t1= time.clock()
signed2 = [-v if s else v for s, v in zip(signs, ints)]
t2 = time.clock()
print 'if', (t2-t1),'vs', t1-t0, '**'
print ((t2- t1) - (t1- t0))/(t2-t1) * 100, '% speedup'

Output

starting test run
if 0.094547212006 vs 0.183539248703 **
-94.1244430262 % speedup
 
0
 

80 bits has exponent of 15 bits and looks like it has one additional integer part bit in format and is not handled perfectly by the function (http://en.wikipedia.org/wiki/Extended_precision)

You
Post:
Start New Discussion
Tags Related to this Article