萌ICP备20220458号

简谈浮点数

2022年4月21日 2619点热度 1人点赞 0条评论

珍爱生命,远离浮点数

这句话绝不是空穴来风,而是一个经验之谈。这里我们将讨论浮点数的精度问题以及一些其他的浮点数问题。

浮点数

浮点数是一种数据类型,用来表示小数。

根据 IEEE 754 标准,浮点数可分为符号位(sign bit)、指数位(exponent bits)、尾数位(fraction bits)三部分。

浮点数的原理类似于科学计数法,即按照指数和尾数的形式表示浮点数。所以可以表示很大或很小的数字。

然而,浮点数真的足够精确吗?

精度问题

很遗憾,浮点数还不够精确。

由于浮点数的存储方式,只有有限的数可以被精确表示,而我们常见的 0.10.2 等并不能以有限的二进制方式表达,而是在小数部分的末尾多了个“尾巴”。

由于这个特性,很多数学运算都会出现精度问题。

比如最经典的一个例子:

#include <stdio.h>

int main()
{
    printf("%.17f\n", .1 + .2);
    return 0;
}

最终得到的结果:

0.30000000000000004

更多内容可以参考 这个网站

其他问题

  • +0.0-0.0

    在计算机中,由于符号位的存在,所有的浮点数都是有符号的,包括 +0.0-0.0,而这两个浮点数的表示不同。

  • infNaN

    计算机中有两种特殊的浮点数:infNaNinf 表示正无穷,-inf 表示负无穷,NaN 表示不是数字的值。

    当遇到非常大的浮点数时,其会被转换为 inf,比如 1e400。当遇到非数字的值时,其会被转换为 NaN,比如 inf - inf

    IEEE 754 标准“贴心”地提供了它们的计算法则:

    • inf + inf = inf
    • inf - inf = NaN
    • inf * inf = inf
    • inf / inf = NaN
    • inf * 0 = NaN
    • NaN == NaN = false
    • NaN < NaN = false
    • NaN > NaN = false
    • ...

    其中,NaN 经过任何运算都会变成 NaN,但 NaN 不等于 NaN

    这样的计算特性也带来了一些问题,比如:

    • 在复杂的浮点数计算中,infNaN 会在计算过程中不断传递,导致得不到预期的结果。

解决方案

  1. 化“浮”为“整”

    最经典的例子是处理货币问题,我们可以把金额转换为更小的单位。比如:

    #include 
    
    int main()
    {
        printf("请输入金额(分):");
        int money = 0;
        scanf("%d", &money);
        printf("请输入实付金额(分):");
        int real_money = 0;
        scanf("%d", &real_money);
        int return_money = real_money - money;
        printf("应退金额:%d分\n", return_money);
        printf("即%.2f元\n", (double)return_money / 100.0);
        return 0;
    }
  2. 使用 decimal 库

    在 C++ 中,我们可以使用 decimal 库来解决这个问题。

    #include 
    #include 
    
    int main()
    {
        std::cout << "请输入金额(元):";
        decimal::Decimal money;
        std::cin >> money;
        std::cout << "请输入实付金额(元):";
        decimal::Decimal real_money;
        std::cin >> real_money;
        decimal::Decimal return_money = real_money - money;
        std::cout << "应退金额:" << return_money << "元\n";
        return 0;
    }

    在其它语言中也可以使用 decimal 库,这里请读者自行查阅相关文档。

NhanchouBaimin

A noob.