您的位置:首页 > 其它

如果需要精确答案,请避免使用float和double

2018-01-02 17:55 531 查看

浮点型

Java的浮点型数据有两种,分别是占据4个字节的单精度浮点型float和占据8个字节内存的双精度浮点型double,如下应用所示,

float和double的包装类Float,Double同样以常量的方式定义了它们的基本类型所占据的内存大小以及存储数据的范围。

private static final Logger LOGGER= LogManager.getLogger();

/**
*
* 浮点数存储数据的内存大小以及极限
* @author tony ittimeline@163.com
* @date 2018-01-02-下午6:27
* @website wwww.ittimeline.net
* @see
* @since JDK9.0.1
*/

@Test
public void testFloatInfo(){

LOGGER.info("float占据的字节大小为"+Float.BYTES+"\tfloat存储的最小值为"+Float.MIN_VALUE+"\tfloat存储的最大值为"+Float.MAX_VALUE); //3.4028235E38 E38表示10的38次方

LOGGER.info("double占据的字节大小为"+Double.BYTES+"\tdouble存储的最小值为"+Double.MIN_VALUE+"\tdouble存储的最大值为"+Double.MAX_VALUE); //

}


浮点数在内存中的存储

任何数据类型在计算机的最底层都是采用二进制的方式存储的,然而二进制无法精确的表示浮点数,就好像十进制无法精确的表示1/3一样。

Java的浮点型数据遵循IEEE754标准,即采用二进制数据的科学计数法来存储浮点数。

对于float类型的数值(32位二进制数据),第一位是符号位,接下来八位表示指数,再接下来23位表示位数,

而对于double类型的数值(64位二进制数据),第一位也是符号位,接下来11位数表示指数,再接下来52位表示尾数。

Java浮点数的特点以及应用案例

Java中的浮点型数据的类型默认是double类型,如果想使用float类型,数值后面必须添加f后缀来表示。浮点数据可以使用十进制数据或者是科学计数法两种方式来表示,如下应用案例所示

private static final Logger LOGGER= LogManager.getLogger();

/**
*
* 浮点数的两种表示方法
* @author tony ittimeline@163.com
* @date 2018-01-02-下午6:29
* @website wwww.ittimeline.net
* @see
* @since JDK9.0.1
*/
@Test
public void testFloatValueType(){

//12.3默认是float类型,不能将表示范围大的值直接赋值给表示范围小的变量 添加f后缀后12.3可以使用float存储
float fltVal=12.3f;
LOGGER.info("fltVal = "+fltVal);

//十进制
double dbVal=Math.PI;
LOGGER.info("圆周率是"+dbVal);

//科学计数法表示法 e10表示10的10次方
double exponentVal=123.45e10;

LOGGER.info("exponentVal = "+exponentVal);

}


通常情况下float的数值有效位数为6位,double类型的数值有效位数是15位数,如果数值超过有效位数则会得到一个四舍五入的结果。

浮点数中还有三个特殊的值

当使用正数除以0.0得到的结果是正无穷大,通过Double或者Float的POSITIVE_INFINTY表示

当使用负数除以0.0得到的结果是负无穷大,通过Double或者Float的NEGATIVE_INFINITY表示

当使用0.0除以0.0或者对一个负数开方将得到一个非数,通过Double或者Float的NaN表示

如下应用程序所示

private static final Logger LOGGER= LogManager.getLogger();
/**
*
* 浮点数的数值精确度
* 三个特殊的浮点数
* @author tony ittimeline@163.com
* @date 2018-01-02-下午6:39
* @website wwww.ittimeline.net
* @see
* @since JDK9.0.1
*/
@Test
public void testFloatValue(){
//java的浮点数默认是是double类型,但是如果想要声明float类型的变量,需要在变量值后面加上f后缀
float floatVal=5.234_555_56f;
double doubleVal=floatVal;
/**
*  输出结果为5.234557 因为float只能精确到小数点后六位 输出结果做了四舍五入的处理
*/
LOGGER.info(" float val is "+floatVal);
/**
* 输出结果为234555721282959 因为double能精确到小数点后15位数,输出结果做了四舍五入的处理
*/
LOGGER.info("double val is "+doubleVal);//5.234555721282959

final double POSITIVE_INFINITY=1.0/0.0;
final double NEGATIVE_INFINITY=-1.0/0.0;

LOGGER.info("double的正无穷大的结果是"+POSITIVE_INFINITY);
LOGGER.info("float的正无穷大的结果是"+NEGATIVE_INFINITY);

final double DOUBLE_NEGATIVE_INFINITY=Double.NEGATIVE_INFINITY; //-1.0 / 0.0
final float FLOAT_NEGAIVE_INFINITY=Float.NEGATIVE_INFINITY;// -1.0f / 0.0f
//输出结果表示double和float的正无穷大是相等的
LOGGER.info(DOUBLE_NEGATIVE_INFINITY==FLOAT_NEGAIVE_INFINITY);

double NANVal=0.0/0.0;
LOGGER.info("0.0除以0.0的结果是一个非数,其结果是 = "+NANVal);

}


在Effecitve Java书籍中指出,float和double类型主要是为了科学计算和工程计算而设计的,它们没有提供完全精确的计算结果,因此不适合用于货币计算,应用程序如下所示

private static final Logger LOGGER= LogManager.getLogger();

/**
* 浮点数计算精度的问题
* @author tony ittimeline@163.com
* @date 2018-01-02-下午6:39
* @website wwww.ittimeline.net
* @see
* @since JDK9.0.1
*/
@Test
public void testFloatCalcutorPrecision(){
/*
* 浮点数默认都是double类型,而double的有效位数是小数点后15位
* 而在内部的存储采用的是二进制指数存储
* */
double dbVal=2.0-1.1;
LOGGER.info("浮点数计算精度的问题:2.0-1.1 = "+dbVal); //2.0-1.1 = 0.8999999999999999
}


为了解决float和double的运算结果的精度问题,java提供了java.math.BigDecimal类,它能提供任意精度的浮点运算,金融理财等项目(单位是元为单位)的浮点数计算时,推荐使用BigDecimal类来完成浮点数的计算,以保证获取正确的计算结果。

如果在金融理财等项目不想使用BigDecimal类获取精确地结果,那么可以使用分作为账户金额的计算单位,数据库(以MySQL为例)中对应以bigint存储金额即可。而在页面上显示时再将分转换成元即可。

private static final Logger LOGGER= LogManager.getLogger();

/**
* 使用BigDecimal来解决浮点数运算精度不准的问题
* @author tony ittimeline@163.com
* @date 2018-01-02-下午6:49
* @website wwww.ittimeline.net
* @see
* @since JDK9.0.1
*/
@Test
public void testBigDecimal(){

/**
* 使用字符串参数来构造BigDecimal对象
*/
BigDecimal constructorOfString1= new BigDecimal("2.0");
BigDecimal constructorOfString2= new BigDecimal("1.1");

BigDecimal constructorOfStringResult =constructorOfString1.subtract(constructorOfString2);

LOGGER.info("constructor of string result  is "+constructorOfStringResult);

/**
* 不能使用double来构造BigDecimal对象,不然即使使用BigDecimal,计算时也会产生精度不准的问题
*/

BigDecimal constructorOfDouble1=new BigDecimal(2.0);
BigDecimal constructorOfDouble2 =new BigDecimal(1.1);

BigDecimal constructorOfDoubleResult =constructorOfDouble1.subtract(constructorOfDouble2);

LOGGER.info("constructor of double result is "+constructorOfDoubleResult);
}


在使用BigDecimal时需要注意两点:

1: 不要使用double类型的参数来创建BigDecimal对象,那样即使使用BigDecimal来运算,运算结果也会产生精度不准的问题。

2: 同时BigDecimal是不可变的对象,这就意味着每次运算的结果都会产生一个新的BigDecimal对象。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐