Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/308.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Java中使双精度圆化_Java_Floating Point - Fatal编程技术网

在Java中使双精度圆化

在Java中使双精度圆化,java,floating-point,Java,Floating Point,我找到了一个很好的四舍五入解决方案: static Double round(Double d, int precise) { BigDecimal bigDecimal = new BigDecimal(d); bigDecimal = bigDecimal.setScale(precise, RoundingMode.HALF_UP); return bigDecimal.doubleValue(); } 然而,结果令人困惑: System.out.println(

我找到了一个很好的四舍五入解决方案:

static Double round(Double d, int precise) {
    BigDecimal bigDecimal = new BigDecimal(d);
    bigDecimal = bigDecimal.setScale(precise, RoundingMode.HALF_UP);
    return bigDecimal.doubleValue();
}
然而,结果令人困惑:

System.out.println(round(2.655d,2)); // -> 2.65
System.out.println(round(1.655d,2)); // -> 1.66
为什么它会给出这个输出?我正在使用jre 1.7.0_45。

如中所述

  • 此构造函数的结果可能有些不可预测。有人可能会认为,在Java中编写新的BigDecimal(0.1)会创建 BigDecimal正好等于0.1(未标度值为1,带 比例为1),但实际上等于 0.1000000000000000055511151231257827021181583404541015625. 这是因为0.1不能精确地表示为double(或者 物质,作为任何有限长度的二元分数)。因此,价值 传递给构造函数的不完全等于 0.1,尽管有外观

  • 另一方面,字符串构造函数是完全可预测的:编写新的BigDecimal(“0.1”)会创建一个BigDecimal 正如人们所期望的,正好等于0.1。所以是 通常建议在中使用字符串构造函数 我更喜欢这个

  • 当必须将double用作BigDecimal的源时,请注意此构造函数提供了精确的转换;它没有给出正确的答案 与使用 toString(Double)方法,然后使用BigDecimal(字符串) 构造器。要获得该结果,请使用静态值(double) 方法


  • 这是因为它不能准确地表示双值。因此您必须使用
    BigDecimal BigDecimal=BigDecimal.valueOf(d)而不是
    BigDecimal BigDecimal=新的BigDecimal(d)

    十进制数字不能用双精度表示

    因此,2.655最终是: 2.6549999999980460074766597

    而1.655最终是:
    1.655000000000000026645352591

    double
    resp
    double
    四舍五入本身没有多大意义,因为
    double
    数据类型不能四舍五入(很容易,或者根本不可能)

    您正在做的是:

  • 将一个
    双d
    作为输入,并在分隔符后面输入一个
    int-precision
    位数
  • 从该
    d
    创建一个
    BigDecimal
  • BigDecimal
    四舍五入
  • 返回该
    BigDecimal
    double
    值,该值不再应用舍入
  • 你可以走两条路:

  • 您可以返回一个表示四舍五入双精度的
    BigDecimal
    ,然后决定如何处理它
  • 您可以返回一个表示四舍五入的
    BigDecimal
    字符串
  • 这两种方法中的任何一种都是有意义的。

    最终都是不言自明的:

    public static void main (String[] args) throws java.lang.Exception
    {
        System.out.println("Rounded: " + round(2.655d,2)); // -> 2.65
        System.out.println("Rounded: " + round(1.655d,2)); // -> 1.66
    }
    
    public static Double round(Double d, int precise)
    {       
        BigDecimal bigDecimal = new BigDecimal(d);
        System.out.println("Before round: " + bigDecimal.toPlainString());
        bigDecimal = bigDecimal.setScale(precise, RoundingMode.HALF_UP);
        System.out.println("After round: " + bigDecimal.toPlainString());
        return bigDecimal.doubleValue();
    }
    
    输出:

    Before round: 2.654999999999999804600747665972448885440826416015625
    After round: 2.65
    Rounded: 2.65
    
    Before round: 1.6550000000000000266453525910037569701671600341796875
    After round: 1.66
    Rounded: 1.66
    
    要解决这个问题,一个肮脏的方法是:

    这里,
    15
    是双精度在基数10中可以表示的最大位数以下的一位。输出:

    Before round: 2.654999999999999804600747665972448885440826416015625
    Hack round: 2.655000000000000
    After round: 2.66
    Rounded: 2.66
    
    Before round: 1.6550000000000000266453525910037569701671600341796875
    Hack round: 1.655000000000000
    After round: 1.66
    Rounded: 1.66
    
    你必须更换

    BigDecimal bigDecimal = new BigDecimal(d);
    

    您将获得预期的结果:

    2.66
    1.66
    
    Java API的解释:

    BigDecimal.valueOf(double val)使用double.toString()方法提供的double的规范字符串表示形式。这是将double(或float)转换为BigDecimal的首选方法


    新的BigDecimal(double val)-使用双精度二进制浮点值的精确十进制表示,因此此构造函数的结果可能有些不可预测。

    您可以尝试如下更改程序:-

    static Double round(Double d, int precise) 
    {
    BigDecimal bigDecimal = BigDecimal.valueOf(d);
    bigDecimal = bigDecimal.setScale(precise, RoundingMode.HALF_UP);
    return bigDecimal.doubleValue();
    }
    

    Success  time: 0.07 memory: 381184 signal:0
    Rounded: 2.66
    Rounded: 1.66
    
    Success  time: 0.07 memory: 381248 signal:0
    Rounded: 2.66
    Rounded: 1.66
    
    使用
    BigDecimal.valueOf
    而不是使用
    new BigDecimal
    获得预期结果的原因,用以下文字表示:

    BigDecimal.valueOf(double)
    将使用传入的double值的值来实例化BigDecimal对象。换句话说,
    BigDecimal
    对象的值将是执行
    System.out.println(d)
    时所看到的值

    但是,如果使用新的BigDecimal(d)
    ,则BigDecimal将尽可能准确地表示双精度值。这通常会导致存储的数字比您想要的多得多

    因此会导致您在节目中看到的一些
    混乱

    从Java文档:

    -使用双精度的规范字符串表示法将双精度转换为BigDecimal 由Double.toString(Double)方法提供

    -

    将double转换为BigDecimal,即精确的十进制 表示双精度二进制浮点值。天平 返回的BigDecimal的最小值为(10scale×) val)是一个整数。注:

    • 此构造函数的结果可能有些不可预测。有人可能会认为,在Java中编写新的BigDecimal(0.1)会创建一个 BigDecimal正好等于0.1(未标度值为1,
      比例为1),但实际上等于 0.1000000000000000055511151231257827021181583404541015625. 这是因为0.1不能精确地表示为双精度(或者,对于双精度) 物质,作为任何有限长度的二元分数)。因此,值
      传递给构造函数的不完全等于 0.1,尽管有外观
    • 另一方面,字符串构造函数是完全可预测的:编写新的BigDecimal(“0.1”)将创建一个BigDecimal 这正好等于0.1,正如人们所期望的。因此, 通常建议在中使用字符串构造函数 我更喜欢这个
    • 当必须将double用作BigDecimal的源时,请注意此构造函数提供了精确的转换;它不会给出
      与使用
      将double转换为字符串的结果相同 Double.toString(Double)方法,然后使用BigDecimal(String)
      构造器。要获得该结果,请使用静态值(dou
      static Double round(Double d, int precise) 
      {
      BigDecimal bigDecimal = BigDecimal.valueOf(d);
      bigDecimal = bigDecimal.setScale(precise, RoundingMode.HALF_UP);
      return bigDecimal.doubleValue();
      }
      
      Success  time: 0.07 memory: 381184 signal:0
      Rounded: 2.66
      Rounded: 1.66
      
      Success  time: 0.07 memory: 381248 signal:0
      Rounded: 2.66
      Rounded: 1.66