浮点型变量和BigDecimal的运用

浮点型变量 float、double

float : 单精度浮点数

double : 双精度浮点数

两者的主要区别如下:

  • 1.在内存中占有的字节数不同

单精度浮点数在机内存占4个字节

双精度浮点数在机内存占8个字节

  • 2.有效数字位数不同

单精度浮点数有效数字8位

双精度浮点数有效数字16位

  • 3.数值取值范围

单精度浮点数的表示范围:-3.40E+38~3.40E+38

双精度浮点数的表示范围:-1.79E+308~-1.79E+308

  • 04.在程序中处理速度不同

一般来说,CPU处理单精度浮点数的速度比处理双精度浮点数快
    
如果不声明,默认小数为double类型,所以如果要用float的话,必须进行强转

例如:float a=1.3; 会编译报错,正确的写法 float a = (float)1.3;或者float a = 1.3f;(f或F都可以不区分大小写)

注意:float是8位有效数字,第7位数字将会四舍五入

特别注意:不要对浮点型变量进行比较和加减乘除运算

原因:因为浮点型变量会精度缺失,缺失原因请参考:我的博客

解决:使用BigDecimal类就能完全处理这个问题

  1. BigDecimal为不可变对象,每次增删改查操作都会返回新对象。并不会改变原来的对象

  2. 尽量避免传递double类型,有可能话,尽量使用int和String类型。

  3. 做乘除计算时,一定要设置精度和保留小数点位数。

  4. BigDecimal计算时,单独放到try catch内。

BigDecimal,不可变的、任意精度的有符号十进制数。BigDecimal 由任意精度的整数非标度值 和 32 位的整数标度(scale) 组成。如果为零或正数,则标度是小数点后的位数。如果为负数,则将该数的非标度值乘以 10 的负 scale 次幂。因此,BigDecimal 表示的数值是 (unscaledValue × 10-scale)。我们知道BigDecimal有三个主要的构造函数

public BigDecimal(double val) 将double表示形式转换为BigDecimalpublic BigDecimal(int val) 将int表示形式转换为BigDecimalpublic BigDecimal(String val) 将字符串表示形式转换为BigDecimal

通过这三个构造函数,可以把double类型,int类型,String类型构造为BigDecimal对象,在BigDecimal对象内通过BigIntegerintVal存储传递对象数字部分,通过int scale;记录小数点位数,通过int precision;记录有效位数(默认为0)。
BigDecimal的加减乘除就成了BigInteger与BigInteger之间的加减乘除,浮点数的计算也转化为整形的计算,可以大大提供性能,并且通过BigInteger可以保存大数字,从而实现真正大十进制的计算,在整个计算过程中,还涉及scale的判断和precision判断从而确定最终输出结果。
我们先看一个例子

BigDecimal d1 = new BigDecimal(0.6);
BigDecimal d2 = new BigDecimal(0.4);
BigDecimal d3 = d1.divide(d2);
System.out.println(d3);

大家猜一下,以上输出结果是?再接着看下面的代码

BigDecimal d1 = new BigDecimal(“0.6”);
BigDecimal d2 = new BigDecimal(“0.4”);
BigDecimal d3 = d1.divide(d2);
System.out.println(d3);

看似相似的代码,其结果完全不同,第一个例子中,抛出异常。第二个例子中,输出打印结果为1.5。造成这种差异的主要原因是第一个例子中的创建BigDecimal时,0.6和0.4是浮动类型的,浮点型放入BigDecimal内,其存储值为

0.59999999999999997779553950749686919152736663818359375
0.40000000000000002220446049250313080847263336181640625

这两个浮点数相除时,由于除不尽,而又没有设置精度和保留小数点位数,导致抛出异常。而第二个例子中0.6和0.4是字符串类型,由于BigDecimal存储特性,通过BigInteger记录BigDecimal的值,所以,0.6和0.4可以非常正确的记录为

0.6
0.4

两者相除得出1.5来。
对于第一个例子,如果我们想得到正确结果,可以这样来

BigDecimal d1 = new BigDecimal(0.6);
BigDecimal d2 = new BigDecimal(0.4);
BigDecimal d3 = d1.divide(d2, 1, BigDecimal.ROUND_HALF_UP);

现在看我们留下的那个问题,使用更优雅的方式解决元转化为分的方式,上一个问题中,我们通过传递s.length()从而获得精度,如果之前的s是double类型的,那边这样的方式就会有问题,通过上面的例子,我们可以调整为一下的通用方式

Double dd= Double.valueOf(s);
BigDecimal bigD = new BigDecimal(dd);
bigD = bigD.multiply(newBigDecimal(100)). divide(1, 1, BigDecimal.ROUND_HALF_UP);
Long result = bigD.longValue();

我们通过/1,然后设置保留小数点方式,以及设置数字保留模式,从而得到两个数乘积的小数部分。还有以下模式枚举常量摘要

ROUND_CEILING   向正无限大方向舍入的舍入模式。 
ROUND_DOWN   向零方向舍入的舍入模式。 
ROUND_FLOOR   向负无限大方向舍入的舍入模式。 
ROUND_HALF_DOWN   向最接近数字方向舍入的舍入模式,如果与两个相邻数字的距离相等,则向下舍入。 
ROUND_HALF_EVEN   向最接近数字方向舍入的舍入模式,如果与两个相邻数字的距离相等,则向相邻的偶数舍入。
ROUND_HALF_UP   向最接近数字方向舍入的舍入模式,如果与两个相邻数字的距离相等,则向上舍入。 
ROUND_UNNECESSARY   用于断言请求的操作具有精确结果的舍入模式,因此不需要舍入。(默认模式) 
ROUND_UP   远离零方向舍入的舍入模式。


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部