1

做笔试题的时候经常会遇到一些看起来比较简单,但是又比较考验基础的题目。

比如遇到的:

问输出结果

float f1=20f;
float f2=20.3f;
float f3=20.5f;

double d1=20;
double d2=20.3;
double d3=20.5;

// 问以下输出结果
System.out.println(f1==d1);
System.out.println(f2==d2);
System.out.println(f3==d3);
String s1 = "a";
String s2 = "b";
String s3 = "a" + "b"; // ab 常量池
String s4 = s1 + s2;   // new String("ab")
String s5 = "ab";
String s6 = s4.intern();


System.out.println(s3 == s4); // false
System.out.println(s3 == s5); // true
System.out.println(s3 == s6); // true

String x2 = new String("c") + new String("d"); 
x2.intern();
String x1 = "cd";

System.out.println(x1 == x2); //false

浮点数

先来第一部分浮点数。

我认为,如果对一个类型的问题需要犹豫很久,那么就是对这个问题的基础还认识不够深。

我们都知道,计算机存储小数的采用的是 浮点数。为什么叫浮点?顾名思义就是,表示小数点是可以浮动的

比如 1000.001 这个二进制数,可以表示成 1.000001 x 2^3,类似于数学上的科学记数法。

计算机使用的浮点数,一般采用的是 IEEE 754 制定的国际标准。

image.png

无论是32位的float,还是64位的 double,根据文档可以分为三部分:

  • 符号位:表示数字是正数还是负数,为 0 表示正数,为 1 表示负数;
  • 指数位:指定了小数点在数据中的位置,指数可以是负数,也可以是正数。
  • 尾数位:小数点右侧的数字,也就是小数部分,比如二进制 1.0001 x 2^(-2),尾数部分就是 0001

image.png

根据位数就可以知道,float 指数部分的最大有效值为254(有一个为特殊值), 小数部分最小为2的-23次方

image.png

double 的尾数部分是 52 位, 指数是11位,double 相比 float 能表示更大的数值范围。

要深刻了解题目的答案,我们需要了解转换截止。

那二进制小数,是如何转换成二进制浮点数的呢?

image.png

  1. 把小数点,移动到第一个有效数字后面,即将 1010.101 右移 3 位成 1.010101,右移 3 位就代表 +3,左移 3 位就是 -3。
  2. 指数位为把移动的位数再加上偏移量,float 的话偏移量是 127,相加后就是指数位的值
  3. 1.010101 这个数的小数点右侧的数字就是 float 里的尾数」,由于尾数位是 23 位,则后面要补充 0,所以最终尾数位存储的数字是 01010100000000000000000。

为什么要加上偏移量?

最根本的原因是因为计算机计算有符号数比无符号数麻烦

IEEE 标准规定单精度浮点的指数取值范围是 -126 ~ +127,为了把指数转换成无符号整数,就要加个偏移量,这样指数就不会出现负数了。

比如,指数如果是 8,则实际存储的指数是 8 + 127(偏移量)= 135,即把 135 转换为二进制之后再存储,而当我们需要计算实际的十进制数的时候,再把指数减去 偏移量。

而我们从浮点数转化为十进制,也很简单, 以float为例。

符号位 ✖️ (1 + 尾数位) ✖️ 2的(指数- 127)次方

二进制转换

而当二进制都无法表示完一个小数时,计算机只能以最逼近的小数表示,

比如十进制 0.1 在转换成二进制小数的时候,是一串无限循环的二进制数,计算机是无法表达无限循环的二进制数的,毕竟计算机的资源是有限。

比如我们可以利用这个网站: 二进制计算

可以简单的帮我们计算浮点数
image.png

我们可以来看一下题目的问题

float f1=20f;
float f2=20.3f;
float f3=20.5f;

double d1=20;
double d2=20.3;
double d3=20.5;

// 问以下输出结果
System.out.println(f1==d1); //true
System.out.println(f2==d2); //false
System.out.println(f3==d3); // true

输入进去, float 的 20.5, 和 double 的 20.5
float:
image.png

Double:
image.png

我们可以看到,除去double的小数点后很多的0,其实它们的数值是一样的。

所以我们可以很自然的联想到,循环的或者位数过长的小数,float和double的值是不一样的,因为double会多出来几十位的小数

20.3的double
image.png

20.3的float
image.png

同样的,在计算机中 0.1 + 0.2 并不等于完整的 0.3, 因为 0.1 和 0.2 都不是能用有限二进制位表达的数,那两个近似数相加,得到的必然也是一个近似数。

比如我们在浏览器的控制台中输入如下就可以看到,
image.png

字符串

只需记住几点

  • 直接声明的字符串在jvm的常量池中
  • intern可以放进常量池中
  • new String在对象堆中
String s1 = "a";
String s2 = "b";
String s3 = "a" + "b"; // ab 常量池
String s4 = s1 + s2;   // new String("ab")
String s5 = "ab";
String s6 = s4.intern();


System.out.println(s3 == s4); // false
System.out.println(s3 == s5); // true
System.out.println(s3 == s6); // true

String x2 = new String("c") + new String("d"); 
x2.intern();
String x1 = "cd";

System.out.println(x1 == x2); //true

weiweiyi
1k 声望123 粉丝