Convert Float to Python Binary

Asked

Viewed 1,132 times

2

I’m studying the IEEE 754 standard, and I should create a new 16-bit method. This function is to convert the mantissa that is already in format 1,xxx*2 yyy

What may have gone wrong in this code, which seems right to me but this conversion does not knock when I do the opposite way.

def convert(num):
  final = []*16
  bits = []
  cont_exp = 0
  #define se o sinal é positivo ou negativo
  if(num>0):
    sinal = 0
  else:
    sinal = 1
    num = num*-1
  #caso maior que 2, dividir até conseguir o modelo 1,xxx*2^xx
  if (num>=2):
    while (num>=2):
      cont_exp += 1
      num = num/2
    #print("exp polarizado " + str(polariza(cont_exp)))
    cont_exp = converte(polariza(cont_exp))
    if num > 1:
      num = num - 1 
    num = num*2
    bits.append(str(1 if num > 1 else 0))
    while(num != 0):
      if num > 1:
        num = num - 1
        num = num*2
        bits.append(str(1 if num > 1 else 0))
      elif num < 1:
        num = num*2
        bits.append(str(1 if num > 1 else 0))
      else:
        num = num - 1
        bits.append(str(1 if num > 1 else 0))
    if len(bits) < 10:
      bits = normaliza(bits)
      final = str(sinal) + " " + str(cont_exp) + " " + ''.join(bits)
      return final
      #return final
    else:
      print ("overflow")
      #caso menor que 1, multiplicar até conseguir o modelo 1,xxx*2^xx
  elif (num < 1):
    while (num<1):
      cont_exp += 1
      num = num*2
    cont_exp = converte(polariza(cont_exp*-1))
    if num > 1:
      num = num - 1 
    num = num*2
    bits.append(str(1 if num > 1 else 0))
    while(num != 0):
      if num > 1:
        num = num - 1
        num = num*2
        bits.append(str(1 if num > 1 else 0))
      elif num < 1:
        num = num*2
        bits.append(str(1 if num > 1 else 0))
      else:
        num = num - 1
        bits.append(str(1 if num > 1 else 0))
    if len(bits) < 10:
      bits = normaliza(bits)
      final = str(sinal) + " " + str(cont_exp) + " " + ''.join(bits)
      return final
    else:
      print ("overflow")

      #caso no modelo 1,xxx*2^xx, converter
  else:
    cont_exp = converte(polariza(0))
    if num > 1:
      num = num - 1 
    num = num*2
    bits.append(str(1 if num > 1 else 0))
    while(num != 0):
      if num > 1:
        num = num - 1
        num = num*2
        bits.append(str(1 if num > 1 else 0))
      elif num < 1:
        num = num*2
        bits.append(str(1 if num > 1 else 0))
      else:
        num = num - 1
        bits.append(str(1 if num > 1 else 0))
    if len(bits) < 10:
      bits = normaliza(bits)
      final = str(sinal) + " " + str(cont_exp) + " " + ''.join(bits)
      return final
    else:
      print ("overflow")

1 answer

2


Hello - let’s split -

first

final = []*16

Does nothing - a Python list is not an "array" - it does not have a number of fixed slots, and can grow dynamically with the methods append and extend. If you multiply an empty list by 16, continue with an empty list - not with something equivalent to an array with 16 positions.

Since you are always using "append" this problem, although conceptual, does not disturb your code, but indicates that you have not experienced the constructs you are doing in interactive mode. The interactive environment is one of the strengths of Python - it is very worth testing expressions and excerpts in a direct way, and only then write the expression in the program - we already do this with the certainty that that little piece will work.

 #caso maior que 2, dividir até conseguir o modelo 1,xxx*2^xx
 if (num>=2):
    while (num>=2):
      cont_exp += 1
      num = num/2

OK - this code normalizes the exponent - I think I understand - anyway, Oce practically triples the length of its function, being that the contents of the most external if clavicles for numbers smaller than 1, etc...is pretty much the same you could put only that stretch in the most external if, instead of making the function 3 times larger with repeated code - this induces even errors - I return to that more external if below.

You do not put in your question, nor does it put what function polariza makes - but as it only works in the exponent, let’s go ahead ...

if num > 1:
  num = num - 1 
num = num*2
bits.append(str(1 if num > 1 else 0))
while(num != 0):
  if num > 1:
    num = num - 1
    num = num*2
    bits.append(str(1 if num > 1 else 0))
  elif num < 1:
    num = num*2
    bits.append(str(1 if num > 1 else 0))
  else:
    num = num - 1
    bits.append(str(1 if num > 1 else 0))

This excerpt from the codex is weird for some reasons- well, you can understand that you want to test the most significant bit always, and concatenate the value of it in your bit string.

But the test is strange: if the number is a multiple of any 2 power greater than a certain one, your "num" will reach zero before you have all the bits - and then you’ll need to use a chunk of code below to try to solve this.

Another: you already take the test if num > 1: - do not have to test again when giving the bits.append - in each append, just make bits.append("1") or bits.append("0") (also if you already know you want to paste the strings "1" and "0", there is no need to write an integer and call "str", you can write the string "1" straightforward).

Another strange thing is that your "if" has three clauses - either the number is greater than or equal to 1 or is less than 1 - in the first case, the bit you want has value "1". You don’t need a special case when the number is exactly "1" -note that even with the other observations I’ve already made, in case the number is exactly "1", (num - 1) * 2 remains "0", the extra multiplication won’t change your result at all (and at this level of code, you shouldn’t be worrying about performance - you’re operating 4 to 5 orders of magnitude less efficient than you could if performance mattered here - part of it being using a very high-level language like Python, part on account of the aproach you have chosen)

Another strange part of this code is that you have an "if" clause with code equal to what goes inside the while out of the while, before the block - code unnecessarily duplicated.

Instead, if you already know you want to extract the 16 most significant bits, count to 16, and simply write down the bits that are becoming more signigicative - I will break into a smaller function, which already receives the number with normalized exponent:

def get_most_significant_bits(num, bits=16):
    result = []
    for i in range(bits):
        result.append("1" if num >= 1 else "0")
        if num >= 1:
            num -= 1
        num *= 2
    return result

I therefore respond to the above observations. Now continuing your code:

 if len(bits) < 10:
     bits = normaliza(bits)
     final = str(sinal) + " " + str(cont_exp) + " " + ''.join(bits)
     return final
 else:
     print ("overflow")

I don’t understand why you compare the len(bits) with 10 - did not want to extract 16 bits of mantissa² Ai - again you do not put the function normaliza - I’m guessing she adds "0" s to the bit string until it reaches the length of 10 digits. Despite all the above observations, the code seems to be right so far - so, your error may be precisely in the function "normalizes".

Another thing, string formatting: Python is an extremely versatile language for working with strings, maintaining readability - and that of opening and closing quotes with a single space and using "+" and explicit conversions to str absolutely not necessary - If you are using version 3.6 or later, you can create the same result with "f-strings" - strings that allow Python expressions between Python signals { } - your formatting would look like this:

final = f"{sinal} {cont_exp} {''.join(bits)}"

In Python 3.5 or below, you could use the ". format method":

final = "{} {} {}".format(sinal, cont_exp, "".join(bits)) 

Continuing - we analyze the first third of your function, but really, you have two more blocks that are almost exactly the same, changing only the exponent calculation - then as I already did above, where refactoring to the extressure of the mantissa work with normalized numbers, we will make the calculation of the separate exponent:

def get_exponent_and_normalized(num):
    # final number should be 0 < num < 2, and the corresponding FP exponent
    exponent = 0
    while True:
        if num < 1:
            exponent -= 1
            num *= 2
        elif num >= 2:
            exponent += 1
            num /= 2
        else:
            break
    return exponent, num

Note that I am using Python’s automatic tuple construction feature with "," in Return, to actually have a function that returns two values.

Well, we now have a generic way of catching the exponent, and the mantissa - without replicating the code under three "if" conditions. We could have a similar little function just to pick up the signal - but in this case, it would be like the code you’ve already done. So, without needing to "polarize" and "normalize" - you can make the complete code equivalent to yours with:

def convert(num):
    if(num>0):
        sinal = 0
    else:
        sinal = 1
        num = num*-1
    cont_exp, normalized_num = get_exponent_and_normalized(num)
    bits = get_most_significant_bits(normalized_num)
    final = final = "{} {} {}".format(sinal, cont_exp, "".join(bits)) 
    return final


def get_most_significant_bits(num, bits=16):
    result = []
    for i in range(bits):
        result.append("1" if num >= 1 else "0")
        if num >= 1:
            num -= 1
        num *= 2
    return result

def get_exponent_and_normalized(num):
    # final number should be 0 < num < 2, and the corresponding FP exponent
    exponent = 0
    while True:
        if num < 1:
            exponent -= 1
            num *= 2
        elif num >= 2:
            exponent += 1
            num /= 2
        else:
            break
    return exponent, num

then - the code was well modular, easier to verify - and, is working with the examples I tested here:

Unfortunately, despite dissecting carefully, I could not find the "point" in which your error code. It can be in the function "normalizes". Or it can be hard to find because your code has enough unnecessary duplication - which is why we always avoid duplicating code. Whenever you have programming and find uqe need to write a snippet - not even large - of similar code, see if there is no way to do a separate function. In this case it was not necessary to call the function more than once - but let’s assume that it was not so trivial to leave that stretch free of the more external "if" - within each part of the "if", the entire body of the internal while and if would be exchanged for a single line by calling the external function.

Another approach

In general, when we want to "dissect" a floating point number, we are not doing several floating point operations with the same - the ideal. is to treat a nḿero in FP as what it is: a binary structure with a bit with very specific roles .

With operators for bits & ("and binary") and >> and << - shift left and shift right, we get the exact bits of an IEE 754 using bit masks - In Python you can use a trick with the "struct" module to get a 64-bit "unteiro" number that is the binary copy of a floating point number - for this, you "encodes" the number in floating point, and from created bytes object, "decodes" a no-signal 64-bit integer. In the latter, the above operators can be used without side effects:

import struct
num = 2.125
string = struct.pack('<d', num)
integer = struct.unpack('<Q', string)[0]

Here is an example of how to "translate" a float64 to a Python uint64 - from there you can use the above bit manipulation operators to extract the binary fields from the number. Then just see in the specification and see that the float has 1 signal bit, 10 exponent bits and 53 significant bits.

Since you’re investigating and learning - this approach is pretty cool too,.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.