在Java中翻一番

我发现这个很好的解决方法:

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。

你必须更换

 BigDecimal bigDecimal = new BigDecimal(d); 

 BigDecimal bigDecimal = BigDecimal.valueOf(d); 

你会得到预期的结果:

 2.66 1.66 

Java API的说明:

BigDecimal.valueOf(double val) – 使用Double.toString()方法提供的double的规范化string表示forms。 这是将double(或float)转换为BigDecimal的首选方法。

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

你可以尝试像这样改变你的程序:

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

样品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值的规范string表示forms来实例化BigDecimal对象。 换句话说: BigDecimal对象的值将是您在执行System.out.println(d)时看到的内容。

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

因此导致你在程序中看到一些混乱

从Java文档:

BigDecimal.valueOf(double val) – 使用Double.toString(double)方法提供的double的规范化string表示forms将double转换为BigDecimal。

新的BigDecimal(双val)

将double翻译为BigDecimal,它是double的二进制浮点值的精确十进制表示forms。 返回的BigDecimal的比例是(10scale×val)是一个整数的最小值。 笔记:

  • 这个构造函数的结果可能有些不可预知。 有人可能会认为用Java编写新的BigDecimal(0.1)会创build一个
    BigDecimal正好等于0.1(非缩放值为1,
    比例为1),但实际上等于0.1000000000000000055511151231257827021181583404541015625。 这是因为0.1不能完全表示为双(或者,
    作为任何有限长度的二分数)。 因此,价值
    即将传递给构造函数的数据不完全等于0.1,尽pipe如此。
  • 另一方面,String构造函数是完全可预测的:编写新的BigDecimal(“0.1”)会创build一个等于0.1的BigDecimal,正如人们所期望的那样。 因此,通常build议使用String构造函数而不是这个。
  • 当必须使用double作为BigDecimal的源时,请注意,此构造函数提供了精确的转换; 它不给的
    相同的结果转换成一个string使用
    Double.toString(double)方法,然后使用BigDecimal(String)
    构造函数。 为了得到这个结果,使用static valueOf(double)
    方法。

这个testing用例最后不难理解:

 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 

一个肮脏的黑客修复它将是两个步骤 :

 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 

正如API所说

  1. 这个构造函数的结果可能有些不可预知。 有人可能会认为在Java中编写新的BigDecimal(0.1)会创build一个正好等于0.1的BigDecimal(非缩放值为1,标度为1),但实际上它等于0.1000000000000000055511151231257827021181583404541015625。 这是因为0.1不能完全表示为双(或者就此而言,作为任何有限长度的二进制分数)。 因此,传递给构造函数的值并不完全等于0.1,尽pipe如此。

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

  3. 当必须使用double作为BigDecimal的源时,请注意,此构造函数提供了精确的转换; 它不会给出使用Double.toString(double)方法将double转换为String然后使用BigDecimal(String)构造函数的结果。 为了得到这个结果,使用静态的valueOf(double)方法。

这是因为不能完全代表双倍的价值。 所以你必须使用BigDecimal bigDecimal = BigDecimal.valueOf(d); 而不是BigDecimal bigDecimal = new BigDecimal(d);

舍入一个double resp Double本身并没有什么意义,因为double数据types不能被舍入(很容易,或根本不?)。

你在做什么是:

  1. Double d作为input,在分隔符后面input一个int precise的数字。
  2. 从那个d创build一个BigDecimal
  3. 正确调整BigDecimal
  4. 返回BigDecimaldouble值,它不再适用舍入

你可以走两条路:

  1. 你可以返回一个BigDecimal来代表舍入的double,然后决定你用它做什么。
  2. 您可以返回一个表示四舍五入的BigDecimalString

这两种方法都是有意义的。

十进制数字不能完全用double来表示。

所以2.655就是这个:2.65499999999999980460074766597

而1.655最后是:1.655000000000000026645352591