python中浮点数的处理

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python中浮点数的处理相关的知识,希望对你有一定的参考价值。

请问为什么是0.999999999999?而不是1.0呢?

主要还是因浮点数在计算机中实际是以二进制保存的,有些数不精确。

0.1 是十进制,转化为二进制后它是个无限循环的数:
0.00011001100110011001100110011001100110011001100110011001100。。。。。

而python是以双精度(64)位来保存浮点数,多余的位会被截掉,所以看到的是0.1,但在电脑上实际保存的已不是精确的0.1,参与运算后,也就有可能有点点误 差。

有些小数转化为二进制后是有理数且在64位内,所以在计算机上保存的也是精确的,这些小数参与运算后结果一般会是很精确的,不会出现你说的情况。

浮点数很复杂,这些也是我以前查资料时的一点记录,你可以自己去GOOGLE看看。
参考技术A 我感觉是python版本问题我这里用的3.2版本输出就是1.0追问

但是准备来说应该是0.999999999999999,为什么呢?

参考技术B 你别在交互shell里面直接运行。

你写成 .py 文件之后再运行就可以了追问

可是不应该一样吗?但是为什么会有些差别

Python中浮点数的二进制表示(位不是十六进制)

【中文标题】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——我实际上更喜欢他的解决方案,但最后一个 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

【讨论】:

这几天是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('&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'

【讨论】:

以上是关于python中浮点数的处理的主要内容,如果未能解决你的问题,请参考以下文章

python中浮点数的十六进制字符串表示

Python中浮点数的二进制表示(位不是十六进制)

python 中浮点数四舍五入的问题

Python 中浮点数四舍五入的问题

DSP中浮点数和定点数 dsp

shell中浮点数运算