转载声明:文章来源:https://www.zhihu.com/question/56545018/answer/149620518
这个问题不简单,既跟浮点数的表示有关,也跟Java的设计机制有关。
对于0.1来说,其本质是1/10,那么若你用二进制表示它们,然后除的话,是这样的:1/1010,然而这一个是除不尽的,是无穷循环。
===> 0.0 00110011001100110011001100110011... 其中0011循环
而根据标准用科学表示法表示的话,就是
根据这幅图和计算公式
我们得到了exponent 是 -4,
所以,我们的 e 取值为 1019 (1019 - 1023 = -4),1019的11位二进制表示为0111 1111 011
然后 m,即multipler,为即1/16。而我们0.1是正数,所以s取值为0。
那么根据图就应该这样摆:
0 01111111011 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 (52位)
然后在fraction,部分则为
那么就应该是 1/2 + 1/16 + 1/32 + 1/256 + 1/512 + 1/4096+ 1/8192 + 1/65536 + 1/131072 + 1/1048576 + 1/2097152 + 1/8388608 + ....,约等于0.6,
然后再根据标准加上先前的隐式1,大约1.6,然后我们再乘以multipler 1/16,值大约是0.10000000000000000555111512312578270211815834045410156。
那么,若你在C++当中打印这个值我们也能得到这个结果:
#include <stdio.h>
int main(int argc, char *argv[])
{
printf("%.53f\n", 0.1);
return 0;
}
然后若你打印17位的话,就会做round四舍五入,于是会是0.10000000000000001,而这个17位一般是我们计算机浮点数中能表示的区分其它浮点数字的位数,这是最坏的情况,一般来说我们可以更少位数就区分开相邻的浮点数。
那么,是不是Java就直接取17位呢?
看3 * 0.1似乎是这样,但是4 * 0.1却得到了0.4,若是取17位,应该是0.40000000000000002这样的结果,但是Java是0.4. 这种情况其实也让我疑惑了一下。
然后,我去查看了Java println double的源代码,跟踪下去的时候发现println double的时候,是调用这句话:
public static String toString(double d) {
return FloatingDecimal.toJavaFormatString(d);
}
楼主的这篇文章写得很精彩,总结的很到位,支持一个
看过之后很多感触,唯有谢谢最简单也最真诚