Convert float.hex () value to binary in Python

I am wondering how to convert the result returned by float.hex() to a binary file, for example, from 0x1.a000000000000p+2 to 110.1 .

Can anybody help? Thanks.

+7
source share
4 answers
 def float_to_binary(num): exponent=0 shifted_num=num while shifted_num != int(shifted_num): shifted_num*=2 exponent+=1 if exponent==0: return '{0:0b}'.format(int(shifted_num)) binary='{0:0{1}b}'.format(int(shifted_num),exponent+1) integer_part=binary[:-exponent] fractional_part=binary[-exponent:].rstrip('0') return '{0}.{1}'.format(integer_part,fractional_part) def floathex_to_binary(floathex): num = float.fromhex(floathex) return float_to_binary(num) print(floathex_to_binary('0x1.a000000000000p+2')) # 110.1 print(floathex_to_binary('0x1.b5c2000000000p+1')) # 11.01101011100001 

Explanation

float.fromhex returns float num . We would like its binary representation.

{0:b}.format(...) returns binary representations of integers, but does not float.

But if we multiply the float by a sufficient power of 2, i.e. we shift the binary representation to sufficiently left places, we get an integer shifted_num .

As soon as we have this integer, we are free from home, because now we can use {0:b}.format(...) .

We can re-insert the decimal point (err, binary point?) Using a string cut bit based on the number of places we left shifted ( exponent ).

Technical point. The number of digits in the binary representation of shifted_num may be less than exponent . In this case, we need to insert a binary representation with a large number 0 on the left, so the binary slicing with binary[:-exponent] will not be empty. We control this with '{0:0{1}b}'.format(...) . 0{1} in the format string sets the width of the formatted string to {1} , padded with zeros on the left. ( {1} is replaced by the number exponent .)

+10
source

Note that the binary form 0x1.a000000000000p+2 not 101.1 (or more precisely 0b101.1 )
but 0b110.1 (in my Python 2.7, binary numbers display like this)

.

The first , useful instance method float float.hex() and its inverse function, class method float float.fromhex()

 fh = 12.34.hex() print fh print float.fromhex(fh) 

result

 0x1.8ae147ae147aep+3 # hexadecimal representation of a float 12.34 

"Note: float.hex () is the instance method, and float.fromhex () is the class method."

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

.

Secondly, I did not find a Python function for converting the hexadecimal representation of the float to the binary representation of this float, that is, with a dot (and not with one to directly convert the decimal representation of the float to a binary file).

So, I created a function for this purpose.

Before processing, this function converts the hexadecimal representation to the decimal representation (string) of the input float.

Then there are two problems:

  • How to transform a part before a point?
    This part is an integer, its easy to use bin()

  • how to convert part after point?
    The problem of this conversion was asked several times on SO, but I did not understand the solutions, so I wrote my own.

Then, here is the function you want, Qiang Li:

 def hexf2binf(x): '''Transforms an hexadecimal float with a dot into a binary float with a dot''' a,_,p = str(float.fromhex(x)).partition('.') # the following part transforms the part after the dot into a binary after the dot tinies = [ Decimal(1) / Decimal(2**i) for i in xrange(1,400)] bits = [] pdec = Decimal('.'+p) for tin in tinies: if pdec-tin==0: bits.append('1') break elif pdec-tin>0: bits.append('1') pdec -= tin else: bits.append('0') pbin = ''.join(bits) # it the binary after the dot # the integer before the dot is easily transformed into a binary return '.'.join((bin(int(a)),pbin)) 

.

To check, I wrote a function to convert the portion of the binary float after the period to its decimal representation:

 from decimal import Decimal, getcontext() getcontext().prec = 500 # precision == 500 , to be large ! tinies = [ Decimal(1) / Decimal(2**i) for i in xrange(1,400)] com = dict((i,tin) for i,tin in enumerate(tinies,1)) def afterdotbinary2float(sbin, com = com): '''Transforms a binary lying after a dot into a float after a dot''' if sbin.startswith('0b.') or sbin.startswith('.'): sbin = sbin.split('.')[1] if all(c in '01' for c in sbin): return sum(int(c)*com[i] for i,c in enumerate(sbin,1)) else: return None 

.

.

Finally , applying the following functions:

 from decimal import Decimal getcontext().prec = 500 # precision == 500 , to be large ! tinies = [ Decimal(1) / Decimal(2**i) for i in xrange(1,400)] com = dict((i,tin) for i,tin in enumerate(tinies,1)) def afterdotbinary2float(sbin, com = com): '''Transforms a binary lying after a dot into a float after a dot''' if sbin.startswith('0b.') or sbin.startswith('.'): sbin = sbin.split('.')[1] if all(c in '01' for c in sbin): return sum(int(c)*com[i] for i,c in enumerate(sbin,1)) else: return None def hexf2binf(x): '''Transforms an hexadecimal float with a dot into a binary float with a dot''' a,_,p = str(float.fromhex(x)).partition('.') # the following part transforms the float after the dot into a binary after the dot tinies = [ Decimal(1) / Decimal(2**i) for i in xrange(1,400)] bits = [] pdec = Decimal('.'+p) for tin in tinies: if pdec-tin==0: bits.append('1') break elif pdec-tin>0: bits.append('1') pdec -= tin else: bits.append('0') pbin = ''.join(bits) # it the binary after the dot # the float before the dot is easily transformed into a binary return '.'.join((bin(int(a)),pbin)) for n in (45.625 , 780.2265625 , 1022.796875): print 'n ==',n,' transformed with its method hex() to:' nhexed = n.hex() print 'nhexed = n.hex() ==',nhexed print '\nhexf2binf(nhexed) ==',hexf2binf(nhexed) print "\nVerification:\nbefore,_,after = hexf2binf(nhexed).partition('.')" before,_,after = hexf2binf(nhexed).partition('.') print 'before ==',before,' after ==',after print 'int(before,2) ==',int(before,2) print 'afterdotbinary2float(after) ==',afterdotbinary2float(after) print '\n---------------------------------------------------------------\n' 

result

 n == 45.625 transformed with its method hex() to: nhexed = n.hex() == 0x1.6d00000000000p+5 hexf2binf(nhexed) == 0b101101.101 Verification: before,_,after = hexf2binf(nhexed).partition('.') before == 0b101101 after == 101 int(before,2) == 45 afterdotbinary2float(after) == 0.625 --------------------------------------------------------------- n == 780.2265625 transformed with its method hex() to: nhexed = n.hex() == 0x1.861d000000000p+9 hexf2binf(nhexed) == 0b1100001100.0011101 Verification: before,_,after = hexf2binf(nhexed).partition('.') before == 0b1100001100 after == 0011101 int(before,2) == 780 afterdotbinary2float(after) == 0.2265625 --------------------------------------------------------------- n == 1022.796875 transformed with its method hex() to: nhexed = n.hex() == 0x1.ff66000000000p+9 hexf2binf(nhexed) == 0b1111111110.110011 Verification: before,_,after = hexf2binf(nhexed).partition('.') before == 0b1111111110 after == 110011 int(before,2) == 1022 afterdotbinary2float(after) == 0.796875 --------------------------------------------------------------- 

.

For two numbers :

 from decimal import Decimal tinies = [ Decimal(1) / Decimal(2**i) for i in xrange(1,400)] com = dict((i,tin) for i,tin in enumerate(tinies,1)) def hexf2binf(x, tinies = tinies): '''Transforms an hexadecimal float with a dot into a binary float with a dot''' a,_,p = str(float.fromhex(x)).partition('.') # the following part transforms the float after the dot into a binary after the dot bits = [] pdec = Decimal('.'+p) for tin in tinies: if pdec-tin==0: bits.append('1') break elif pdec-tin>0: bits.append('1') pdec -= tin else: bits.append('0') pbin = ''.join(bits) # it the binary after the dot # the float before the dot is easily transformed into a binary return '.'.join((bin(int(a)),pbin)) print hexf2binf('0x1.a000000000000p+2') print print hexf2binf('0x1.b5c2000000000p+1') 

as a result it will be displayed:

 0b110.1 0b11.011010111000010000000000000000000000010000011111100001111111101001000110110000101101110000000001111110011100110101011100011110011101111010001011111001011101011000000011001111111010111011000101000111100100110000010110001000111010111101110111011111000100100110110101011100101001110011000100000000010001101111111010001110100100101110111000111111001011101101010011011111011001010011111111010101011010110 
+3
source

Given the hexadecimal string h , you can find the corresponding float with

 x = float.fromhex(h) 

So it’s really interesting that you can create a fixed point binary representation of any float. There may not be a final representation, so you probably want to limit the length that may be. (for example, the binary representation of math.pi would not end ...)

Maybe something like the following might work

 def binaryRepresentation(x, n=8): # the base and remainder ... handle negatives as well ... base = int(x) fraction = abs(int(round( (x - base) * (2**n) ))) # format and remove redundant zeros return "{0:b}.{1:b}".format(base, fraction).rstrip("0") 
+1
source

Big EDIT about big numbers

.

The following code shows the problem with my solution in my other answer.
Please note that I changed the parameter of my hexf2binf (floathex) function from h to floathex to make it the same as the parameter used by unutbu in its floathex_to_binary (floathex) function

 from decimal import Decimal,getcontext getcontext.prec = 500 tinies = [ Decimal(1) / Decimal(2**i) for i in xrange(1,400)] com = dict((i,tin) for i,tin in enumerate(tinies,1)) def hexf2binf(floathex, tinies = tinies): fromh = float.fromhex(floathex) print 'fromh = float.fromhex(h) DONE' print 'fromh ==',fromh print "str(float.fromhex(floathex)) ==",str(float.fromhex(floathex)) a,_,p = str(float.fromhex(floathex)).partition('.') print 'before the dot ==',a print 'after the dot ==',p # the following part transforms the float after the dot into a binary after the dot bits = [] pdec = Decimal('.'+p) for tin in tinies: if pdec-tin==0: bits.append('1') break elif pdec-tin>0: bits.append('1') pdec -= tin else: bits.append('0') pbin = ''.join(bits) # it the binary after the dot # the float before the dot is easily transformed into a binary return '.'.join((bin(int(a)),pbin)) x = x = 123456789012345685803008.0 print ' x = {:f}'.format(x) h = x.hex() print ' h = x.hex() ==',h print '\nENTERING hexf2binf(floathex) with h as argument' v = hexf2binf(h) print '\nhexf2binf(x)==',v 

result

  x = 123456789012345685803008.000000 h = x.hex() == 0x1.a249b1f10a06dp+76 ENTERING hexf2binf(floathex) with h as argument fromh = float.fromhex(h) DONE fromh == 1.23456789012e+23 str(float.fromhex(floathex)) == 1.23456789012e+23 before the dot == 1 after the dot == 23456789012e+23 hexf2binf(x)== 0b1.111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 

The problem is with the str(float.fromhex(x)) statement in the a,_,p = str(float.fromhex(x)).partition('.') Statement , which expresses the representation of float.fromhex (x) for a large number with an indicator.
Then PARTS BEFORE DATA (a ante) AND AFTER CURRENT (p as a message) EASY.

Fixing this is easy: replacing the inaccurate statement with this:

 a,_,p = '{:f}'.format(float.fromhex(x)).partition('.') 

.

Nota bene:

On a typical machine running Python, there is 53 bits of precision available for floating Python, so the value is stored internally when you enter the decimal number 0.1 - binary fraction 0.00011001100110011001100110011001100110011001100110011010 http://docs.python.org/tutorial/floatingpoint .html

This means that when a large value for a float is written in the code, its internal representation is actually an approximation to the recorded value. This is shown in the following code:

 x1 = 123456789012345685803008.0 print 'x1 == 123456789012345685803008.0' h1 = x1.hex() print 'h1 = x1.hex() ==',h1 y1 = float.fromhex(h1) print 'y1 = float.fromhex(h1) == {:f}'.format(y1) print x2 = 123456789012345678901234.64655 print 'x2 == 123456789012345678901234.64655' h2 = x2.hex() print 'h2 = x2.hex() ==',h2 y2 = float.fromhex(h2) print 'y2 = float.fromhex(h2) == {:f}'.format(y2) print 

result

 x1 == 123456789012345685803008.0 h1 = x1.hex() == 0x1.a249b1f10a06dp+76 y1 = float.fromhex(h1) == 123456789012345685803008.000000 x2 == 123456789012345678901234.64655 h2 = x2.hex() == 0x1.a249b1f10a06dp+76 y2 = float.fromhex(h2) == 123456789012345685803008.000000 

The values ​​of h1 and h2 are the same because, although different values ​​are assigned to the identifiers x1 and x2 in the script, OBJECTS x1 and x2 are represented with the same approximation in the machine.
The internal representation of 123456789012345685803008.0 is the exact value 123456789012345685803008.0 and is the internal representation of 123456789012345678901234.64655 , but its approximation, therefore, outputting h1 and h2 from x1 and x2 gives the same value of h1 and h2 .

This problem exists when we write a decimal number in a script. This does not exist when we write a number directly in hexadecimal or binary representation.

What I wanted to emphasize

is that I wrote the afterdotbinary2float (sbin, com = com) function to check the results obtained by hexf2binf () . This check works well when the number passed to hexf2binf () is small, but due to the internal approximation of large numbers (= with a lot of digits), I wonder if this check is not distorted. Indeed, when a large number comes into the function, it has already been approximated: the numbers after the point have been converted to a series of zeros,
as shown here after:

 from decimal import Decimal, getcontext getcontext().prec = 500 tinies = [ Decimal(1) / Decimal(2**i) for i in xrange(1,400)] com = dict((i,tin) for i,tin in enumerate(tinies,1)) def afterdotbinary2float(sbin, com = com): '''Transforms a binary lying after a dot into a float after a dot''' if sbin.startswith('0b.') or sbin.startswith('.'): sbin = sbin.split('.')[1] if all(c in '01' for c in sbin): return sum(int(c)*com[i] for i,c in enumerate(sbin,1)) else: return None def hexf2binf(floathex, tinies = tinies): '''Transforms an hexadecimal float with a dot into a binary float with a dot''' a,_,p = '{:.400f}'.format(float.fromhex(floathex)).partition('.') # the following part transforms the float after the dot into a binary after the dot bits = [] pdec = Decimal('.'+p) for tin in tinies: if pdec-tin==0: bits.append('1') break elif pdec-tin>0: bits.append('1') pdec -= tin else: bits.append('0') pbin = ''.join(bits) # it the binary after the dot # the float before the dot is easily transformed into a binary return '.'.join((bin(int(a)),pbin)) for n in (123456789012345685803008.0, 123456789012345678901234.64655, Decimal('123456789012345.2546') ): print 'n == {:f} transformed with its method hex() to:'.format(n) nhexed = n.hex() print 'nhexed = n.hex() ==',nhexed print '\nhexf2binf(nhexed) ==',hexf2binf(nhexed) print "\nVerification:\nbefore,_,after = hexf2binf(nhexed).partition('.')" before,_,after = hexf2binf(nhexed).partition('.') print 'before ==',before,' after ==',after print 'int(before,2) ==',int(before,2) print 'afterdotbinary2float(after) ==',afterdotbinary2float(after) print '\n---------------------------------------------------------------\n' 

result

 n == 123456789012345685803008.000000 transformed with its method hex() to: nhexed = n.hex() == 0x1.a249b1f10a06dp+76 hexf2binf(nhexed) == 0b11010001001001001101100011111000100001010000001101101000000000000000000000000.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 Verification: before,_,after = hexf2binf(nhexed).partition('.') before == 0b11010001001001001101100011111000100001010000001101101000000000000000000000000 after == 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 int(before,2) == 123456789012345685803008 afterdotbinary2float(after) == 0E-399 --------------------------------------------------------------- n == 123456789012345685803008.000000 transformed with its method hex() to: nhexed = n.hex() == 0x1.a249b1f10a06dp+76 hexf2binf(nhexed) == 0b11010001001001001101100011111000100001010000001101101000000000000000000000000.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 Verification: before,_,after = hexf2binf(nhexed).partition('.') before == 0b11010001001001001101100011111000100001010000001101101000000000000000000000000 after == 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 int(before,2) == 123456789012345685803008 afterdotbinary2float(after) == 0E-399 --------------------------------------------------------------- n == 123456789012345.2546 transformed with its method hex() to: Traceback (most recent call last): File "I:\verfitruc.py", line 41, in <module> nhexed = n.hex() AttributeError: 'Decimal' object has no attribute 'hex' 

Conclusion: testing with numbers 123456789012345685803008.0 and 123456789012345678901234.64655 has no meaning and is not of interest.

So, I wanted to check for unapproximated numbers, and I passed in a floating point number. As you can see, the problem is that such an instance does not have a hex () method.

.

Finally, I'm not quite sure of my function for large numbers, but it works correctly for common numbers after I corrected an inaccurate instruction.

.

EDIT

I added '.400' to the statement:

 a,_,p = '{:.400f}'.format(fromh).partition('.') 

otherwise, the value of p can be truncated, which gives a binary representation of a slightly different number than the value passed to the function.

I put 400 because it is the length that I defined for the tinies list, which contains decimal instances corresponding to 1/2, 1/4, 1/8, 1/16, etc.

However, although it rarely happens that a number with more than 400 digits after the decimal point makes any sense, this addition remains unsatisfactory to me: the code is not absolutely general, which is the case of unutbu code.

+1
source

All Articles