浮点数与 IEEE 754

文章将从以下几个问题出发,讨论浮点数与 IEEE 754 标准:

  • 二进制 0.1,用十进制表示的话是多少?十进制的 0.1,用二进制表示又是多少?
  • 为什么 0.1 + 0.2 = 0.30000000000000004?
  • 单精度和双精度浮点数的有效小数位分别是多少?
  • 单精度浮点数能表示的范围是什么?
  • 浮点数为什么会存在 -0?infinity 和 NaN 又是怎么表示的?

小数的二进制和十进制转换

二进制转十进制

和整数转换一样,采用各位数值和位权相乘。记住小数点后第一位是从 -1 开始即可。

十进制转二进制

十进制整数转二进制采用“除 2 取余,逆序排列”法,直到商为0为止

十进制小数转二进制采用“乘 2 取整,顺序排列”法,直到小数为0或达到精度要求

这样一来,有些十进制为有限小数转为二进制就成了无限循环小数了,例如:

(0.1)2=(0.5)10(0.1)10=(0.0 0011 0011)2\begin{matrix} (0.1)_{2} = (0.5)_{10} \\ (0.1)_{10} = (0.0 \ 0011 \ 0011\cdots)_2 \\ \end{matrix}

什么是浮点数

计算机中小数的表示法,其实有定点和浮点两种。

定点数:数的小数点固定在同一位置不变。

因为资源的限制,数学中的小数无法直接在计算机中准确表示,为了更好地表示便有了浮点数。这种表示方法类似于基数为10的科学记数法,是对小数的近似表示。简单来说,浮点数是相对于定点数而言的,小数点位置是浮动的。

维基百科中对浮点数的解释如下:

在计算机科学中,浮点(英语:floating point,缩写为FP)是一种对于实数的近似值数值表现法,由一个有效数字(即尾数)加上幂数来表示,通常是乘以某个基数的整数次指数得到。以这种表示法表示的数值,称为浮点数(floating-point number)。

一个浮点数 a 由两个数 m 和 e 来表示:

a=m×bea = m \times b^e

选择一个基数 b 和精度 p(即使用多少位来存储)。尾数 m 是形如 d.ddd…ddd 的 p 位数(每一位都为介于0到b-1之间的整数,包括0和b-1)。如果m的第一位是非0整数,m称作正规化的。e是指数。使用一个单独的符号位 s 来表示正负。

即浮点数是指用符号、尾数、基数和指数这四部分来表示的小数。

image.png

浮点数的 IEEE 754 表示

解释这个问题之前,先来看一个经典的问题:0.1+0.2=?

人来计算这个问题,答案无疑是 0.3 ;但是让计算机来解决这个问题,答案还是 0.3 吗?

我们来写个程序看一下:

C:

1
2
3
4
5
6
7
#include <stdio.h>

int main() {
double a = 0.1, b = 0.2;
printf("%.17f", a + b);
return 0;
}

C++:

1
2
3
4
5
6
7
8
9
#include <iostream>
#include <iomanip>

int main() {
double a = 0.1, b = 0.2;
std::cout << std::setiosflags(std::ios::fixed) << std::setprecision(17)
<< a + b << std::endl;
return 0;
}

Java:

1
2
3
4
5
6
public class Main {
public static void main(String[] argv) {
double a = 0.1, b = 0.2;
System.out.println(a + b);
}
}

Python:

1
2
3
a = 0.1
b = 0.2
print(a + b)

Go:

1
2
3
4
5
6
7
import "fmt"

func main() {
var a float64 = 0.1
var b float64 = 0.2
fmt.Println(a + b)
}

结果无一例外,结果均为:

1
0.30000000000000004

这就要回到 IEEE 754 标准关于浮点数的规定。

使用搜索:谷歌必应百度