简友广场散文想法

计算机组成原理 | 为什么浮点数运算不精确?(阿里笔试)

2020-05-25  本文已影响0人  彭旭锐

前言

最近在公众号阿里技术上看到一套孤尽老师出的 10道Java测试题(据说阿里 P7 工程师的答题正确率只有 50%) ,其中有几道题是关于浮点数的,聪明的你,在评论区留下答案吧。

(1)
float a = 0.125f; 
double b = 0.125d;
System.out.println((a - b) == 0.0); 
代码的输出结果是什么?

A. true
B. false
(2)
double c = 0.8;
double d = 0.7;
double e = 0.6;

那么 c-d 与 d-e 是否相等?

A. true
B. false
(3)
System.out.println(1.0 / 0); 的结果是什么?

A. 抛出异常
B. Infinity
C. NaN
(4)
System.out.println(0.0 / 0.0); 的结果是什么?

A. 抛出异常
B. Infinity
C. NaN
D. 1.0
(5) 引用自《技术之瞳》
以下数字在表示为double(8字节的双精度浮点数)时存在舍入误差的有:

A 100 
B 根号2 
C 10^30
D 0.1 
E 0.5
(6) 
写出float x 与“零值”比较的if语句

目录


1. 相关概念

关于浮点数的相关概念如下,在下面的分享中,我将不重复解释:

2. 计算机中数据的表示方法

你会发现前者的结果是0.30000000000000004,而后者的结果是0.3(当然了!)。那么,为什么计算机的准确度,连普通的电子计算器的都比不上?关键在于计算机与计算器使用了不同的数据表示方法

2.1 n 位二进制可以表示的信息量

对于整数来说,大家都知道8位有符号整数可以表示[-128,127],8位无符号整数可以表示[0,255],不管怎么样,8位二进制无论如何也只能表示256个整数。当需要表示257这个数,有且只有两个办法:

这就是计算机的自有属性,数字计算机只能处理离散数据,二进制的位数直接决定了它能表示的离散数据个数,也决定了它所能表示的信息个数,对于n位二进制数,它可以表示的信息量为2^N

同理,我们把问题域扩展到全体实数,8位二进制同样也只能表示256个实数。假如约定这样一种8位编码:最低两位为小数区域,其余是整数区域,这样就有:

000000.00 // 表示 0.0
000000.01 // 表示 0.25
000000.10 // 表示 0.5
000000.11 // 表示 0.75
000001.00 // 表示 1.0
000001.01 // 表示 1.25
... 此处省略250个数

我们发现,介于0.0到0.25的数字被跳过了,而即使把小数区域的位长扩大到8位、16位、甚至一个极大的位数,也无法充分表示介于0.0到0.25所有的数。这是因为,在0.0到0.25之间的数是连续的,有无限多个数,但是有限的N位长二进制最多只能表示2^N个信息量,有限的信息量无法表示无限的数据量,这就是现实世界与计算机世界的矛盾。

2.2 定点数表示

实数有两种表示格式,分别是定点数浮点数。像上面说的这种约定整数部分和小数部分为固定位置的格式,就是定点数表示。

2.3 浮点数表示

我们已经知道32位二进制可以表示的信息量有2^{32}\approx 4*10^9,但是很多语言都会宣称它们的32位单精度浮点数的数值范围约为-3.4*10^{38}~ 3.4*10^{38}(左右边界),这是因为采用了浮点数格式。

N=2^E*M

2.4 定点数和浮点数的区别

2.5 计算机表示实数的步骤

前面讲到相关概念时提到了实数的概念,具体如下:

复数的分类 示意图

一个虚数上相当于两个实数,所以我们只需要关心实数在计算机中的表示即可,将一个实数装载入计算机需要分为三个步骤:


3. IEEE 754 标准的浮点数

IEEE 二进制浮点数算术标准(IEEE 754)是广泛使用的浮点数运算标准,是大多数高级语言的现行浮点运算标准,例如C/C++、Java、JavaScript等。

3.1 一般格式

浮点数格式的关键是科学计数法格式:N = a * B^E,其中:

一个数的科学计数法表示是不唯一的,举个例子,对于二进制数1111.0000_{(2)}来说,以下都是合法的科学计数法表示:111.1*211.11*2^211110*2^{-1},但这些都不是规格化的表示,唯一规格化的表示为:1.111*2^3

对于一个科学计数法表示,当尾数a的整数部分有且仅有一位有效数字时,我们称它是规格化的。由于0在数字的最左边是无效的,而在二进制的世界里只有0和1,因此,二进制数使用规格化的科学计数法时,整数部分固定为1。

既然整数部分1是固定的,那么就没有必要存储整数部分的信息了。正因如此,IEEE 754 标准的浮点数采用隐藏位的策略,整数部分的1是隐含的,不需要占用一位比特,这样是使得尾数可以多一位有效数。

综上,IEEE 754 浮点数的一般格式如下:
N = (-1)^s*1.f*2^E

IEEE 754 标准的一般格式

现在,我们已经知道浮点数划分的三个区域,现在我们来看这三个区域是如何求值的:

举个例子,十进制数100_{(10)}转换为二进制为1.100100*2^6_{(2)}。这里推荐一个站点:浮点数转换器,它可以很方便地对比实数的真值与机器数表示,如下图所示:

3.2 两种常用格式

前面讲的是IEEE 754 浮点数的一般格式,其中最常用的是32位单精度浮点数64位双精度浮点数,在高级语言中通常代表floatdouble两种数据类型(例如C/C++、Java),在有些语言中只有一种数字格式number(例如JavaScript/TypeScript)。

3.3 特殊值

在 IEEE 754 标准规定指数区域全0 或 全1为特殊值,具体如下:


参考资料


推荐阅读

快点击右上角关注吧!你的点赞和关注真的对我非常重要!欢迎关注彭旭锐的Github!

上一篇 下一篇

猜你喜欢

热点阅读