欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

计算机里为什么不能精确表示浮点数

程序员文章站 2022-07-03 07:56:13
计算机的世界 是 一个二进制的世界。我们先来看看 十进制 和 二进制的相互转换。十进制 --> 二进制:对整数部分,就是除2取余,倒着来。 对小数部分,就是乘2取整,正着来。先来从宏观角度看看十进制和二进制间的关系先来看整数部分,比如一个 8 bit 的大小:(二进制 --> 十进制)0000 0001 --> 10000 0010 --> 20000 0100 --> 40000 1000 --> 8………………我们可...

计算机的世界 是 一个二进制的世界。

我们先来看看 十进制 和 二进制的相互转换。

十进制 --> 二进制:对整数部分,就是除2取余,倒着来。 对小数部分,就是乘2取整,正着来。

先来从宏观角度看看十进制和二进制间的关系

先来看整数部分,比如一个 8 bit 的大小:

(二进制 --> 十进制)

0000 0001 --> 1

0000 0010 --> 2

0000 0100 --> 4

0000 1000 --> 8

………………

我们可以用这些,拼成一个 十进制的数值。

比如 十进制的 12 , 可以看成 8 + 4 ,

所以对应二进制: 0000 1000 + 0000 0100 = 0000 1100

类似的,对整数部分,十进制中的全部整数是可以用二进制来表示的,

因为有个最小的1,最慢的也就是可以用 1 来累加。

再来看小数部分:

(二进制 —> 十进制)

0.1 —> 1 / 2 = 0.5

0.01 —> 1 / 4 = 0.25

0.001 —> 1 / 8 = 0.125

0.0001 —> 1 / 16 = 0.0625

0.0000 1 —> 1 / 32 = 0.03125

………………

仍然的,我们可以用这些,拼成一个十进制的小数。

比如: 十进制的 0.75 ,可以看成 0.5 + 0.25

所以对应二进制: 0.1 + 0.01 = 0.11

但是这样问题就来了, 在拼十进制的 0.2 时,就要哇哇的哭出来了。。。。

十进制的 0.2 , 二进制的拼凑过程:

1 / 4 ,0.25 太大。

1 / 8 , 0.125 太小。接着往下凑。

1 / 8 + 1 / 16 = 0.125 + 0.0625 = 0.1875, 还是小,接着往下一位加。

1 / 8 + 1 / 16 + 1 / 32 = 0.1875 + 0.03125 = 0.21875, 大了,不加这一位,加下一位。

1 / 8 + 1 / 16 + 1 / 64 = 0.1875 + 0.015625 = 0.203125 , 还是大了,不加这一位,加下一位。

1 / 8 + 1 / 16 + 1 / 128 = 0.1875 + 0.0078125 = 0.1953125,小了,还需要接着往下加。

………………

你就会发现,这样无限往下拼凑,二进制永远不能精确表示十进制的 0.2 。 多多少少会有一点点的误差。

这些误差对应我们日常使用来说是可以忽略的,但是对于银行、金融这样对精度有严格要求的,可能就会出问题。

现在我知道的,就有 2 种解决方法。

  1. Java 里的 BigDecimal 类。(原理跟第二种方法也是一样的)

  2. 干脆直接不存浮点数类型了,直接存储整数,和小数部分有几位 这 2 个数据。

    用的时候再拿出来进行相应的缩放。

这样二进制不能精确表示小数,实际上,二进制表示十进制小数就是不精确的。

感觉就有点类似于 十进制中, 小数也不能精确表示分数。

比如 分数的 1 / 3 , 小数就要表示成 0.33333333, 3 无限循环。。。

从微观角度看看二进制

这些小数, 在计算机里是怎么存储 二进制的呢?

有 2 种思路, 定点数形式、和 浮点数形式。

定点数:

例如现在是 一个 32 bit 的计算机,

可以这么划分:

计算机里为什么不能精确表示浮点数

也可以这么划分:

计算机里为什么不能精确表示浮点数

因为整数部分 和 小数部分的位数都是固定的,所以叫做定点数的表示方法。

这些部分怎么划分的,也就会影响到他们的表示范围 和 表示精度。

浮点数:

利用 类似科学计数法 的形式,达到了让小数点浮动的效果。

比如 : 1.234 * 10^4 。 1.234 是尾数。 10 是基数。 4 是指数。

IEEE754 标准就是规定这些规则的一个约束。 于 1985 年 intel 公司 和 加州伯克利分校的教授制定。

IEEE 754 规定的基本形式是:

( + or - ) 1.(mantissa) * 2 ^ (exponent) 。 mantissa 表示尾数。 exponent 表示指数。

对单精度浮点型 float 来说,32 bit, 符号位 1 bit , 指数 8 bit, 尾数 23 bit。

小数点可以往前移,也可以往后移。

所以指数里的 8bit,也是需要表示正负数,就一半一半,127表示0,0-126 表示负数,128-255表示正数。

尾数里的 23bit,表示的是小数部分,省去了整数部分的数值 1 。

对双精度浮点型 double 来说, 64bit, 符号位 1bit, 指数 11 bit, 尾数 52 bit。

下面就以代码的形式来验证下这个规则:

计算机里为什么不能精确表示浮点数

上述验证过程的代码:

        int i = Float.floatToRawIntBits(0.2f);
        String s = Integer.toBinaryString(i);
        System.out.println("0.2 的二进制表示形式: " + s);
        System.out.println("length : " + s.length());

        // 0  01111100   10011001100110011001101
        int exponent = Integer.parseInt("01111100", 2);
        int mantissa = Integer.parseInt("10011001100110011001101", 2);
        System.out.println("指数 exponent 的十进制: " + exponent);
        System.out.println("尾数 mantissa 的十进制: " + mantissa);

        // 所以最终的表示形式为 :  + 1.10011001100110011001101 * 2 ^ (-3) 
        // 最后的二进制为 :  0. 00110011001100110011001101
	    // 以下这个过程,是看看移动完后的二进制结果对应着的十进制。 要看累加的过程。
        String mStr = "00110011001100110011001101";   
        int[] index = new int[32];  // 下标值就表示,这个 1 对应着是第几位数。
        for (int j = 0; j < mStr.length(); j++) {
            if (mStr.charAt(j) == '1') {
                index[j + 1] = 1;
            }
        }

	    double res = 0.0;
        System.out.println("``````````````````````````````````````````````````````");
        for (int j = 0; j < index.length; j++) { 
            // bit 位上是 1 , 说明有他表示的数,加上去。
            if(index[j] == 1){
                double temp = 2;
                for (int k = 1; k < j; k++) {
                    temp = temp * 2;
                }
                temp = 1 / temp;
                System.out.print("temp = " + temp + "    ;  ");
                res = res + temp;
                System.out.println("res = " + res);
            }
        }
        System.out.println("``````````````````````````````````````````````````````");
        System.out.println("final res : " + res);

本文地址:https://blog.csdn.net/weixin_43201538/article/details/107433982

相关标签: 计算机组成原理