容易忽视的基础——浮点精度

417 阅读2分钟

首先看下下面的题目

public static void main(String[] args) {
		float a = 1.0f;
		float b = 0.9f;
		float c = 0.8f;
		System.out.println((a-b)==(b-c));
	}

请问控制台输出的结果是:

A. true

B. false

不知道各位同学是怎么选的,换做之前的我肯定毫不犹豫的选择A了。但是,在ide中运行一下后控制台给出的结果是false。为啥呢?

浮点精度

浮点型数据类型,FLOAT 数据类型用于存储单精度浮点数或双精度浮点数。浮点数使用 IEEE(电气和电子工程师协会)格式。浮点类型的单精度值具有 4 个字节,包括一个符号位、一个 8 位 excess-127 二进制指数和一个 23 位尾数。尾数表示一个介于 1.0 和 2.0 之间的数。由于尾数的高顺序位始终为 1,因此它不是以数字形式存储的。此表示形式为 float 类型提供了一个大约在 -3.4E+38 和 3.4E+38 之间的范围。--引自百度

实际上我们的数字都是由2的幂数来凑成的。就如同上面的题目,两个减法得出的结果的二进制相似数是不同的所以控制台答应出来的结果是false。

下面再举个开发过程中遇到的一个坑

class Untitled {
	public static void main(String[] args) {
		float a = 3.01f;
		float b = a % 1.0f;
		System.out.println(b);
	}
}

····
0.00999999

没错它不是0.01,取模尽然是0.00999999接近1。所以float的运算是不安全的。那我们如何保证其计算结果不失真呢?

这个时候我们需要利用BigDecimal

class Untitled {
	public static void main(String[] args) {
	BigDecimal a =new BigDecimal("3.01");
    BigDecimal b =new BigDecimal("1");
    float c = a.remainder(b).floatValue();
    System.out.println(c);
	}
}

····
0.01

这里用字符串是因为字符串是按位来存储的,自然数都是可以用2进制精确表示的,所以这里使用字符串来实例化。详细可以参考BigDecimal官方文档