浮点数(float,double)表数范围和精度问题
浮点数(float,double)表数范围和精度问题
其实之前就遇到过浮点数精度丢失的问题,但是⼀直没有去深⼊研究,只是停留在知识记忆的层⾯,久⽽久之发现之前的东西都忘记了,之所以想要围绕这个问题来写⼀篇⽂章,是因为最近出现的⼀个bug,在项⽬中⼀个列表显⽰中id列后台数据是long类型,前端⽤easyui显⽰的时候发现数据显⽰错乱,显⽰的数据并不是数据库拿到的数据,之前是没有这个问题的,数据量多了以后就出现了这个问题,后来经过各种排查,苦⼼孤诣探索原因,然并卵,这时候偶然想到会不会是long类型长度超出的问题,于是把后台long类型字段转为String后再返回给前段,显⽰正常了。原来js中整数只能表⽰15位,多于15位就会出现精度问题;
JavaScript不是类型语⾔,与其他⾬多编程语⾔不同,js没有区分定义short,int,long这些类型,
只有Number⼀种数字类型,Number本质是浮点数,跟java中的double类似,64位,8字节的长度
1. 简单定义
在IEEE754标准中进⾏了单精度浮点数(float)和双精度数浮点数(double)的定义。
float有32bit,double有64bit。它们的构成包括符号位、指数位和尾数位。
2. 结构组成
类型符号位指数位尾数位floa(32bit)最左侧(第31位)第30-23位(占8bit)第22-0位(占23bit)
double(64bit)最左侧(第63位)第62-52位(占11bit)第51-0位(占52bit)
3. 取值范围
取值范围主要看指数部分:
float的指数部分有8bit(2^8),由于是有符号型,所以得到对应的指数范围-128~128。取值范围为:
-2^128到2^128,约等于-3.4E38 ~ +3.4E38 ;
double的指数部分有11bit(2^11) , 对应的指数范围-1024~1024。 取值范围为:-2^1024~2^1024,约 等于-1.797E308 ~ +1.797E308;
4. 精度
精度(有效数字)主要看尾数位:
float的尾数位是23bit,对应7~8位⼗进制数,所以有效数字有的编译器是7位,也有的是8位;
double的尾数位是52bit,对应15~16位⼗进制数,有效数字位15位或16位;
5. java中浮点数运算精度丢失问题
项⽬中⽤浮点数运算时通常会出现精度丢失的问题,⽐如:
public class Test {
public static void main(String[] args) {
double a = 0.2;
double b = 0.4;
System.out.println(a);
System.out.println(b);
System.out.println(a+b);
System.out.println(a*b);
}
}
输出结果为:
0.2
0.4
0.6000000000000001
0.08000000000000002
可以看到0.2+0.4并不等于0.6, 0.2*0.4也不等于0.08,这就是浮点运算中出现的精度丢失问题,他跟我们通常⼯程运算中以为的不同,这是由于计算机⼆进制存储的原因造成的,那么怎么解决呢?在项⽬中通常会避免使⽤double类型,⽽是使⽤BigDecimal类来进⾏浮点数的运算。
BigDecimal
public BigDecimal(double val)
将 double 转换为 BigDecimal,后者是 double 的⼆进制浮点值准确的⼗进制表⽰形式。
注:
1. 此构造⽅法的结果有⼀定的不可预知性。有⼈可能认为在 Java 中写⼊ new BigDecimal(0.1) 所创建的 BigDecimal 正好等于
0.1,但是它实际上等于 0.1000000000000000055511151231257827021181583404541015625。这是因为
double ⽆法准确地表⽰为 0.1(或者说对于该情况,不能表⽰为任何有限长度的⼆进制⼩数)。这样,传⼊ 到构造⽅法的值不会正好等于 0.1(虽然表⾯上等于该值)。
2. 另⼀⽅⾯,String 构造⽅法是完全可预知的:写⼊ new BigDecimal(“0.1”) 将创建⼀个 BigDecimal,它正好 等于预期的
0.1。因此,⽐较⽽⾔,通常建议优先使⽤ String 构造⽅法。
3. 当 double 必须⽤作 BigDecimal 的源时,请注意,此构造⽅法提供了⼀个准确转换;它不提供与以下操作相同的结果:先使⽤
解决⽅法
在需要精确的表⽰两位⼩数时我们需要把他们转换为BigDecimal对象,然后再进⾏运算。
使⽤BigDecimal(double val)构造函数时仍会存在精度丢失问题,通常使⽤BigDecimal(String val)
这就需要先把double转换为字符串然后在作为BigDecimal(String val)构造函数的参数。转换为BigDecimal对象
之后再进⾏加减乘除操作,这样精度就不会出现问题了。所以有关⾦钱数据存储都使⽤BigDecimal。
bigdecimal取值范围输出: 0.6 0.08import java.math.BigDecimal;
public class Test {
public static void main(String[] args) {
double a = 0.2;
double b = 0.4;
BigDecimal c = BigDecimal.valueOf(a); BigDecimal d = BigDecimal.valueOf(b); System.out.println(c.add(d));
System.out.println(c.multiply(d));
}
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论