java中float和double的大小比较

float说明

  • 在Java中float为单精度小数,占4个字节空间,其中1为表示符号位,8为表示指数为,剩下23位表示小数位。举个例子:1.234*1012 ,其中1.234为指数表示的小数位,12表示指数位(科学表示法中的指数学法),所以小数为23位在计算机中的大小为0~223,十进制范围则是:0~8388608,最多表示7为数小数,严格来说甚至只能表示6为的准确,因为0~223的十进制返回不是0 ~ 9999999。所以超过0 ~ 8388608的小数部分在计算机存储的float中会发生截取现象,导致与实际输入的大小不符的错误。
    下面我们通过一个测试代码来测试结果:
public class Test{

    public static void main(String[] args) {
		float f = 1/3f;
		System.out.println("1/3f = " + 1/3f);
		System.out.println("(1/3f == 0.3333334f) = " + (1f/3f == 0.3333334f));
		f +=1;
		System.out.println("f = " + f);
		System.out.println("f = " + (f - 1));
		System.out.println("1/3f = " + (1f/3f == f - 1));
    }

输出log如下:

1/3f = 0.33333334
(1/3f == 0.33333334f) = true
f = 1.3333334
f = 0.33333337
1/3f = false
  • 首先分析第一行输出,1/3f最后返回float类型的小数,结果为0.3333334,转为指数写法则为3.333334*10-1,小数为8位,其中前七位在准确范围内,第八位4不在,所以第八位是取得近似值。
  • 接下来是第二行的输出,直接比较,由于比较的值刚好等于第一行的输出的结果,所以在计算机内部转为2进制后数据相等。
  • 第三行是+1后的结果,我们发现小数位数还是8位,但是由于前面多了一个1,所以后面相应的少了一个3,符合float的精度结果,但是此时我们发现,数据其实已经存在了精度的丢失
  • 第四行-1则是回复+1之前的状态,发现此时小数位的状态已经发生改变,并且此时的值和+1之前的值已经不一致,但是按照正常逻辑来说,+1之后再-1数值应该是不可变的。所以在float计算过程中存在精度的问题。
  • 和上一行一样,直接比较原始计算值与+1后再-1的值是否相等

根据上面的实验分析可以得出,float数值的等于比较实用==是不可靠的。因为存在精度问题,所以float比较相等,都应该在指定的精度范围内比较,才具备可操作性,例如以下代码:


public class Test{
		private static final float PRECISION = 0.000001f;
		private static final float PRECISION2 = 0.00001f;
		private static final float PRECISION3 = PRECISION * 10000;

    public static void main(String[] args) {
		float f = 1/3f;
		System.out.println("1/3f = " + 1/3f);
		f +=1;
		System.out.println("f = " + f);
		System.out.println("f = " + (f - 1));
		System.out.println("(1f/3f + 1) - f = " + ((1f/3f + 1) - f < PRECISION));
		System.out.println("1f/3f - (f - 1) = " + (1f/3f - (f - 1) < PRECISION2));
		float f2 = 10000f/3f;
		System.out.println("10000f/3f = " + f2);
		System.out.println("10000f/3f - (f2 - 1) = " + ((10000f/3f -1) - (f2 - 1) < PRECISION3));
        executors.shutdown();
    }
}

输出结果:

1/3f = 0.33333334
f = 1.3333334
f = 0.33333337
(1f/3f + 1) - f = true
1f/3f - (f - 1) = true
10000f/3f = 3333.3333
10000f/3f - (f2 - 1) = true

对比上一个实验,我们此时比较大小都是在指定的精度范围内比较的,所以都是相等。因为对于超出精度范围的值,我们无法控制其值的大小,所以对于超出精度范围的比较没有实际意义,另外最后有个10000f/3f的大小比较是为了说明,精度范围是随着要比较的float目标值大小而改变的。

double说明

  • double值俗称双精度,与float非常相似,区别仅在于double的大小为8个字节,其中1个符号为,11位指数位,52位小数位,所以其小数为返回为0~252, 十进制范围为0~4 503 599 627 370 496,攻击16位,百分百保证精度的小数位数为17位。等于比较也是在指定精度范围内才具有比较意义。

代码中的使用

所以平时涉及到小数使用,特别是要求精确范围使用时,一般保留两位小数时,存储和计算都会默认使用整数,在实际显示的时候再除以100,得到精确的两位小数(比如商品价格)。在金融领域,一般要求精度较高,都会是由BigDecimal来计算大小,避免因为精度产生的误差。

你可能感兴趣的:(java基础)