Python中浮点数的二进制表示(位不是十六进制)
Posted
技术标签:
【中文标题】Python中浮点数的二进制表示(位不是十六进制)【英文标题】:Binary representation of float in Python (bits not hex) 【发布时间】:2013-05-02 22:08:30 【问题描述】:如何将字符串作为 32 位浮点数的二进制 IEEE 754 表示?
示例
1.00 -> '00111111100000000000000000000000'
【问题讨论】:
【参考方案1】:您可以使用 struct
包做到这一点:
import struct
def binary(num):
return ''.join(':0>8b'.format(c) for c in struct.pack('!f', num))
将其打包为网络字节序浮点数,然后将每个生成的字节转换为 8 位二进制表示形式并将它们连接起来:
>>> binary(1)
'00111111100000000000000000000000'
编辑: 有人要求扩大解释。我将使用中间变量来扩展它来评论每个步骤。
def binary(num):
# Struct can provide us with the float packed into bytes. The '!' ensures that
# it's in network byte order (big-endian) and the 'f' says that it should be
# packed as a float. Alternatively, for double-precision, you could use 'd'.
packed = struct.pack('!f', num)
print 'Packed: %s' % repr(packed)
# For each character in the returned string, we'll turn it into its corresponding
# integer code point
#
# [62, 163, 215, 10] = [ord(c) for c in '>\xa3\xd7\n']
integers = [ord(c) for c in packed]
print 'Integers: %s' % integers
# For each integer, we'll convert it to its binary representation.
binaries = [bin(i) for i in integers]
print 'Binaries: %s' % binaries
# Now strip off the '0b' from each of these
stripped_binaries = [s.replace('0b', '') for s in binaries]
print 'Stripped: %s' % stripped_binaries
# Pad each byte's binary representation's with 0's to make sure it has all 8 bits:
#
# ['00111110', '10100011', '11010111', '00001010']
padded = [s.rjust(8, '0') for s in stripped_binaries]
print 'Padded: %s' % padded
# At this point, we have each of the bytes for the network byte ordered float
# in an array as binary strings. Now we just concatenate them to get the total
# representation of the float:
return ''.join(padded)
还有几个例子的结果:
>>> binary(1)
Packed: '?\x80\x00\x00'
Integers: [63, 128, 0, 0]
Binaries: ['0b111111', '0b10000000', '0b0', '0b0']
Stripped: ['111111', '10000000', '0', '0']
Padded: ['00111111', '10000000', '00000000', '00000000']
'00111111100000000000000000000000'
>>> binary(0.32)
Packed: '>\xa3\xd7\n'
Integers: [62, 163, 215, 10]
Binaries: ['0b111110', '0b10100011', '0b11010111', '0b1010']
Stripped: ['111110', '10100011', '11010111', '1010']
Padded: ['00111110', '10100011', '11010111', '00001010']
'00111110101000111101011100001010'
【讨论】:
@MarkRansom -- 你可能是对的,但似乎对于不需要完成的每一位都进行了大量的字符串操作...... 我同意@mgilson——我实际上更喜欢他的解决方案,但最后一个replace
和 rjust
到 32(或 64),而不是每个字节一个。
对于 Python 3,您必须省略对 ord() 的调用,因为 pack() 返回一个字节对象,该对象在迭代时直接产生整数。
更短的 Python 3 版本:''.join(':0>8b'.format(c) for c in struct.pack('!f', num))
@gciriani 将浮点格式 (f
) 替换为 double format (d
),即 ''.join(':0>8b'.format(c) for c in struct.pack('!f', num))
【参考方案2】:
这是一个丑陋的...
>>> import struct
>>> bin(struct.unpack('!i',struct.pack('!f',1.0))[0])
'0b111111100000000000000000000000'
基本上,我只是使用 struct 模块将 float 转换为 int ...
这是一个稍微好一点的使用ctypes
:
>>> import ctypes
>>> bin(ctypes.c_uint.from_buffer(ctypes.c_float(1.0)).value)
'0b111111100000000000000000000000'
基本上,我构造了一个float
并使用相同的内存位置,但我将它标记为c_uint
。 c_uint
的值是一个 python 整数,你可以使用内置的 bin
函数。
【讨论】:
它依赖于sizeof(int) == sizeof(float)
(使用'!'
强制4
字节用于i
格式)。 ctypes.sizeof(ctypes.c_int)
可能取决于平台。 Python 3.2+ 上有int.from_bytes()
@J.F.Sebastian -- 我想我还假设 bin
返回 IEEE 标准表示...
我想我也没有。 bin
函数并不能保证输出的多少——只是它是一个 python 可以处理的对象。如果sizeof(int) != sizeof(float)
那么它没有使用 IEEE 754(是吗?)。在这种情况下,bin
返回的位模式也可以是任何东西——例如由于不同的字节顺序,这些位可能会向后报告或其他原因。符号位可能在其他地方,等等。
sizeof(int) != sizeof(float)
问题与bin()
无关(适用于无限制的 Python 整数)。要支持负浮点数,use !I
format.
我很抱歉这个大死灵,但我必须对ctypes
变体进行更正:当使用负数时,结果都是错误的,可能是由于 python 应用2 的补码或中间某处的其他一些恶作剧。当尝试使用数字 -1.0 的示例时,结果中实际编码的 IEEE 754 为 4.0 ('-0b1000000100000000000000000000000'
)... 将示例中的 ctypes.c_int
替换为 ctypes.c_uint
时似乎可以正常工作。编辑:快速响应的巨大荣誉! :)【参考方案3】:
使用bitstring 模块找到了另一个解决方案。
import bitstring
f1 = bitstring.BitArray(float=1.0, length=32)
print(f1.bin)
输出:
00111111100000000000000000000000
【讨论】:
这几天是f1.bin
可以从以下网站验证值:h-schmidt.net/FloatConverter/IEEE754.html
bitstring
链接断开【参考方案4】:
为了完整起见,您可以使用 numpy 实现这一点:
f = 1.00
int32bits = np.asarray(f, dtype=np.float32).view(np.int32).item() # item() optional
然后您可以使用 b
格式说明符打印此内容并使用填充
print(':032b'.format(int32bits))
【讨论】:
稍微整洁一点:int32bits = np.float32(1.0).view(np.int32)
如果你真的想要一个 python,仍然需要.item()
int
是的,没错。否则,你有一个np.int32
对象
仍然整洁:int32bits = np.float32(1.0).view(np.int32).item()
:)
int32bits = np.float32(-3.03).view(np.uint32).item() 只放一次标志【参考方案5】:
这个问题通过分成两部分处理得更干净。
第一种是将float转换为具有等效位模式的int:
import struct
def float32_bit_pattern(value):
return sum(ord(b) << 8*i for i,b in enumerate(struct.pack('f', value)))
Python 3 不需要ord
将字节转换为整数,因此您需要稍微简化一下上述内容:
def float32_bit_pattern(value):
return sum(b << 8*i for i,b in enumerate(struct.pack('f', value)))
接下来将 int 转换为字符串:
def int_to_binary(value, bits):
return bin(value).replace('0b', '').rjust(bits, '0')
现在将它们组合起来:
>>> int_to_binary(float32_bit_pattern(1.0), 32)
'00111111100000000000000000000000'
【讨论】:
float32_bit_pattern
在 Python 3.2+ 上可以定义为 lambda x: int.from_bytes(struct.pack("f", x), byteorder="little")
无法编译。
@noobcoder 抱歉,Python 3 不需要 ord
来转换 struct.pack
的输出。我忘了提import struct
。【参考方案6】:
使用这两个简单的函数 (Python >=3.6),您可以轻松地将浮点数转换为二进制数,反之亦然,适用于 IEEE 754 binary64。
import struct
def bin2float(b):
''' Convert binary string to a float.
Attributes:
:b: Binary string to transform.
'''
h = int(b, 2).to_bytes(8, byteorder="big")
return struct.unpack('>d', h)[0]
def float2bin(f):
''' Convert float to 64-bit binary string.
Attributes:
:f: Float number to transform.
'''
[d] = struct.unpack(">Q", struct.pack(">d", f))
return f'd:064b'
例如:
print(float2bin(1.618033988749894))
print(float2bin(3.14159265359))
print(float2bin(5.125))
print(float2bin(13.80))
print(bin2float('0011111111111001111000110111011110011011100101111111010010100100'))
print(bin2float('0100000000001001001000011111101101010100010001000010111011101010'))
print(bin2float('0100000000010100100000000000000000000000000000000000000000000000'))
print(bin2float('0100000000101011100110011001100110011001100110011001100110011010'))
输出是:
0011111111111001111000110111011110011011100101111111010010100100
0100000000001001001000011111101101010100010001000010111011101010
0100000000010100100000000000000000000000000000000000000000000000
0100000000101011100110011001100110011001100110011001100110011010
1.618033988749894
3.14159265359
5.125
13.8
我希望你喜欢它,它对我很有效。
【讨论】:
还有——你为什么用struct.unpack('>Q', ...)
而不是int.from_bytes(..., 'big')
?
你说得对,decode
行无意中渗入了,我已经对其进行了编辑。谢谢通知。关于您的最后一条评论,请使用struct
,因为接收函数的参数是浮点数,而不是整数,并且浮点数没有可用的to_bytes()
方法。如果您能想到更好的方法,欢迎您:)
感谢您的回答!如果你有时间,你能看看这个例子吗?如果我理解正确,1.0
的浮点表示是.100...0 * 2^(00000000001)
,其中指数有 11 位,尾数有 52。因此,我认为1.0
的浮点表示应该是00000000000110000000000000000000000000000000000000000000000000000
。我试过float2bin(1.0)
,结果是0011111111110000000000000000000000000000000000000000000000000000
。你能解释一下区别吗?【参考方案7】:
用 Python3 的彩色版本来跟踪 Dan 的回答:
import struct
BLUE = "\033[1;34m"
CYAN = "\033[1;36m"
GREEN = "\033[0;32m"
RESET = "\033[0;0m"
def binary(num):
return [bin(c).replace('0b', '').rjust(8, '0') for c in struct.pack('!f', num)]
def binary_str(num):
bits = ''.join(binary(num))
return ''.join([BLUE, bits[:1], GREEN, bits[1:10], CYAN, bits[10:], RESET])
def binary_str_fp16(num):
bits = ''.join(binary(num))
return ''.join([BLUE, bits[:1], GREEN, bits[1:10][-5:], CYAN, bits[10:][:11], RESET])
x = 0.7
print(x, "as fp32:", binary_str(0.7), "as fp16 is sort of:", binary_str_fp16(0.7))
【讨论】:
【参考方案8】:在浏览了很多类似的问题后,我写了一些希望能达到我想要的东西。
f = 1.00
negative = False
if f < 0:
f = f*-1
negative = True
s = struct.pack('>f', f)
p = struct.unpack('>l', s)[0]
hex_data = hex(p)
scale = 16
num_of_bits = 32
binrep = bin(int(hex_data, scale))[2:].zfill(num_of_bits)
if negative:
binrep = '1' + binrep[1:]
binrep
是结果。
每个部分都会进行说明。
f = 1.00
negative = False
if f < 0:
f = f*-1
negative = True
如果为负数,则将数字转换为正数,并将变量负数设置为假。原因是正负二进制表示的区别就在第一位,这比在用负数做整个过程时找出问题所在更简单。
s = struct.pack('>f', f) #'?\x80\x00\x00'
p = struct.unpack('>l', s)[0] #1065353216
hex_data = hex(p) #'0x3f800000'
s
是二进制 f
的十六进制表示。然而,它不是我需要的漂亮形式。这就是 p 的用武之地。它是十六进制 s 的 int 表示。然后进行另一次转换以获得漂亮的十六进制。
scale = 16
num_of_bits = 32
binrep = bin(int(hex_data, scale))[2:].zfill(num_of_bits)
if negative:
binrep = '1' + binrep[1:]
scale
是十六进制的基数 16。 num_of_bits
是 32,因为 float 是 32 位,所以稍后用 0 填充其他位置以达到 32。从 this question 获取 binrep
的代码。如果数字是负数,只需更改第一位。
我知道这很难看,但我没有找到一个好的方法,我需要它快点。欢迎评论。
【讨论】:
bin(struct.unpack('!I', struct.pack('!f', -1.))[0])[2:].zfill(32)
支持正/负浮点数。为了提高性能,您可以修改 b2a_bin(struct.pack('!f', -1.))
以直接接受浮点数。【参考方案9】:
这比要求的要多一点,但是当我找到这个条目时,它就是我所需要的。此代码将给出 IEEE 754 32 位浮点数的尾数、基数和符号。
import ctypes
def binRep(num):
binNum = bin(ctypes.c_uint.from_buffer(ctypes.c_float(num)).value)[2:]
print("bits: " + binNum.rjust(32,"0"))
mantissa = "1" + binNum[-23:]
print("sig (bin): " + mantissa.rjust(24))
mantInt = int(mantissa,2)/2**23
print("sig (float): " + str(mantInt))
base = int(binNum[-31:-23],2)-127
print("base:" + str(base))
sign = 1-2*("1"==binNum[-32:-31].rjust(1,"0"))
print("sign:" + str(sign))
print("recreate:" + str(sign*mantInt*(2**base)))
binRep(-0.75)
输出:
bits: 10111111010000000000000000000000
sig (bin): 110000000000000000000000
sig (float): 1.5
base:-1
sign:-1
recreate:-0.75
【讨论】:
当用 sqrt(2) 验证这个脚本时,它似乎只给出了正确的 6 个十进制数字。某处有错误吗? 2**.5 = 1.4142135623730951 但您的脚本为 binRep(2**.5) 输出以下内容,重新创建:1.4142135381698608。【参考方案10】:在 0..1 之间转换浮点数
def float_bin(n, places = 3):
if (n < 0 or n > 1):
return "ERROR, n must be in 0..1"
answer = "0."
while n > 0:
if len(answer) - 2 == places:
return answer
b = n * 2
if b >= 1:
answer += '1'
n = b - 1
else:
answer += '0'
n = b
return answer
【讨论】:
我修正了代码:长度检查没有考虑前导零和点,b > 1
必须是b >= 1
。测试结果:0 为“0.”,0.5 为“0.1”,0.25 为“0.01”,0.125 为“0.001”,0.1 为“0.0001100110”(places = 10)。【参考方案11】:
这些答案中有几个不能像用 Python 3 编写的那样工作,或者没有给出负浮点数的正确表示。我发现以下内容对我有用(尽管这提供了我需要的 64 位表示)
def float_to_binary_string(f):
def int_to_8bit_binary_string(n):
stg=bin(n).replace('0b','')
fillstg = '0'*(8-len(stg))
return fillstg+stg
return ''.join( int_to_8bit_binary_string(int(b)) for b in struct.pack('>d',f) )
【讨论】:
或许值得一提的是,>d
用于大端双精度(8 字节数字),<d
用于小端双精度。【参考方案12】:
我做了一个非常简单的。请检查一下。如果您认为有任何错误,请告诉我。这对我来说很好。
sds=float(input("Enter the number : "))
sf=float("0."+(str(sds).split(".")[-1]))
aa=[]
while len(aa)<15:
dd=round(sf*2,5)
if dd-1>0:
aa.append(1)
sf=dd-1
else:
sf=round(dd,5)
aa.append(0)
des=aa[:-1]
print("\n")
AA=([str(i) for i in des])
print("So the Binary Of : %s>>>"%sds,bin(int(str(sds).split(".")[0])).replace("0b",'')+"."+"".join(AA))
或者如果是整数,只需使用bin(integer).replace("0b",'')
【讨论】:
【参考方案13】:在我看来,您可以使用 .format 来最简单地表示位:
我的代码看起来像:
def fto32b(flt):
# is given a 32 bit float value and converts it to a binary string
if isinstance(flt,float):
# THE FOLLOWING IS AN EXPANDED REPRESENTATION OF THE ONE LINE RETURN
# packed = struct.pack('!f',flt) <- get the hex representation in (!)Big Endian format of a (f) Float
# integers = []
# for c in packed:
# integers.append(ord(c)) <- change each entry into an int
# binaries = []
# for i in integers:
# binaries.append("0:08b".format(i)) <- get the 8bit binary representation of each int (00100101)
# binarystring = ''.join(binaries) <- join all the bytes together
# return binarystring
return ''.join(["0:08b".format(i) for i in [ord(c) for c in struct.pack('!f',flt)]])
return None
输出:
>>> a = 5.0
'01000000101000000000000000000000'
>>> b = 1.0
'00111111100000000000000000000000'
【讨论】:
以上是关于Python中浮点数的二进制表示(位不是十六进制)的主要内容,如果未能解决你的问题,请参考以下文章
MODBUS RTU协议中浮点数是如何存储,读到浮点数寄存器的数值如何转换成所需的浮点数
$("#i23").val(数字($("#f112").val()));如何 i23 是输入框 id 结果显示整数不是 javascript 中浮点数的十进制