【问题标题】:Double subtraction precision issue双减法精度问题
【发布时间】:2012-03-28 15:40:03
【问题描述】:

我的同事做了这个实验:

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);
      }
 }

对于这个一年级的操作,我期望这个输出:

0.005

但出乎意料的输出是:

0.0050000000000001155

为什么这么简单的操作 double 会失败?如果 double 不是这项工作的数据类型,我应该使用什么?

【问题讨论】:

标签: java double


【解决方案1】:

double 在内部以分数形式存储在 binary 中 -- 就像 1/4 + 1/8 + 1/16 + ...

值0.005 -- 或值1.435 -- 不能存储为二进制的精确小数,所以double 不能存储精确值0.005,减去的值不是很精确.

如果您关心精确的十进制运算,请使用BigDecimal。

您可能还会发现this article 有用的阅读。

【讨论】:

  • 我有一个问题,如果我们有一个 double 并且我们在进行算术运算之前将它转换为 BigDecimal,它会起作用吗?不过要检查自己:)
  • @muasif80:不,不会。如果你想做精确的算术,你绝对不能碰double。
  • 好的,谢谢。我虽然尝试了一个如下共享的示例。但我会尽量听从你的建议。
  • “分数之和”的概念从何而来?你通常有指数和尾数,这样表示的值是 V=(2^e)*(1.m)
  • @Jack:是的。你认为m 是什么?
【解决方案2】:

double 和 float 是不完全是实数。

在任何范围内都有无限数量的实数,但只有有限数量的位来表示它们!因此,预计双精度和浮点数会出现舍入错误。

你得到的数字是浮点表示中可以用 double 表示的最接近的数字。

有关更多详细信息,您可能需要阅读this article [警告:可能是高级别的]。

您可能希望使用BigDecimal 来精确地得到一个十进制数[但是当您尝试获取1/3 时,您将再次遇到舍入错误]。

【讨论】:

    【解决方案3】:

    double 和 float 算术永远不会完全正确,因为“幕后”发生舍入。

    基本上双精度和浮点数可以有无限数量的小数,但在内存中它们必须由一些实数位表示。因此,当您执行此小数运算时,会发生舍入过程,如果您考虑所有小数,则通常会出现非常小的偏差。

    如前所述,如果您需要完全精确的值,请使用BigDecimal,它以不同的方式存储其值。 Here's the API

    【讨论】:

    • 说他们“永远不会”完全正确是不准确的。可以正确地说,当表示除分母不是 2 的幂的分数以外的任何内容时,它们永远不会完全正确。
    【解决方案4】:

    是的,它使用 BigDecimal 操作以这种方式工作

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

    【讨论】:

    • BigDecimal.valueOf 适用于某些 double 值,但不是全部。
    • 好的。我必须首先检查 double 值是如何出现的,然后在我的特定情况下。我将尝试根据您的见解进行更改。感谢您的回复。
    【解决方案5】:
    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)));       
        }      
    }
    

    输出:

    使用双精度的 2.15 和 1.0 之间的差异是:1.0499999999999998 2.15 和 1.0 使用 BigDecmial 的区别是:1.05

    【讨论】:

      【解决方案6】:
       //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();  
       }
      

      【讨论】:

      • 这没有回答问题。
      • 嗯,你是对的。试着在 BigDecimal 上做一些小例子。已经有人回答这个问题了。无需重复。
      • 在 SO 上,答案是为了提供一个完整的、自包含的答案。如果您觉得另一个答案中缺少某些内容,请在获得足够声誉后发表评论。如果你认为你可以写出更好的完整答案,那就去做吧。不要发布像这样的“半答案”。
      • 这不好,因为它只是设置值它不适用于负值减法
      猜你喜欢
      • 1970-01-01
      • 2010-10-08
      • 1970-01-01
      • 1970-01-01
      • 2016-12-19
      • 1970-01-01
      • 2016-06-14
      • 2019-06-07
      • 1970-01-01
      相关资源
      最近更新 更多