• 浮点数的迷思


    这两天研究了下CSAPP中的浮点数的知识,结合这些知识,终于可以解答之前关于float的一些迷思。

    C语言中浮点数相等的比较为什么不能用==?

    A:浮点数的舍入方法引发的。如果要搞清楚这个问题,需要明白下面的两个知识点。

    1. 计算机判定两个浮点数相等,基于它们的在计算上的二进制的表示的比特位是否相等。
    2. 计算机上用有限bit位表示的浮点对应到数学上的实数,表示的不是一个点而是一个区域。仔细考察这个结论,参考信息的表示和处理中关于浮点数舍入部分的描述和之前的浮点数在计算机中的表示,半精度浮点数可以表示的数中有如下的数[126.3, 126.4, 126.44, 126.5],它们是离散分布在数轴上的,这3个数对应的二进制的表示分别是\([(1.1111100101)_2\times 2^{6},(1.1111100110)_2\times 2^{6},(1.1111100111)_2\times 2^{6}]\),根据之前分析的round to even的舍入规则,所有位于\([(1.11111001011)_2\times 2^{6},(1.11111001101)_2\times 2^{6}]\)之间的浮点数都会舍入到\((1.1111100110)_2\times 2^{6}\),换算过来也就是所有位于\([126.34375,126.40625]\)区间的实数都会映射到这个值。如果有下面的语句
    float16 a = 126.4;
    float16 b;
    if (a == b) {
        //
    }
    

    那么b = 126.35或者b = 126.38或者b = 126.3876都是成立的,因为它们都会在浮点数的表示体系下舍入到126.4。数学上不相等,但是在计算机里面是相等的,这就是数学意义和计算机表示意义上的背离,

    浮点数的==和数学意义上严格的相等是不等价的。

    所以为了追求相等的精确性,可以利用\(|a-b|<\epsilon\)的方式,将\(b\)限定在以\(a\)为中心的很短的绝对值区间上,\(\epsilon\)越小,二者的相等程度就越高。所以可以使用下面的代码做一个等价的处理

    #define EPSILON 1e-6
    float16 a = 126.4;
    float16 b;
    if (fabs(a - b) < EPSILON) {
        //
    }
    

    什么是浮点数的“大数吃小数”问题?原理是什么?

    A:浮点数在计算中的舍入引发的。在深入理解计算机系统第2章浮点运算的章节,作者提到过

    使用单精度浮点,3.14 + 1e10 - 1e10 = 0.0

    这就是一个大数吃小数的问题,严格来讲,在C语言中,当一个比较大的浮点数与一个相对小很多的浮点数相加时,结果是那个比较大的浮点数,就好像小数被“吃掉”了一样。上面的例子中,3.14 + 1e10 = 1e10,也就是说从计算机的角度来看,3.14 + 1e10的二进制表示与1e10的二进制浮点表示没有区别。这是怎么回事?

    参考信息的表示和处理中的内容,模拟它们的二进制加法就能得到答案。

    1. 按照浮点数的表示法,在计算机中\((3.14)_{10} = (1.10010001111010111000011)_2\times 2^{1}\),而\((1e10)_{10} = (1.00101010000001011111001)_2\times 2^{33}\)。两个的二进制的小数都是23位
    2. 将两个数字的指数对齐,那么\((3.14)_{10} = (1.10010001111010111000011)_2\times 2^{1} = (0.0000000000000000000000000000000110010001111010111000011)_2\times 2^{33}\),但是float的小数点精度只能到23位,所以舍入到小数点后23位,结果就是浮点数的0
    3. 指数相同,小数相加,1e10的小数加的就是0,所以结果还是1e10。

    从上面的分析可以看到,如果两个相加的数字由于指数悬殊,而导致在对齐指数中,较小数的浮点数用有限精度的尾数表示为0的时候,就会发生这样奇怪的事情。


    全文完

  • 相关阅读:
    安装Windows Live Writer
    CSS实现鼠标滑过表格变色
    简单实用TAB选项卡,支持单页面多个调用
    在asp:Repeater中的label中分类绑定值时用asp:Repeater的ItemDataBound方法
    在asp:Repeater中的asp:LinkButton中按Id删除对应行的数据时用asp:Repeater的ItemCommand方法
    密码请设为616位字母或数字的检查
    List 和 IList的区别
    取得前九条之后的数据
    对List(IList)集合作求和,最大(小)值操作
    验证码验证
  • 原文地址:https://www.cnblogs.com/bugxch/p/16204065.html
Copyright © 2020-2023  润新知