Start New Discussion within our Software Development Community

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 :)

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)

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

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
"""

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))

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'

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.

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.

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

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.

can it write 24bit floats?? :P

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

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))

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.

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
This question has already been answered. Start a new discussion instead.