上图中虚线表示转换过程中存在精度丢失问题,因为与其它数据类型的十进制直接转换为二进制不同,float、double有其独特的数据结构,如下所示:
类型 | 符号位 | 指数 | 尾数 | 长度 |
---|---|---|---|---|
float | 1 | 8 | 23 | 32 |
double | 1 | 11 | 52 | 64 |
以float为例进行分析,例如:12.15623,执行以下步骤:
12/2 0 低位
6/2 0
3/2 1
1/2 1 高位
12 = 0000 1100B
0.15623*2 = 0.31246 0
0.31246*2 = 0.62492 0
0.62492*2 = 1.24984 1
0.24984*2 = 0.49968 0
0.49968*2 = 0.99936 0
0.99936*2 = 1.99872 1
0.99872*2 = 1.99744 1
0.99744*2 = 1.99488 1
...
0.15623 = 0.00100111111111101011000001110100101001110111001
存在无限循环或者小数位过多的情况,例如:float尾数位23bit表示,这也是浮点数无法精确表示的原因之一
\(\color{#4285f2}{0}\color{#ea4335}{1000001}\quad\color{#ea4335}{0}\color{#34a853}{1000010}\quad\color{#34a853}{01111111}\quad\color{#34a853}{11101011}\)
\(value = (-1)^{sign} \times (1+\sum_{i=1}^{23} b_{23-i}2^{-i})\times2^{e-127}\)
求和展开后如下:
\(value = (-1)^{sign} \times (1+b_{22}2^{-1}+b_{21}2^{-2}+...+b_{0}2^{-23}) \times2^{e-127}\)
其中\(b_n2^{n-23}\),当尾数位为0时结果都是0,可直接忽略。因此,尾数位求和只取尾数位等于1的即可。\(\color{#4285f2}{0}\color{#ea4335}{1000001}\quad\color{#ea4335}{0}\color{#34a853}{1000010}\quad\color{#34a853}{01111111}\quad\color{#34a853}{11101011}\)代入公式为:
\(value = (-1)^0 \times (1+2^{-1}+2^{-6}+2^{-9\sim-18}+2^{-20}+2^{-22}+2^{-23}) \times2^{130-127}\)
\(value = 1 \times (1+1/2+1/64+1/512+1/1024+1/2048+1/4096+1/8192+1/16384+1/32768+1/65536+1/131072+1/262144+1/1048576+1/4194304+1/8388608) \times2^3\)
\(value = 1 \times 1.5195287466049194 \times 8 = 12.156229972839355\)
还是以float举例,尾数一共23位,加上隐藏位1,实际有24位,所能表示的取值范围\([0,2^{24}-1]\),最大值为16777215,转化为二进制科学计数法为\(1.1111111 11111111 1111111 * 2^{23}\)。比这个大的数小数位已经是24位了,存储时超出的位数就被舍弃掉了。因此,可以得出以下结论:
最终结论:精度是由尾数的位数来决定的,浮点数在内存中是按科学计数法来存储的,其整数部分始终是1,由于它是不变的,故不能对精度造成影响。数学领域的精度一般指有效位数,即十进制位数。因此,结论如下:
印象中float的整数位+小数位一共8位,例如:
- (float)10/3=3.3333333
- (float)1/3=0.33333334
再看以下代码:
System.out.println(0.100000024f);
System.out.println(0.100000025f);
System.out.println(0.100000026f);
System.out.println(0.100000027f);
System.out.println(0.100000028f);
输出结果如下:
0.100000024
0.100000024
0.100000024
0.100000024
0.10000003
0.100000024~0.100000027输出都是0.100000024,小数位9位,0.100000028就变成0.10000003,小数位8位,为什么??
0.100000024=0.000110011001100110011010000 00000101011011110000100001011
0.100000025=0.000110011001100110011010000 00100111110010110010000000101
0.100000026=0.000110011001100110011010000 01001010001001110011011111111
0.100000027=0.000110011001100110011010000 01101100100000110100111111001
0.100000028=0.000110011001100110011010000 10001110110111110110011110011
由于尾数位只有23位,丢弃多余部分,0.100000024~0.100000027的数据结构完全一样,0.100000028由于第24位是1,舍弃时进位使23位变成了1,这就是差异部分
0.100000024如下:
\((1+1/2+1/16+1/32+1/256+1/512+1/4096+1/8192+1/65536+1/131072+1/524288)*2^{-4}=0.10000002384185791\)
0.100000028如下:
\((1+1/2+1/16+1/32+1/256+1/512+1/4096+1/8192+1/65536+1/131072+1/524288+1/8388608)*2^{-4}=0.10000003129243851\)
到这一步,可以看出其不同,但为什么输出0.100000024和0.10000003呢?
观察结果如下:
结论:
原文:https://www.cnblogs.com/sheung/p/14888429.html