java 浮点数值计算误差

问题

当我们运行如下函数时

	public void test() {
		System.out.println(2.0-1.1); //=>0.8999999999999999
	}

我们发现其结果并非是我们预想的0.9

原因

其主要原因是浮点数值采用二进制系统表示,而在二进制系统中无法精确表示1/10。这就好像十进制无法精确表示1/3一样。如果需要在数值计算中不含有任何舍入误差,就应该使用BigDecimal类.

上述原因摘自“Java 核心技术 卷一 (第九版)  p35”

解决方案

public void test() {
    System.out.println(new BigDecimal("2.0").subtract(new BigDecimal("1.1")));
}

详解

首先我们要知道,计算机是如何将10进制的浮点类型转化成2进制进行计算的。这里我们需要知道一个概念,所有的浮点数值计算都遵循IEEE 745规范,所以我们来看一个该规范。

IEEE 745规范定义

IEEE 754是最广泛使用的二进制浮点数算术标准,被许多CPU与浮点运算器所采用。IEEE 754规定了多种表示浮点数值的方式,在本文档里只介绍32bits的float浮点类型。它被分为3个部分,分别是符号位S(sign bit)、指数偏差E(exponent bias)和小数部分F(fraction)。

其中S位占1bit,为bit31。S位为0代表浮点数是正数,S位为1代表浮点数是负数,比如说0x449A522C的S位为0,表示这是一个正数,0x849A522C的S位为1,表示这是一个负数。
E位占8bits,为bit23~bit30。E位代表2的N次方,但需要减去127,比如说E位为87,那么E位的值为2(87-127)=9.094947017729282379150390625e-13。
F位占23bits,为bit0~bit22。F位是小数点后面的位数,其中bit22是2-1=0.5,bit21是2-2=0.25,以此类推,bit0为2-23=0.00000011920928955078125。但F位里隐藏了一个1,也就是说F位所表示的值是1+(F位bit22~bit0所表示的数值),比如说F位是0b10100000000000000000001,只有bit22、bit20和bit0为1,那么F位的值为1+(2-1+2-3+2-23),为1.62500011920928955078125。
综上所述,从二进制数换算到浮点数的公式为:(-1)S×2E-127×(1+F)。但还有几个特殊的情形:
若E位为0并且F位也为0时表示浮点数0,此时浮点数受S位影响,表现出+0和-0两种0,但数值是相等的。比如二进制数0x00000000表示+0,二进制数0x80000000表示-0。
若E位为0并且F位不为0时浮点数为(-1)S×2-126×F,注意,E位的指数是-126,而不是0-127=-127,而且F位是0.xx格式而不是1.xx格式,比如0x00000001的浮点数为2-126×2-23=1.4012984643248170709237295832899e-45,而不是20-121×(1+2-23)。一旦E为不为0,从0变为1,不是增加2倍的关系,因为公式改变了。
若E位为255并且F位不为0时表示非数值,也就是说是非法数,例如0x7F800001。
若E位为255并且F位为0时表示无穷大的数,此时浮点数受S位影响,例如0x7F800000表示正无穷大,0xFF800000表示负无穷大。当我们使用1个数除以0时,结果将被记作0x7F800000。

上述定义摘自:http://bbs.chinaunix.net/thread-3746530-1-1.html

上述解释为float类型转化规则,与double类型规则一样,double类型就不在进行赘述了。

示例2.0-1.1预算

下面我们来回顾一下2.0-1.1的问题,我们先将这2个数转化为2进制

2.0=10.0000000000000000
1.1=01.0001100110011001  //此为无限循环小数
10.0000000000000000-01.0001100110011001=0.1110011001100111

转为10进制只能接近0.8999999999999999了。


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值