`

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相加出现的怪事 Java 中的浮点数类型 float 和 double 在进行运算时会出现不精确的问题,这是因为计算机无法精确地表示十进制小数。这种问题不仅存在于 Java 中,在其它许多编程语言中也存在。 问题的...

    test2_JavaDouble类型比较大小_java编程_

    标题"test2_JavaDouble类型比较大小_java编程_"提示我们关注的是Java中`Double`类型的比较操作。通常,我们可能会像处理整数那样直接使用`&gt;`或`来比较两个`Double`值,例如`double a = 0.1; double b = 0.2; if (a +...

    多项式相加 java链表实现

    总结来说,用Java链表实现多项式相加和相乘,主要步骤包括: 1. 创建`Node`类,表示多项式项。 2. 创建`LinkedList`类,表示多项式链表,并实现链表的基本操作。 3. 实现`insertA`方法,将一个多项式插入到另一个...

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

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

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

    Java中Double除保留后小数位的几种方法 Java中Double除保留后小数位的几种方法是指在Java编程语言中,对Double类型数据进行保留小数位的操作。这种操作在实际开发中非常常见,例如在统计成绩、金融计算、科学计算等...

    程序中两个Double类型相加出现误差的解决办法

    在编程中,尤其是在涉及到金融计算或者精确度要求高的领域,我们常常会遇到浮点数(如Double类型)相加时出现误差的问题。这是因为计算机在表示和计算浮点数时采用了二进制,而十进制的浮点数转换为二进制可能会导致...

    java实验大数相加.doc

    ### Java实验大数相加知识点总结 #### 一、实验目的 本次实验旨在通过一系列具体的编程任务,加深学生对Java语言的理解与应用能力。具体包括: - 进一步熟悉Java的基本语法及其内置方法。 - 加强对Java程序结构的...

    java中double类型运算结果异常的解决方法

    在Java编程语言中,`double`类型用于表示浮点数,它在大多数计算中能提供较高的精度和广泛的数值范围。然而,当你对`double`类型的数值进行运算时,可能会遇到结果值异常的情况,即计算结果并非预期的准确值。这通常...

    java代码-两个数字相加

    首先,Java中的基本数据类型包括整型(如int)、浮点型(如double)以及字符型(char)等。在本例中,我们关注的是整型或浮点型,因为数字相加通常涉及这些类型。例如,如果我们有两个整数a和b,我们可以使用加法...

    double类型精度丢失;double转换到64位整数

    `double`是C++、Java等编程语言中的一种数据类型,它用于表示带有小数部分的数值,通常可以精确到15位十进制数。然而,由于浮点数的二进制表示方式,当进行某些计算或转换时,可能会出现精度丢失的问题。标题提到的...

    Java用BigDecimal解决double类型相减时可能存在的误差

    在Java编程语言中,`double`类型用于表示浮点数,但存在精度问题,尤其是在进行数学运算时。这是因为`double`是基于二进制浮点数标准(IEEE 754)来存储和计算的,这可能导致计算结果与预期的十进制值存在微小的差异...

    java 公式计算汇总,直接调用其方法即可进行运算

    - 加法:在Java中,可以使用`+`运算符进行数值的相加。封装成方法后,例如`public double add(double a, double b)`,能够方便地对两个双精度浮点数进行求和。 - 减法:类似地,`-`运算符用于执行减法,对应的封装...

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

    - `complexAdd(ComplexNumber c)`:接收一个`ComplexNumber`对象作为参数,将当前对象与参数对象相加,并返回新的`ComplexNumber`对象,表示结果。 - `complexMinus(ComplexNumber c)`:与上述类似,但执行减法...

    java四则运算

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

    复数类java代码

    例如,复数加法可以通过将两个复数的实部相加、虚部相加来完成。 ```java public ComplexNumber add(ComplexNumber other) { double newReal = this.real + other.real; double newImaginary = this.imaginary +...

    java实现大数加法(BigDecimal)的实例代码

    与Java中的`int`, `long`, `float`, `double`等基本类型不同,`BigDecimal`不依赖于机器的浮点数表示,因此可以进行精确的算术运算,避免了浮点数运算常见的精度损失问题。 在给定的实例代码中,我们首先创建两个`...

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

    在Java中,我们可以用两个`double`类型的实例变量来表示这两个部分。 ```java public class Complex { private double realPart; // 实部 private double imagPart; // 虚部 // 构造函数 public Complex...

    java数据结构测试题及答案解析.doc

    - 第4行代码中,将浮点型f与double类型相加,需要强制类型转换,否则会导致编译错误。 8. **Java多线程并发控制**: - Java通过`synchronized`关键字实现并发控制,确保临界区的互斥访问。线程间交互推荐使用`...

Global site tag (gtag.js) - Google Analytics