Java编程经验---浮点型数值问题

前言

最近看了阿里的Java编程手册确实启发很多,有必要拾人牙慧补营养了。关于数值这个问题,确实是超出我自己的认知之外了,所以有意去仔细了解Java对于数值的处理,找出那些匪夷所思的坑。简单的来说当前的计算机是二进制的世界,而我们是十进制的世界,在转换的过程中,出于对存储空间的考虑,转换过程可能会有精度数据的丢失。所以在这里详细的分析一下。

原理解释

浮点数采用“尾数+阶码”的编码方式,类似于科学计数法的“有效数字+指数”的表示方式。

回顾一下什么是科学计数法?(下面来自百度百科的小例子)

运用科学记数法a×10^n的数字,它的精确度以a的最后一个数在原数中的数位为准。如:
13600 , 精 确 到 十 位 , 记 作 : 1.360 ∗ 1 0 4 13600,精确到十位,记作:1.360*10^4 136001.360104

13200 , 精 确 到 百 位 , 记 作 : 1.32 ∗ 1 0 4 13200 ,精确到百位,记作:1.32*10^4 132001.32104

322000 , 精 确 到 千 位 , 记 作 : 3.22 ∗ 1 0 5 322000,精确到千位,记作:3.22*10^5 3220003.22105

接着来看双精度浮点型数值
双精度浮点型数值

符号位用来记录数值的正负。

举例

现在举一个例子(来源阿里 孤尽)

后端的long型 -> 前端double Number

前端:
3 = 2 1 ∗ 1.1 3 = 2^1 * 1.1 3=211.1

5 = 2 2 ∗ 1.01 5 = 2^2 * 1.01 5=221.01

9 = 2 3 ∗ 1.001 9 = 2^3 * 1.001 9=231.001

( 2 53 + 1 ) = 2 53 ∗ 1.0000000...01 (2^{53}+1) = 2^{53}*1.0000000...01 (253+1)=2531.0000000...01

此时最后的1,会因为尾数(fraction)位置的限制而溢出。此时传到前端的数值精度将会被截断,此时数值会不准确。

且注意超过2^53 后,并非一定出错,类似于2^54这类不使用尾数的值,依然可以准确表示,所以会有随机产生Bug的可能。

解决办法

前后端传值的情况,如果数值精确度高,或数值巨大,推荐使用使用String进行传递。

再举一个例子(Java 手册)

后端浮点和浮点的计算与比较

float a = 1.0F - 0.9F;
float b = 0.9F - 0.8F;

if (a == b) {
    // 预期进入此代码块,执行其它业务逻辑
    // 但事实上a==b的结果为false
}

Float x = Float.valueOf(a);
Float y = Float.valueOf(b);

if (x.equals(y)) {
    // 预期进入此代码块,执行其它业务逻辑
    // 但事实上equals的结果为false
}
解决办法
/* (1) 指定一个误差范围,两个浮点数的差值在此范围之内,则认为是相等的。 float a = 1.0F - 0.9F; */
float b = 0.9F - 0.8F;
float diff = 1e-6F;

if (Math.abs(a - b) < diff) {
	System.out.println("true");
}
/* (2) 使用BigDecimal来定义值,再进行浮点数的运算操作。*/
BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("0.9");
BigDecimal c = new BigDecimal("0.8");
BigDecimal x = a.subtract(b);
BigDecimal y = b.subtract(c);

if (x.compareTo(y) == 0) {
	System.out.println("true");
}

如上所示BigDecimal的等值比较应使用compareTo()方法,而不是equals()方法。 说明:equals()方法会比较值和精度(1.0与1.00返回结果为false),而compareTo()则会忽略精度。

结尾

点滴积累,才能扎实成长。

时间有限,如有问题,及时更正。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值