`

java double 相加的怪事情

阅读更多

今天有个老同学问了这样一个问题
java中
double x = 0.9;
double y = 0.8;
double z = x+y;
System.out.println(z);
输出结果是多少?

我将程序跑了一遍,居然发现答案如下:
1.7000000000000002

有点惊讶,但是回想起来 觉得也不怪,以前在c里面也遇到过这样出现冗余码的问题,下面是我从网上找的一些资料,大家共同学习下。

编译运行下面这个程序会看到什么? 
public   class   Test{ 
          public   static   void   main(String   args[]){ 
                  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); 
          } 
}; 
  
你没有看错!结果确实是 
0.060000000000000005 
0.5800000000000001 
401.49999999999994 
1.2329999999999999 
  
Java中的简单浮点数类型float和double不能够进行运算。不光是Java,在其它很多编程语言中也有这样的问题。在大多数情况下,计算的结果是准确的,但是多试几次(可以做一个循环)就可以试出类似上面的错误。现在终于理解为什么要有BCD码了。 
这个问题相当严重,如果你有9.999999999999元,你的计算机是不会认为你可以购买10元的商品的。 
在有的编程语言中提供了专门的货币类型来处理这种情况,但是Java没有。现在让我们看看如何解决这个问题。 
  
    
  
四舍五入 
我们的第一个反应是做四舍五入。Math类中的round方法不能设置保留几位小数,我们只能象这样(保留两位): 
public   double   round(double   value){ 
          return   Math.round(value*100)/100.0; 

  
非常不幸,上面的代码并不能正常工作,给这个方法传入4.015它将返回4.01而不是4.02,如我们在上面看到的 
4.015*100=401.49999999999994 
因此如果我们要做到精确的四舍五入,不能利用简单类型做任何运算 
java.text.DecimalFormat也不能解决这个问题: 
System.out.println(new   java.text.DecimalFormat("0.00").format(4.025)); 
输出是4.02 
  
    
  
BigDecimal 
在《Effective   Java》这本书中也提到这个原则,float和double只能用来做科学计算或者是工程计算,在商业计算中我们要用java.math.BigDecimal。BigDecimal一共有4个够造方法,我们不关心用BigInteger来够造的那两个,那么还有两个,它们是: 
BigDecimal(double   val)   
                      Translates   a   double   into   a   BigDecimal.   
BigDecimal(String   val)   
                      Translates   the   String   repre   sentation   of   a   BigDecimal   into   a   BigDecimal. 
  
上面的API简要描述相当的明确,而且通常情况下,上面的那一个使用起来要方便一些。我们可能想都不想就用上了,会有什么问题呢?等到出了问题的时候,才发现上面哪个够造方法的详细说明中有这么一段: 
Note:   the   results   of   this   constructor   can   be   somewhat   unpredictable.   One   might   assume   that   new   BigDecimal(.1)   is   exactly   equal   to   .1,   but   it   is   actually   equal   to   .1000000000000000055511151231257827021181583404541015625.   This   is   so   because   .1   cannot   be   represented   exactly   as   a   double   (or,   for   that   matter,   as   a   binary   fraction   of   any   finite   length).   Thus,   the   long   value   that   is   being   passed   in   to   the   constructor   is   not   exactly   equal   to   .1,   appearances   nonwithstanding.   
The   (String)   constructor,   on   the   other   hand,   is   perfectly   predictable:   new   BigDecimal(".1")   is   exactly   equal   to   .1,   as   one   would   expect.   Therefore,   it   is   generally   recommended   that   the   (String)   constructor   be   used   in   preference   to   this   one.  
   可怕的.1,
原来我们如果需要精确计算,非要用String来够造BigDecimal不可!在《Effective   Java》一书中的例子是用String来够造BigDecimal的,但是书上却没有强调这一点,这也许是一个小小的失误吧。 
    
  
解决方案 
现在我们已经可以解决这个问题了,原则是使用BigDecimal并且一定要用String来够造。 
但是想像一下吧,如果我们要做一个加法运算,需要先将两个浮点数转为String,然后够造成BigDecimal,在其中一个上调用add方法,传入另一个作为参数,然后把运算的结果(BigDecimal)再转换为浮点数。你能够忍受这么烦琐的过程吗?下面我们提供一个工具类Arith来简化操作。它提供以下静态方法,包括加减乘除和四舍五入: 
public   static   double   add(double   v1,double   v2) 
public   static   double   sub(double   v1,double   v2) 
public   static   double   mul(double   v1,double   v2) 
public   static   double   div(double   v1,double   v2) 
public   static   double   div(double   v1,double   v2,int   scale) 
public   static   double   round(double   v,int   scale) 
  
  
  
附录 
  
  
源文件Arith.java: 
  
import   java.math.BigDecimal; 
/** 
    *   由于Java的简单类型不能够精确的对浮点数进行运算,这个工具类提供精 
    *   确的浮点数运算,包括加减乘除和四舍五入。 
    */ 
  
public   class   Arith{ 
  
          //默认除法运算精度 
          private   static   final   int   DEF_DIV_SCALE   =   10; 
  
  
          //这个类不能实例化 
          private   Arith(){ 
          } 
  
    
          /** 
            *   提供精确的加法运算。 
            *   @param   v1   被加数 
            *   @param   v2   加数 
            *   @return   两个参数的和 
            */ 
  
          public   static   double   add(double   v1,double   v2){ 
                  BigDecimal   b1   =   new   BigDecimal(Double.toString(v1)); 
                  BigDecimal   b2   =   new   BigDecimal(Double.toString(v2)); 
                  return   b1.add(b2).doubleValue(); 
          } 
  
          /** 
            *   提供精确的减法运算。 
            *   @param   v1   被减数 
            *   @param   v2   减数 
            *   @return   两个参数的差 
            */ 
  
          public   static   double   sub(double   v1,double   v2){ 
                  BigDecimal   b1   =   new   BigDecimal(Double.toString(v1)); 
                  BigDecimal   b2   =   new   BigDecimal(Double.toString(v2)); 
                  return   b1.subtract(b2).doubleValue(); 
          }   
  
          /** 
            *   提供精确的乘法运算。 
            *   @param   v1   被乘数 
            *   @param   v2   乘数 
            *   @return   两个参数的积 
            */ 
  
          public   static   double   mul(double   v1,double   v2){ 
                  BigDecimal   b1   =   new   BigDecimal(Double.toString(v1)); 
                  BigDecimal   b2   =   new   BigDecimal(Double.toString(v2)); 
                  return   b1.multiply(b2).doubleValue(); 
          } 
  
    
  
          /** 
            *   提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 
            *   小数点以后10位,以后的数字四舍五入。 
            *   @param   v1   被除数 
            *   @param   v2   除数 
            *   @return   两个参数的商 
            */ 
  
          public   static   double   div(double   v1,double   v2){ 
                  return   div(v1,v2,DEF_DIV_SCALE); 
          } 
  
    
  
          /** 
            *   提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 
            *   定精度,以后的数字四舍五入。 
            *   @param   v1   被除数 
            *   @param   v2   除数 
            *   @param   scale   表示表示需要精确到小数点以后几位。 
            *   @return   两个参数的商 
            */ 
  
          public   static   double   div(double   v1,double   v2,int   scale){ 
                  if(scale<0){ 
                          throw   new   IllegalArgumentException( 
                                  "The   scale   must   be   a   positive   integer   or   zero"); 
                  } 
                  BigDecimal   b1   =   new   BigDecimal(Double.toString(v1)); 
                  BigDecimal   b2   =   new   BigDecimal(Double.toString(v2)); 
                  return   b1.divide(b2,scale,BigDecimal.ROUND_HALF_UP).doubleValue(); 
          } 
  
    
  
          /** 
            *   提供精确的小数位四舍五入处理。 
            *   @param   v   需要四舍五入的数字 
            *   @param   scale   小数点后保留几位 
            *   @return   四舍五入后的结果 
            */ 
  
          public   static   double   round(double   v,int   scale){ 
                  if(scale<0){ 
                          throw   new   IllegalArgumentException( 
                                  "The   scale   must   be   a   positive   integer   or   zero"); 
                  } 
                  BigDecimal   b   =   new   BigDecimal(Double.toString(v)); 
                  BigDecimal   one   =   new   BigDecimal("1"); 
                  return   b.divide(one,scale,BigDecimal.ROUND_HALF_UP).doubleValue(); 
          } 
};  
分享到:
评论

相关推荐

    Java Double相加出现的怪事

    Java Double相加出现的怪事,浮点运算不精确问题

    防止Double加double后形成科学计数法问题

    防止Double加double后形成科学计数法问题

    Java中Double除保留后小数位的几种方法(小结)

    主要介绍了Java中Double保留后小数位的几种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

    java四则运算

    一个java类,该类可以进行加、减、乘、除四则运算,并且可以输出运算结果。此外,对于除法,如果被除数为0,该类可以报错。对于加、减、乘、除之外的运算符,该类应该告知无法处理。 编写一个包含主方法main的公共类...

    北科大2011Java设计实验报告2

    complexAdd(ComplexNumber c):将当前对象与c相加,返回所得的结果。 complexMinus(ComplexNumber c):将当前对象与形参复数对象相减,返回所得的结果。 complexMulti(ComplexNumber c):将当前对象与形参复数对象...

    java中复数类的实现

    Scanner reader = new Scanner(System.... System.out.println("相加的结果是:"+num1.Add(num2)); System.out.println("相减的结果是:"+num1.Jian(num2)); System.out.println("相乘的结果是:"+num1.Chen(num2));

    java复数四则运算-类的定义与对象.rar

    complexAdd(double c):当前复数对象与形式参数实数对象相加,所得的结果仍是复数值,返回给此方法的调用者。 complexMinus(ComplexNumber c) :当前复数对象与形式参数复数对象相减,所得的结果也是复数值,返回给...

    解决java数值范围以及float与double精度丢失的问题

    下面小编就为大家带来一篇解决java数值范围以及float与double精度丢失的问题。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧

    java 经典习题.doc

    例如2+22+222+2222+22222(此时共有5个数相加),几个数相加有键盘控制。 1.程序分析:关键是计算出每一项的值。 【程序9】 题目:一个数如果恰好等于它的因子之和,这个数就称为"完数"。例如6=1+2+3.编程 找出...

    java的用来处理数字方面的逻辑工具类

    * * 两个Double数相加 * * @param v1 * @param v2 * @return Double */ public static Double add(Double v1, Double v2) { } /** * * 两个Double数相减 * * @param v1 * @param v2 * @return ...

    达内 coreJava 习题答案

    System.out.println("请问要相加多少项?"); howMany = scanner.nextInt(); int sum=0; int a1=a; // 用来保存 a 的初始值 for (int i=1; i; i++){ sum+= a; a = 10*a +a1; // 这表示a 的下一项 // 每次 a ...

    java编写的计算器,能实现加减乘除

    java编写的计算器,能实现加减乘除。 //***该梦幻计算器与Windows附件自带计算器的标准版功能、界面相仿***// //******但还不支持键盘操作,如有需要,下一次等级将满足你**********// import java.awt.*; import ...

    整理后java开发全套达内学习笔记(含练习)

    注意:两个 byte 数相加,变 int 型) short 16bit, -2^15~2^15-1 (2^15=32768) int 32bit, -2^31~2^31-1 (2147483648,20亿,10位有效数字) long 64bit, -2^63~2^63-1 (900亿亿,20位有效数字) float 32bit, 9...

    编译器用Java编写程序

    else if(e2.getSource()==b6)//M+为将显示的数字与已经存储的数据相加要查看新的数字单击MR { m=m+Double.parseDouble(tf1.getText().trim()); } else//选择的是其他的按钮 { if(e2.getSource()==b22)//如果...

    2.Java基础语法.ppt

    2.相加运算 3.字符串拼接 在单独运算的时候 ++i,i++ 没有任何区别,都是加1操作相当于 i=i+1 i++;先运算,后加1(先运算后加减) ++i;先加1,后运算(先加减后运算) 2.逻辑运算符(&&,||,!,^) 逻辑非 ! ...

    java《Java程序设计》实验报告1:Java面向对象程序设计.doc

    实验目的:熟悉和掌握Java中的面向对象编程,包括类、属性、方法、构造方法。 2. 实验内容: 1. 定义一个矩形类Rectangle: (1)属性:长length、宽width (2)方法:getArea()求面积、getPerimeter()求周长,show...

    java .定义一个复数类 complex ,它的内部具有两个实例变量:realPart和imagPart,分别代表复数的实部和虚

    这是1.... (1)实现两个复数相加。复数加运算的原则是:复数的实部和虚部分别相加。 (2)实现两个复数相减。复数减运算的原则是:复数的实部和虚部分别相减。 (3)输出运算结果,判断是否正确。

    JVM面试总结

    操作这些原始数据类型数据的字节码(指令)本身就已经指出了操作数的数据类型,例如iadd、ladd、fadd和dadd指令都是把两个数相加,其操作数类型分别是int、long、float和double。虚拟机没有给boolean(布尔)类型设置...

    Java的数字精确计算问题-BigDecimal

    java的数字运算,偶尔会出现精度的问题,以下阐述的 java的...  java为了让float或者double类型能够精确的计算,提供了BigDecimal类。  示例如下: public static BigDecimal getBigDecimal(Doubleval){ //把d

    java 经典程序50道

    import java.util.*; public class lianxi08 { public static void main(String[] args) { long a , b = 0, sum = 0; Scanner s = new Scanner(System.in); System.out.print("输入数字a的值: "); a = s....

Global site tag (gtag.js) - Google Analytics