妈妈,我的电脑连加法都算错了
垃圾电脑,连一个加法都算不对
小朱: 怎么给儿子换电脑了,我那旧电脑她不想嘛。
小仙女: 你也知道旧电脑,连个加法都算不对,你好意思。
小朱: 纳尼,什么加法算不对。
示例
public static void main(String[] args) {
float f = 2000000000f;
System.out.println((f + 50f) == f);
}
小仙女: 虽然我没学过,但是没吃过猪肉,没见过猪跑吗?肯定是false
啊。但是得到的竟然是true
。
小朱: 😯,那你新电脑试过了吗?
小仙女: 那还用试吗,肯定没问题,我试下好了。
......
小仙女: 走,和我一起找他们理论去,垃圾电脑,毁我儿子。
小朱: ......
各位,你们的电脑是不是也出问题了。😄😄😄
解析
我们先来了解计算机怎么存储float的数据 float
的存储正是将4字节32位划分为了3部分来分别存储正负号,指数部分,小数部分
- 1.Sign(1位): 用来表示浮点数是正数还是负数,0表示正数,1表示负数。
- 2.Exponent(8位): 指数部分。为了同时表示正负指数以及他们的大小顺序,这里实际存储的是指数+127。
- 3.Mantissa(23位): 尾数部分。
我们先算2000000000f
的二进制
float f = 2000000000f;
int i = Float.floatToIntBits(f);
System.out.println(Integer.toBinaryString(i));
//运行结果
//1001110111011100110101100101000
结果应该在补一个0,正好32位。
有同学有可能对着很模糊,为什么2000000000f
这的二进制是这样的。下面我们来介绍一下,因为这个例子全是整数部分所以这么写
public static void main(String[] args) {
Integer x = 2000000000;
StringBuilder stringBuilder = new StringBuilder();
while (x >= 1){
//求余数
Integer y = x % 2;
x = x / 2;
stringBuilder.append(y);
}
//反转字符串
stringBuilder.reverse();
System.out.println(stringBuilder);
}
//结果
//1110111001101011001010000000000
1110111001101011001010000000000
可以写成1.11011100110101100101 * 2^30
第一步: Sign(1位)
因为是正数所以第一位是0
第二步: Exponent(8位)
因为指数是30+127=157
转成二进制是10011101
第三步: Mantissa(23位)
是11011100110101100101,000
这个地方不足23位补0,以逗号分割
综上所得第一步➕第二步➕第三步不足32位补0得到 0,10011101,11011100110101100101,000,
这就得到上面的值了(上面的值第一位0
隐藏了)。
到这里读者还是不明白为什么(f + 50f) == f
是true
,其实2000000000f == 2000000050f
。是不是不可思议。当你把2000000050f
用二进制表示出来你就知道了。
float f = 2000000050f;
int i = Float.floatToIntBits(f);
System.out.println(Integer.toBinaryString(i));
//运行结果
//1001110111011100110101100101000
没有任何变化,为什么呢?我们来按我们的方法去写。
public static void main(String[] args) {
Integer x = 2000000050;
StringBuilder stringBuilder = new StringBuilder();
while (x >= 1){
//求余数
Integer y = x % 2;
x = x / 2;
stringBuilder.append(y);
}
//反转字符串
stringBuilder.reverse();
System.out.println(stringBuilder);
}
//结果
//1110111001101011001010000110010
同上面 1110111001101011001010000110010
可以写成1.11011100110101100101000011001 * 2^30
第一步: Sign(1位)
因为是正数所以第一位是0
第二步: Exponent(8位)
因为指数是30+127=157
转成二进制是10011101
第三步: Mantissa(23位)
是11011100110101100101000,011001
因为超了23位,所以多余的部分是四舍五入,这里是遇1
进位所以得到的是11011100110101100101000
综上所得第一步➕第二步➕第三步超过32位四舍五入得到 0,10011101,11011100110101100101000,
这就得到上面的值了(上面的值第一位0
隐藏了)。
所以2000000000f == 2000000050f
是true。这下是不是理解了。去考考你的同学和同事,让他们怀疑一下自己的编程人生(大牛除外😄)。
最后留一个问题0.1 + 0.2 == 0.3
吗?之前讲过哦。