浅谈java的浮点数精度问题及如何解决精度缺失问题文档格式.docx
- 文档编号:21574583
- 上传时间:2023-01-31
- 格式:DOCX
- 页数:7
- 大小:22.68KB
浅谈java的浮点数精度问题及如何解决精度缺失问题文档格式.docx
《浅谈java的浮点数精度问题及如何解决精度缺失问题文档格式.docx》由会员分享,可在线阅读,更多相关《浅谈java的浮点数精度问题及如何解决精度缺失问题文档格式.docx(7页珍藏版)》请在冰豆网上搜索。
d
f;
5.double
d2
6.System.out.println("
f="
+
f);
7.System.out.println("
d="
d);
8.System.out.println("
d2="
d2);
9.
System.out.println(0.05+0.01);
System.out.println(1.0-0.42);
System.out.println(4.015*100);
System.out.println(123.3/100);
10.}
11.}
publicclassFloatDoubleTest{publicstaticvoidmain(String[]args){floatf=20014999;
doubled=f;
doubled2=20014999;
System.out.println("
+f);
+d);
+d2);
}}
得到的结果如下:
f=2.0015E7
d=2.0015E7
d2=2.0014999E7
从输出结果可以看出double可以正确的表示20014999,而float没有办法表示20014999,得到的只是一个近似值。
这样的结果很让人讶异。
20014999这么小的数字在float下没办法表示。
于是带着这个问题,做了一次关于float和double学习,做个简单分享,希望有助于大家对java浮点数的理解。
关于java的float和double
Java语言支持两种基本的浮点类型:
float和double。
java的浮点类型都依据IEEE754标准。
IEEE754定义了32位和64位双精度两种浮点二进制小数标准。
IEEE754用科学记数法以底数为2的小数来表示浮点数。
32位浮点数用1位表示数字的符号,用8位来表示指数,用23位来表示尾数,即小数部分。
作为有符号整数的指数可以有正负之分。
小数部分用二进制(底数2)小数来表示。
对于64位双精度浮点数,用1位表示数字的符号,用11位表示指数,52位表示尾数。
如下两个图来表示:
float(32位):
double(64位):
都是分为三个部分:
(1)一个单独的符号位s直接编码符号s。
(2)k位的幂指数E,移码表示。
(3)n位的小数,原码表示。
那么20014999为什么用float没有办法正确表示?
结合float和double的表示方法,通过分析20014999的二进制表示就可以知道答案了。
以下程序可以得出20014999在double和float下的二进制表示方式。
FloatDoubleTest3
3.double
8;
4.long
l
Double.doubleToLongBits(d);
5.System.out.println(Long.toBinaryString(l));
6.float
7.int
i
Float.floatToIntBits(f);
8.System.out.println(Integer.toBinaryString(i));
9.}
publicclassFloatDoubleTest3{publicstaticvoidmain(String[]args){doubled=8;
longl=Double.doubleToLongBits(d);
System.out.println(Long.toBinaryString(l));
floatf=8;
inti=Float.floatToIntBits(f);
System.out.println(Integer.toBinaryString(i));
输出结果如下:
Double:
100000101110011000101100111100101110000000000000000000000000000
Float:
1001011100110001011001111001100
对于输出结果分析如下。
对于都不double的二进制左边补上符号位0刚好可以得到64位的二进制数。
根据double的表示法,分为符号数、幂指数和尾数三个部分如下:
0100000101110011000101100111100101110000000000000000000000000000
对于float左边补上符号位0刚好可以得到32位的二进制数。
根据float的表示法,也分为符号数、幂指数和尾数三个部分如下:
01001011100110001011001111001100
绿色部分是符号位,红色部分是幂指数,蓝色部分是尾数。
对比可以得出:
符号位都是0,幂指数为移码表示,两者刚好也相等。
唯一不同的是尾数。
在double的尾数为:
0011000101100111100101110000000000000000000000000000,省略后面的零,至少需要24位才能正确表示。
而在float下面尾数为:
00110001011001111001100,共23位。
为什么会这样?
原因很明显,因为float尾数最多只能表示23位,所以24位的001100010110011110010111在float下面经过四舍五入变成了23位的00110001011001111001100。
所以20014999在float下面变成了20015000。
也就是说20014999虽然是在float的表示范围之内,但在IEEE754的float表示法精度长度没有办法表示出20014999,而只能通过四舍五入得到一个近似值。
总结:
浮点运算很少是精确的,只要是超过精度能表示的范围就会产生误差。
往往产生误差不是因为数的大小,而是因为数的精度。
因此,产生的结果接近但不等于想要的结果。
尤其在使用float和double作精确运算的时候要特别小心。
可以考虑采用一些替代方案来实现。
如通过String结合BigDecimal或者通过使用long类型来转换。
解决方案:
packageA;
import
java.math.BigDecimal;
/**
*
由于Java的简单类型不能够精确的对浮点数进行运算,这个工具类提供精
确的浮点数运算,包括加减乘除和四舍五入。
*/
public
class
Arith{
//默认除法运算精度
private
static
final
int
DEF_DIV_SCALE
=
10;
//这个类不能实例化
Arith(){
}
/**
提供精确的加法运算。
@param
v1
被加数
v2
加数
@return
两个参数的和
public
double
add(double
v1,double
v2){
BigDecimal
b1
new
BigDecimal(Double.toString(v1));
b2
BigDecimal(Double.toString(v2));
return
b1.add(b2).doubleValue();
提供精确的减法运算。
被减数
减数
两个参数的差
sub(double
b1.subtract(b2).doubleValue();
提供精确的乘法运算。
被乘数
乘数
两个参数的积
mul(double
b1.multiply(b2).doubleValue();
提供(相对)精确的除法运算,当发生除不尽的情况时,精确到
小数点以后10位,以后的数字四舍五入。
被除数
除数
两个参数的商
div(double
div(v1,v2,DEF_DIV_SCALE);
提供(相对)精确的除法运算。
当发生除不尽的情况时,由scale参数指
定精度,以后的数字四舍五入。
scale
表示表示需要精确到小数点以后几位。
v2,int
scale){
if(scale<
0){
throw
IllegalArgumentException(
"
The
must
be
a
positive
integer
or
zero"
);
b1.divide(b2,scale,BigDecimal.ROUND_HALF_UP).doubleValue();
提供精确的小数位四舍五入处理。
v
需要四舍五入的数字
小数点后保留几位
四舍五入后的结果
round(double
v,int
b
BigDecimal(Double.toString(v));
one
BigDecimal("
1"
b.divide(one,scale,BigDecimal.ROUND_HALF_UP).doubleValue();
};
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 浅谈 java 浮点 精度 问题 如何 解决 缺失