浮点数的精度问题

A. 用Python计算9.4 - 9得到的结果为0.40000000000000036。
B. 结果用32位浮点数表示为:
0 01111101 1001 1001 1001 1001 1001 101
这是因为尾数只能保存23位,尾数最低位应该“0舍1入”,所以结果如上。
C. 下面分析为何Python计算9.4-9的结果为0.40000000000000036:
本人的计算机中存储的浮点数采用的是双精度浮点数,那么
+9.4转换为二进制即23×1.00101100110(0110循环),用64位浮点数表示
0 10000000010 0010 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1101
-9用64位浮点数表示
1 10000000010 0010 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
阶码相同,无需对阶,两个浮点数尾数相减得
0010 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1101  
-
0010 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
=
0000 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1101
将尾数首次出现的1视作规约数隐藏的1,并在尾数后补0凑齐52位,变换成
100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1101 0000 0
阶码应相应退阶-5,变为
(10000000010)B - (5)D = (01111111101)B
所以9.4-9计算结果用64位浮点数表示
0 01111111101 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1010 0000
用所编写的程序将该64位浮点数转换成实际的十进制数得到的结果为0.40000000000000035527,由于计算机保留的位数有限,最后经过四舍五入后打印出来的数为0.40000000000000036。
这个问题本质上是由于浮点数位数有限,在尾数的最低位有“0舍1入”的操作,造成了与实际值的误差。对于某个十进制数,当其转换成二进制数时尾数不断循环的话,这种误差无法避免。

附录代码:

import numpy as np

# 直接打印输出9.4-9
def test_fun():
    a = 9.4-9
    print(a)

# 将32位浮点数转换为实际的十进制数
def cal_float32(ee, mm):
    """
    :param ee: ee为输入参数,以10进制表达指数位;
    :param mm:  mm为输入参数,以0/1序列表示23位小数位;
    :return: F32为输出参数
    """
    mm_list = np.float64(list(mm))
    mesa_multi = 2 ** np.float64((np.arange(-1, -24, -1)))
    if ee == 0:
        E = 2 ** (-126)
        F32 = E * np.sum(mm_list * mesa_multi)
    else:
        E = 2 ** (np.float64(ee) - 127)
        F32 = E * (1 + np.sum(mm_list * mesa_multi))
    return F32

# 将64位浮点数转换为实际的十进制数
def cal_float64(ee, mm):
    """
    :param ee: ee为输入参数,以10进制表达指数位;
    :param mm:  mm为输入参数,以0/1序列表示52位小数位;
    :return: F64为输出参数
    """
    mm_list = np.float64(list(mm))
    mesa_multi = 2 ** np.float64((np.arange(-1, -53, -1)))
    if ee == 0:
        E = 2 ** (-1022)
        F64 = E * np.sum(mm_list * mesa_multi)
    else:
        E = 2 ** (np.float64(ee) - 1023)
        F64 = E * (1 + np.sum(mm_list * mesa_multi))
    return F64

# 十进制整数转二进制
def int_decimal_to_binary(par):
    """
    :param par: par为输入的十进制整数
    :return: 输出为二进制的list
    """
    binary_result = []
    while par != 0:
        r = divmod(par, 2)
        par = r[0]
        remainder = r[1]
        binary_result.append(remainder)
    binary_result.reverse()
    return binary_result

# 十进制小数部分转二进制
def man_decimal_to_binary(par):
    """
    :param par: par为输入的十进制小数部分
    :return: 输出为二进制的list
    """
    binary_result = []
    par -= int(par)
    while par != 0:
        par *= 2
        if par >= 1:
            binary_result.append(1)
            par -= int(par)
        else:
            binary_result.append(0)
    return binary_result

# 十进制数部分转二进制
def decimal_to_binary(par):
    """
    :param par: par为输入的十进制数(包含整数小数)
    :return: 输出为二进制的list
    """
    integer = int_decimal_to_binary(int(par))
    mantissa = man_decimal_to_binary(par-int(par))
    integer_str = [str(i) for i in integer]
    mantissa_str = [str(i) for i in mantissa]
    binary_result = "".join(integer_str) + "." + "".join(mantissa_str)
    return binary_result

# 整数二进制转十进制
def int_binary_to_decimal(par):
    """
    :param par: par为输入的二进制整数,类型为字符串
    :return: 输出为十进制数
    """
    return int(par, 2)

# 二进制小数部分转十进制
def man_binary_to_decimal(par):
    """
    :param par: par为输入的二进制小数部分,类型为字符串(不包含0.)
    :return: 输出为二进制的list
    """
    mesa_multi = 2 ** np.float64((np.arange(-1, -(len(par)+1), -1)))
    par_list = np.float64(list(par))
    decimal_result = np.sum(par_list * mesa_multi)
    return decimal_result

if __name__ == '__main__':
    # 直接打印输出9.4-9,打印结果为0.40000000000000036
    test_fun()
    # 为了得到9.4和9两个数的浮点数,应先将它们转换成二进制数再来计算
    result1 = decimal_to_binary(9.4)
    result2 = decimal_to_binary(9)
    # 得到9.4的64位浮点数为0 10000000010 0010 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1101
    # 得到-9 的64位浮点数为1 10000000010 0010 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
    # 9.4-9浮点数计算结果为0 01111111101 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1010 0000
    # 具体计算过程见作业报告,利用程序将该浮点数结果转换成实际十进制数并打印
    result3 = cal_float64(1021, "1001100110011001100110011001100110011001100110100000")
    # 人为保留20位小数,结果为0.40000000000000035527
    print('%.20f' % result3)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值