【问题标题】:Binary representation of float in Python (bits not hex)Python中浮点数的二进制表示(位不是十六进制)
【发布时间】:2013-05-02 22:08:30
【问题描述】:

如何将字符串作为 32 位浮点数的二进制 IEEE 754 表示?

示例

1.00 -> '00111111100000000000000000000000'

【问题讨论】:

    标签: python binary floating-point


    【解决方案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——我实际上更喜欢他的解决方案,但最后一个 replacerjust 到 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_uintc_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
    

    【讨论】:

    【解决方案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('&gt;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 &gt; 1 必须是b &gt;= 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) )
      

      【讨论】:

      • 或许值得一提的是,&gt;d 用于大端双精度(8 字节数字),&lt;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'
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多