6

I'm trying to convert a floating point number to binary representation; how can I achieve this? My goal is, however, not to be limited by 2m so I'm hoping for something that could be easily extended to any base (3, 4, 8) ecc.

I've got a straightforward implementation so far for integers:

import string

LETTER = '0123456789' + string.ascii_lowercase
def convert_int(num, base):
    if base == 1:
        print "WARNING! ASKING FOR BASE = 1"
        return '1' * num if num != 0 else '0'

    if base > 36: raise ValueError('base must be >= 1 and <= 36')

    num, rest = divmod(num, base)
    rest = [LETTER[rest]]
    while num >= base:
        num, r = divmod(num, base)
        rest.append(LETTER[r])
    rest.reverse()
    return (LETTER[num] if num else '') + ''.join(str(x) for x in rest)

any help appreciated :)

edit:

def convert_float(num, base, digits=None):
    num = float(num)
    if digits is None: digits = 6

    num = int(round(num * pow(base, digits)))
    num = convert_int(num, base)
    num = num[:-digits] + '.' + num[:digits]
    if num.startswith('.'): num = '0' + num
    return num

is that right? why do i get this behaviour?

>>> convert_float(1289.2893, 16)
'509.5094a0'
>>> float.hex(1289.2983)
'0x1.42531758e2196p+10'

p.s. How to convert float number to Binary?

I've read that discussion, but I don't get the answer.. I mean, does it work only for 0.25, 0.125? and I dont understand the phrase 'must be in reverse order'...

Community
  • 1
  • 1
Ant
  • 5,151
  • 2
  • 26
  • 43
  • 2
    The answer to the previous question seems pretty obvious to me. Warning: float arithmetic with odd bases is generally not recommended. – Fred Foo Jan 29 '11 at 19:39
  • 1
    Do you want floating point numbers (which consists of `significant_digits` and an `exponent`) or a natural number with a "binary point" in between? Both are actually not that hard (once you grok the math), but different. –  Jan 29 '11 at 19:39

6 Answers6

17

For floats there is built-in method hex().

http://docs.python.org/library/stdtypes.html#float.hex

It gives you the hexadecimal representation of a given number. And translation form hex to binary is trivial.

For example:

In [15]: float.hex(1.25)
Out[15]: '0x1.4000000000000p+0'

In [16]: float.hex(8.25)
Out[16]: '0x1.0800000000000p+3'
stmi
  • 733
  • 4
  • 13
  • 4
    @stmi: how do you "translation form hex to binary" here for `'0x1.0800000000000p+3'? Thanks! ` – Qiang Li Oct 04 '11 at 22:43
  • 3
    @stmi: I mean of course a programmatic way, i.e. how to use python to translate `'0x1.0800000000000p+3'` to `'1000.01'` – Qiang Li Oct 05 '11 at 14:34
  • 6
    There should be a rule against "stating something is trivial". Especially when not explaining it. -1 – Romain Vincent Feb 14 '21 at 14:00
  • That's exactly the point of stating that, to say "I'm not going to explain that part, but it's easy so you can figure it out yourself with little work". – stmi Feb 14 '21 at 21:27
14

Next answer with a bit of theory.

Explanation below does not explain IEEE Floating Point standard only general ideas concerning representation of floating point numbers

Every float number is represented as a fractional part multiplied by an exponent multiplied by a sign. Additionally there is so called bias for exponent, which will be explained bellow.

So we have

  1. Sign bit
  2. Fractional part digits
  3. Exponent part digits

Example for base 2 with 8 bit fraction and 8 bit exponent

Bits in fraction part tell us which summands (numbers to be added) from sequence below are to be included in represented number value

2^-1 + 2^-2 + 2^-3 + 2^-4 + 2^-5 + 2^-6 + 2^-7 + 2^-8

So if you have say 01101101 in fractional part it gives

0*2^-1 + 1*2^-2 + 1*2^-3 + 0*2^-4 + 1*2^-5 + 1*2^-6 + 0*2^-7 + 1*2^-8 = 0.42578125

Now non-zero numbers that are representable that way fall between 2 ** -8 = 0.00390625 and 1 - 2**-8 = 0.99609375

Here the exponent part comes in. Exponent allows us to represent very big numbers by multiplying the fraction part by exponent. So if we have an 8bit exponent we can multiply the resulting fraction by numbers between 0 and 2^255.

So going back to example above let's take exponent of 11000011 = 195.

We have fractional part of 01101101 = 0.42578125 and exponent part 11000011 = 195. It gives us the number 0.42578125 * 2^195, this is really big number.

So far we can represent non-zero numbers between 2^-8 * 2^0 and (1-2^-8) * 2^255. This allows for very big numbers but not for very small numbers. In order to be able to represent small numbers we have to include so called bias in our exponent. It is a number that will be always subtracted from exponent in order to allow for representation of small numbers.

Let's take a bias of 127. Now all exponents are subtracted 127. So numbers that can be represented are between 2^-8 * 2^(0 - 127) and (1-2^-8) * 2^(255 - 127 = 128)

Example number is now 0.42578125 * 2^(195-127 = 68) which is still pretty big.

Example ends

In order to understand this better try to experiment with different bases and sizes for fractional and exponential part. At beginning don't try with odd bases because it only complicates things necessary.

Once you grasp how this representation works you should be able to write code to obtain representation of any number in any base, fractional/exponential part combination.

Brian Minton
  • 3,377
  • 3
  • 35
  • 41
stmi
  • 733
  • 4
  • 13
10

If you want to convert a float to a string with d digits after the decimal radix point:

  1. Multiply the number by base**d.
  2. Round to the nearest integer.
  3. Convert the integer to a string.
  4. Insert the . character d digits before the end.

For example, to represent 0.1 in base 12 with 4 decimal dozenal places,

  1. 0.1 × 124 = 2073.6
  2. Round to nearest integer → 2074
  3. Convert to string → 124A
  4. Add radix point → 0.124A
dan04
  • 87,747
  • 23
  • 163
  • 198
  • thanks! I think it is exactly what i was looking for; 1) can you explain why is works? and 2) I'm having trouble with that..can you take a look ath the edits of my answer? thanks ;) – Ant Jan 30 '11 at 09:44
  • awesome solution. Would be nice to know how to do the final adding of the radix point. instead of 1.74a3 I get 174a3 – Norman Bird May 11 '16 at 13:09
1

This isn't the same style of binary representation that you want, but this will convert an IEEE 754 into it's sign, mantissa and base, which can be used to create a hex representation in a fairly straightforward fashion. Note that the 'value' of the mantissa is 1+BINARY, where BINARY is the binary representation - hence the -1 in the return.

I wrote this code and am declaring it public domain.

    def disect_float(f):
      f = float(f); #fixes passing integers
      sign = 0x00; #positive
      exp = 0;
      mant = 0;
      if(f < 0): #make f positive, store the sign
        sign = '-'; #negative
        f = -f;
      #get the mantissa by itself
      while(f % 1 > 0): #exp is positive
        f*=2
        exp+=1
      #while(f % 1 > 0):
      tf = f/2;
      while(tf % 1 <= 0): #exp is negative
        exp-=1;
        f=tf;
        tf=f/2;
        if(exp < -1024): break;
      mant=int(f);
      return sign, mant-1, exp;
1

There is one trick that i observed that we can do using simple string manipulations. I felt this method to be simpler than other methods that i came across.

s = "1101.0101"
s1, s2 = s.split(".")
s1 = int(s1, 2)
s2 = int(s2, 2)/(2**len(s2))
x = s1+s2
print(x)

Output :

13.3125

Hope it will be helpful to someone.

kuldeep725
  • 19
  • 4
  • Nice, but that's the opposite of what's asked, and not the standard way to store binary float data. `>>> bin(3125) '0b110000110101'` – Cees Timmerman Mar 18 '21 at 12:52
0

Answering the title directly and using float.hex, which uses 64bit IEE754, one could write this method:

def float_to_bin(x):
  if x == 0:
    return "0" * 64
  w, sign = (float.hex(x), 0) if x > 0 else (float.hex(x)[1:], 1)
  mantissa, exp = int(w[4:17], 16), int(w[18:])
  return "{}{:011b}{:052b}".format(sign, exp + 1023, mantissa)

float_to_bin(-0.001) # '1011111101010000000010110011111101011000011011100110110100101010'

Note however, this does not work for NaN and Inf.

Ozbolt
  • 133
  • 1
  • 5