浮点数
计算机中如何表示浮点数的?
计算机中的浮点数采用IEEE 754标准表示,常用到的有:单精度浮点数(32位)、双精度浮点数(64位)。
在IEEE 754标准中,浮点数以二进制的形式存储在计算机中,分为:符号位、指数和尾数。
- 符号位(1 位):0 表示正数,1 表示负数。
- 指数位:存储指数的偏移值,即用实际指数加上一个固定偏移值。
-
- 固定偏移值是为了避免处理正负指数的符号位,比如1.1×10^2和1.1×10^-2,指数分别为2和-2,为了让指数为均为整数,因为给指数加上固定的偏移值。
- 单精度浮点数指数为占8位,有符号范围是[-127, 128],因此单精度浮点数的固定偏移值为127。双精度浮点数指数为占11位,有符号范围是[-1023, 1024],因此双精度浮点数的固定偏移值为1023.
- 尾数位:单精度占 23 位,双精度占 52 位,存储二进制小数的有效数字(隐含整数部分 1,节省 1 位存储)。
为什么浮点数相加会有精度误差?
a := 0.1
b := 0.2
sum := a + b
fmt.Println(sum) //输出:0.30000000000000004
fmt.Println(int(sum)) // 输出:0
计算机中使用二进制表示小数,但不是所有小数都能够用二进制准确的进行表示,0.1、0.2等小数无法转换成有限长度的二进制小数,存储时会被截断,出现精度损失,实际上计算机中存储的已经不是0.1了,因此0.1+0.2的实际结果是0.30000000000000004。
为什么0.1是无限长度的小数?
十进制小数转换为二进制使用“乘2取整”的方法:
- 将小数部分乘以 2,取整数部分(0 或 1)作为二进制小数的下一位;
- 用剩余的小数部分重复步骤 1,直到小数部分为 0(此时为有限小数)或进入循环(此时为无限循环小数)。
0.1(十进制)转换过程如下:
0.1 × 2 = 0.2 0
0.2 × 2 = 0.4 0
0.4 × 2 = 0.8 0
0.8 × 2 = 1.6 1
0.6 × 2 = 1.2 1
0.2 × 2 = 0.4 0 开始循环
0.4 × 2 = 0.8 0
0.8 × 2 = 1.6 1
0.6 × 2 = 1.2 1
...(循环节为“0011”,无限重复)
根本原因:十进制与二进制的 “不兼容”
十进制小数x能被精确表示为二进制小数的前提是:x可以写成a/(2^n)的形式(其中a是整数,n是正整数),即分母必须是 2 的幂次。
而 0.1(即 1/10)的分母是 10,10 = 2 × 5,包含非 2 的质因数(5),因此无法表示为有限二进制小数,只能是无限循环形式。
BigDecimal解决精度问题
计算机中如何表示BigDecimal的?
BigDecimal实际上是将小数拆成整数、小数点位置进行存储的。比如0.1会被表示为:整数是1,小数位是1。
BigDecimal为什么可以避免精度丢失的问题?
- 浮点数是将小数转换为二进制存储的,有些小数转换为二进制时会出现精度损失。
- BigDecimal不存在小数转换的问题,小数相加时,首先将小数位补齐,再进行整数相加,不存在精度丢失的问题。
-
- 比如0.1+1.23,首先将0.1转换为0.10,再10+123= 133,小数位是2,最终结果为1.33。