为什么在python中 0.3+0.55=0.8500000000000001

a=0.55
b=0.3
print(" a=",a," b=",b," a+b=",a+b,"?") # a= 0.55  b= 0.3  a+b= 0.8500000000000001 ?

为什么0.3+0.55=0.8500000000000001呢?

这个过程中究竟发生了什么?下面就开始尝试找到结果

十进制小数的二进制表示:
整数部分:除以2,取出余数,商继续除以2,直到得到0为止,将取出的余数逆序
小数部分:乘以2,然后取出整数部分,将剩下的小数部分继续乘以2,然后再取整数部分,一直取到小数部分为零为止。如果永远不为零,则按要求保留足够位数的小数,最后一位做0舍1入。将取出的整数顺序排列。


以0.3为例
0.3x2=0.6 取整0,小数部分是0.6
0.6x2=1.2 取整1,小数部分是0.2
0.2x2=0.4 取整0,小数部分是0.4
0.4x2=0.8 取整0,小数部分是0.8

0.8x2=1.6 取整1,小数部分是0.6
0.6x2=1.2 取整1,小数部分是0.2
0.2x2=0.4 取整0,小数部分是0.4
0.4x2=0.8 取整0,小数部分是0.8

0.8x2=1.6 取整1,小数部分是0.6
0.6x2=1.2 取整1,小数部分是0.2
0.2x2=0.4 取整0,小数部分是0.4
0.4x2=0.8 取整0,小数部分是0.8
.... ,
得到0.8125的二进制是0.0100 1100 1100....

然而float共计32位,折合4字节。
其中:30-23位,一共8位是指数位,22-0位,一共23位是尾数位(有一位是符号位)。

因为机器字长有限,当小数部分需要舍去一些精度的时候 1进0舍 
所以0.3在存储中表示为           0.0100 1100 1100 1100 1100 1100 1100 110
然而0.55在存储中表示为         0.1000 1100 1100 1100 1100 1100 1100 110
最后0.3+0.55在存储中表示为  0.1101 1001 1001 1001 1001 1001 1001 100
转为十进制为0.8499999046325684 与结果不符

#转化为二进制
def mybin(num,pre):
    if num == int(num):
        # 若为整数
        integer = '{:08b}'.format(int(num))  # {:08b} :高位补0
        return integer
    else:
        # 若为浮点数
        # 取整数部分
        integer = int(num)
        # 取小数部分
        flo = num - integer
        # 整数部分进制转换
        integercom = '{:b}'.format(integer)
        # 小数部分进制转换
        tem = flo
        tmpflo = []
        for i in range(pre): #31位长
            tem *= 2
            tmpflo += str(int(tem))
            tem -= int(tem)
        flocom = tmpflo
        return integercom + '.' + ''.join(flocom)


#转化为十进制
def myebin(n, pre):
    '''
    把一个带小数的二进制数n转换成十进制
    小数点后面保留pre位小数
    '''
    string_number1 = str(n) #number1 表示二进制数,number2表示十进制数
    decimal = 0  #小数部分化成二进制后的值
    flag = False
    for i in string_number1: #判断是否含小数部分
        if i == '.':
            flag = True
            break
    if flag: #若二进制数含有小数部分
        string_integer, string_decimal = string_number1.split('.') #分离整数部分和小数部分
        for i in range(len(string_decimal)):
            decimal += 2**(-i-1)*int(string_decimal[i])  #小数部分化成二进制
        number2 = int(str(int(string_integer, 2))) + decimal
        return round(number2, pre)
    else: #若二进制数只有整数部分
        return int(string_number1, 2)#若只有整数部分 直接一行代码二进制转十进制

a=0.55
b=0.3
c=a+b
print(" a=",a," b=",b," a+b=",a+b,"?")# a= 0.55  b= 0.3  a+b= 0.8500000000000001 ?
print("过程:")            #过程:
print("二进制a:",mybin(a,23),"二进制b:",mybin(b,23),"二进制a+b:",mybin(a+b,23))
 #二进制a: 0.10001100110011001100110 二进制b: 0.01001100110011001100110 二进制a+b: 0.11011001100110011001100
print("a+b的二进制的十进制表达:",myebin(mybin(a+b,23),16))
#a+b的二进制的十进制表达: 0.8499999046325684

double数据类型上述同理,唯一的区别在于尾数由23位扩展到52位,阶码由8位增加到了11位
所以0.3在存储中表示为           0.0100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100
然而0.55在存储中表示为         0.1000 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100
最后0.3+0.55在存储中表示为  0.1101 1001 1001 1001 1001 1001 1001 1000 1001 1001 1001 1001 1000
转为十进制为0.8500000000000001,符合实验结果

#转化为二进制
def mybin(num,pre):
    if num == int(num):
        # 若为整数
        integer = '{:08b}'.format(int(num))  # {:08b} :高位补0
        return integer
    else:
        # 若为浮点数
        # 取整数部分
        integer = int(num)
        # 取小数部分
        flo = num - integer
        # 整数部分进制转换
        integercom = '{:b}'.format(integer)
        # 小数部分进制转换
        tem = flo
        tmpflo = []
        for i in range(pre): #31位长
            tem *= 2
            tmpflo += str(int(tem))
            tem -= int(tem)
        flocom = tmpflo
        return integercom + '.' + ''.join(flocom)


#转化为十进制
def myebin(n, pre):
    '''
    把一个带小数的二进制数n转换成十进制
    小数点后面保留pre位小数
    '''
    string_number1 = str(n) #number1 表示二进制数,number2表示十进制数
    decimal = 0  #小数部分化成二进制后的值
    flag = False
    for i in string_number1: #判断是否含小数部分
        if i == '.':
            flag = True
            break
    if flag: #若二进制数含有小数部分
        string_integer, string_decimal = string_number1.split('.') #分离整数部分和小数部分
        for i in range(len(string_decimal)):
            decimal += 2**(-i-1)*int(string_decimal[i])  #小数部分化成二进制
        number2 = int(str(int(string_integer, 2))) + decimal
        return round(number2, pre)
    else: #若二进制数只有整数部分
        return int(string_number1, 2)#若只有整数部分 直接一行代码二进制转十进制

a=0.55
b=0.3
c=a+b
print(" a=",a," b=",b," a+b=",a+b,"?")
print("过程:")
print("如果是float形状:")
print("二进制a:",mybin(a,23),"二进制b:",mybin(b,23),"二进制a+b:",mybin(a+b,23))
#二进制a: 0.10001100110011001100110 二进制b: 0.01001100110011001100110 二进制a+b: 0.11011001100110011001100
print("a+b的二进制的十进制表达:",myebin(mybin(a+b,23),16))
#a+b的二进制的十进制表达: 0.8499999046325684
print("如果是double形状:")
print("二进制a:",mybin(a,52),"二进制b:",mybin(b,52),"二进制a+b:",mybin(a+b,52))
#二进制a: 0.1000110011001100110011001100110011001100110011001101 二进制b: 0.0100110011001100110011001100110011001100110011001100 二进制a+b: 0.1101100110011001100110011001100110011001100110011010
print("a+b的二进制的十进制表达:",myebin(mybin(a+b,52),16))
#a+b的二进制的十进制表达: 0.8500000000000001

综上所述:a=(double)(0.55),b=(double)(0.3),在转二进制的过程中,小数部分需要舍去一些精度的时候 1进0舍

以上就是我学习过程中遇到的一点问题,以及解决过程,如果有不对的地方希望大家指正,谢谢。
 

  • 8
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小球0369

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值