在Java中使用双倍

时间:2014-02-26 09:22:24

标签: 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(round(2.655d,2)); // -> 2.65
System.out.println(round(1.655d,2)); // -> 1.66

为什么要提供此输出?我正在使用jre 1.7.0_45。

6 个答案:

答案 0 :(得分:79)

你必须替换

BigDecimal bigDecimal = new BigDecimal(d);

BigDecimal bigDecimal = BigDecimal.valueOf(d);

您将获得预期的结果:

2.66
1.66

Java API的解释:

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

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

答案 1 :(得分:26)

您可以尝试更改您的程序: -

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

<强> Sample Ideone

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 获得预期结果的原因,用Joachim Sauer的话来说:

  

BigDecimal.valueOf(double)将使用传入的double值的canonical String representation来实例化BigDecimal对象。换句话说:BigDecimal对象的值将是您在System.out.println(d)时看到的内容。

     

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

因此导致您在节目中看到一些混淆

来自Java Doc:

  

BigDecimal.valueOf(double val) - 使用double的规范字符串表示将double转换为BigDecimal   由Double.toString(double)方法提供。

     

new BigDecimal(double val) -

     

将double转换为BigDecimal,这是精确的小数   double的二进制浮点值的表示。规模   返回的BigDecimal是最小的值,(10scale×   val)是一个整数。注意:

     
      
  • 此构造函数的结果可能有些不可预测。有人可能会认为在Java中编写新的BigDecimal(0.1)会创建一个   BigDecimal恰好等于0.1(未缩放的值为1,
      比例为1),但它实际上等于   0.1000000000000000055511151231257827021181583404541015625。这是因为0.1不能精确地表示为双精度(或者,为此   重要的是,作为任何有限长度的二进制分数)。因此,值为   传入构造函数的不完全等于   0.1,尽管有外表。
  •   
  • 另一方面,String构造函数是完全可预测的:编写新的BigDecimal(“0.1”)会创建一个BigDecimal   正如人们所预料的那样,它正好等于0.1。因此,它   通常建议使用String构造函数   偏爱这一个。
  •   
  • 当必须将double用作BigDecimal的源时,请注意此构造函数提供了精确的转换;它没有给出   使用
    将double转换为String的结果相同   Double.toString(double)方法然后使用BigDecimal(String)
      构造函数。要获得该结果,请使用静态valueOf(double)
      方法
  •   

答案 2 :(得分:18)

This test case最终变得不言自明:

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

修复它的肮脏黑客将是round in two steps

static Double round(Double d, int precise)
{
    BigDecimal bigDecimal = new BigDecimal(d);
    System.out.println("Before round: " + bigDecimal.toPlainString());
    bigDecimal = bigDecimal.setScale(15, RoundingMode.HALF_UP);
    System.out.println("Hack round: " + bigDecimal.toPlainString());
    bigDecimal = bigDecimal.setScale(precise, RoundingMode.HALF_UP);
    System.out.println("After round: " + bigDecimal.toPlainString());
    return bigDecimal.doubleValue();
}

这里,15略低于double在基数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

答案 3 :(得分:8)

API

所述
  
      
  1. 此构造函数的结果可能有些不可预测。有人可能会认为在Java中编写新的BigDecimal(0.1)会创建一个   BigDecimal正好等于0.1(未缩放的值为1,带有   比例为1),但它实际上等于   0.1000000000000000055511151231257827021181583404541015625。这是因为0.1不能完全表示为double(或者为此)   重要的是,作为任何有限长度的二进制分数)。因此,价值   传入构造函数的不完全等于   0.1,尽管有外观。

  2.   
  3. 另一方面,String构造函数是完全可预测的:编写新的BigDecimal(“0.1”)会创建一个BigDecimal,   正如人们所期望的那样,正好等于0.1。因此它是   一般建议使用String构造函数   偏好这个。

  4.   
  5. 当必须将double用作BigDecimal的源时,请注意此构造函数提供了精确的转换;它没有给出   使用。将double转换为String的结果相同   Double.toString(double)方法然后使用BigDecimal(String)   构造函数。要获得该结果,请使用静态valueOf(double)   方法

  6.   

这是因为无法准确表示双重值。因此,您必须使用BigDecimal bigDecimal = BigDecimal.valueOf(d);而不是BigDecimal bigDecimal = new BigDecimal(d);

答案 4 :(得分:7)

舍入double resp Double本身并没有多大意义,因为double数据类型无法被舍入(很容易,或者根本没有?)

你在做的是:

  1. 输入Double d作为输入,并在分隔符后面加int precise个数字。
  2. BigDecimal
  3. 创建d
  4. 正确地围绕BigDecimal
  5. 返回double的{​​{1}}值BigDecimal,其中不再应用舍入
  6. 你可以采取两种方式:

    1. 您可以返回代表圆角双精度的BigDecimal,然后决定使用它做什么。
    2. 您可以返回代表四舍五入String的{​​{1}}。
    3. 这些方法中的任何一种都有意义。

答案 5 :(得分:6)

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

所以2.655最终是这样的: 2.65499999999999980460074766597

而1.655最终是这样的: 1.655000000000000026645352591