简体   繁体   English

双减法精度问题

[英]Double subtraction precision issue

My coworker did this experiment:我的同事做了这个实验:

public class DoubleDemo {

      public static void main(String[] args) {
           double a = 1.435;
           double b = 1.43;
           double c = a - b;
           System.out.println(c);
      }
 }

For this first-grade operation I expected this output:对于这个一年级的操作,我期望这个输出:

0.005

But unexpectedly the output was:但出乎意料的是,输出是:

0.0050000000000001155

Why does double fails in such a simple operation?这么简单的操作,为什么double会失败? And if double is not the datatype for this work, what should I use?如果 double 不是这项工作的数据类型,我应该使用什么?

double is internally stored as a fraction in binary -- like 1/4 + 1/8 + 1/16 + ... double在内部存储为二进制的分数——比如1/4 + 1/8 + 1/16 + ...

The value 0.005 -- or the value 1.435 -- cannot be stored as an exact fraction in binary, so double cannot store the exact value 0.005 , and the subtracted value isn't quite exact.0.005 -- 或值1.435 -- 不能存储为二进制的精确分数,因此double不能存储精确值0.005 ,并且减去的值不是很精确。

If you care about precise decimal arithmetic, use BigDecimal .如果您关心精确的十进制算术,请使用BigDecimal

You may also find this article useful reading.您可能还会发现 这篇文章很有用。

double and float are not exactly real numbers . double 和 float不完全是实数

There are infinite number of real numbers in any range, but only finite number of bits to represent them!在任何范围内都有无限数量的实数,但只有有限数量的位来表示它们! for this reason, rounding errors is expected for double and floats.出于这个原因,双精度和浮点数预计会出现舍入误差。

The number you get is the closest number possible that can be represented by double in floating point representation.您得到的数字是最接近的数字,可以用浮点表示中的 double 表示。

For more details, you might want to read this article [warning: might be high-level].有关更多详细信息,您可能需要阅读 这篇文章[警告:可能是高级别的]。

You might want to use BigDecimal to get exactly a decimal number [but you will again encounter rounding errors when you try to get 1/3 ].您可能想要使用BigDecimal来精确地获得一个十进制数 [但是当您尝试获得1/3时,您将再次遇到舍入错误]。

double and float arithmetic are never going to be exactly correct because of the rounding that occurs "under the hood".由于“幕后”发生的舍入, doublefloat算法永远不会完全正确。

Essentially doubles and floats can have an infinite amount of decimals but in memory they must be represented by some real number of bits.本质上双精度和浮点数可以有无限数量的小数,但在内存中它们必须由一些实数表示。 So when you do this decimal arithmetic a rounding procedure occurs and is often off by a very small amount if you take all of the decimals into account.因此,当您执行此十进制算术时,会发生舍入过程,并且如果您将所有小数都考虑在内,通常会偏离很小的数量。

As suggested earlier, if you need completely exact values then use BigDecimal which stores its values differently.如前所述,如果您需要完全精确的值,则使用BigDecimal以不同方式存储其值。 Here's the API 这是 API

Yes it worked this way using BigDecimal operations是的,它使用 BigDecimal 操作以这种方式工作

private static void subtractUsingBigDecimalOperation(double a, double b) {
  BigDecimal c = BigDecimal.valueOf(a).subtract(BigDecimal.valueOf(b));
  System.out.println(c);
}

在此处输入图片说明

public class BigDecimalExample {

    public static void main(String args[]) throws IOException {

      //floating point calculation
      double amount1 = 2.15;
      double amount2 = 1.10;
      System.out.println("difference between 2.15 and 1.0 using double is: " + (amount1 - amount2));

      //Use BigDecimal for financial calculation
      BigDecimal amount3 = new BigDecimal("2.15");
      BigDecimal amount4 = new BigDecimal("1.10") ;
      System.out.println("difference between 2.15 and 1.0 using BigDecimal is: " + (amount3.subtract(amount4)));       
    }      
}

Output :输出

difference between 2.15 and 1.0 using double is: 1.0499999999999998 difference between 2.15 and 1.0 using BigDecmial is: 1.05使用 double 的 2.15 和 1.0 之间的差异是:1.0499999999999998 使用 BigDecmial 的 2.15 和 1.0 之间的差异是:1.05

 //just try to make a quick example to make b to have the same precision as a has, by using BigDecimal

 private double getDesiredPrecision(Double a, Double b){
     String[] splitter = a.toString().split("\\.");
     splitter[0].length();   // Before Decimal Count
     int numDecimals = splitter[1].length(); //After Decimal Count

     BigDecimal bBigDecimal = new BigDecimal(b);
     bBigDecimal = bBigDecimal.setScale(numDecimals,BigDecimal.ROUND_HALF_EVEN);

     return bBigDecimal.doubleValue();  
 }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM