在Java中使用双倍

pus*_*tic 57 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();
}
Run Code Online (Sandbox Code Playgroud)

但是,结果令人困惑:

System.out.println(round(2.655d,2)); // -> 2.65
System.out.println(round(1.655d,2)); // -> 1.66
Run Code Online (Sandbox Code Playgroud)

为什么要提供这个输出?我正在使用jre 1.7.0_45.

Bor*_*ris 79

你必须更换

BigDecimal bigDecimal = new BigDecimal(d);
Run Code Online (Sandbox Code Playgroud)

BigDecimal bigDecimal = BigDecimal.valueOf(d);
Run Code Online (Sandbox Code Playgroud)

你会得到预期的结果:

2.66
1.66
Run Code Online (Sandbox Code Playgroud)

Java API的解释:

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

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

  • +1,我不知道.但是你应该解释[why](http://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html#BigDecimal%28double%29),它们会有所作为. (2认同)
  • @zapl: - 我试着在我的回答中解释一下!如果我错过任何事情,请告诉我 (2认同)

Rah*_*thi 26

您可以尝试更改您的程序,如下所示: -

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

示例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
Run Code Online (Sandbox Code Playgroud)

BigDecimal.valueOfnew BigDecimalJoachim Sauer的话来说,你得到预期结果的原因是什么

BigDecimal.valueOf(double)将使用传入的double值的规范String表示来实例化BigDecimal对象.换句话说:BigDecimal对象的价值将是你所看到的System.out.println(d).

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

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

来自Java Doc:

BigDecimal.valueOf(双VAL) - double转换为BigDecimal,使用Double.toString(double)方法提供的double规范的字符串表示.

新BigDecimal(双val) -

将double转换为BigDecimal,它是double的二进制浮点值的精确十进制表示形式.返回的BigDecimal的比例是最小值,使得(10scale×val)是整数.笔记:

  • 这个构造函数的结果可能有点不可预测.有人可能会假设在Java中编写新的BigDecimal(0.1)会创建一个
    BigDecimal,它恰好等于0.1(未缩放值为1,
    标度为1),但它实际上等于0.1000000000000000055511151231257827021181583404541015625.这是因为0.1不能精确地表示为double(或者,
    就此而言,作为任何有限长度的二进制分数).因此,
    传递给构造函数的值并不完全等于0.1,尽管有外观.
  • String构造,在另一方面,是完全可以预测的:写入new BigDecimal("0.1")将创建一个BigDecimal,它正好等于0.1,正如人们所期望的那样.因此,通常建议优先使用String构造函数.
  • 当double必须用作BigDecimal的源时,请注意此构造函数提供了精确的转换; 它不会产生
    与使用
    Double.toString(double)方法将double转换为String 然后使用BigDecimal(String)
    构造函数相同的结果.要获得该结果,请使用static valueOf(double)
    方法.


Mar*_*aux 18

这个测试用例结果非常明显:

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();
}
Run Code Online (Sandbox Code Playgroud)

输出:

Before round: 2.654999999999999804600747665972448885440826416015625
After round: 2.65
Rounded: 2.65

Before round: 1.6550000000000000266453525910037569701671600341796875
After round: 1.66
Rounded: 1.66
Run Code Online (Sandbox Code Playgroud)

一个肮脏的黑客来修复它将分两步:

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();
}
Run Code Online (Sandbox Code Playgroud)

这里,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
Run Code Online (Sandbox Code Playgroud)

  • 这看起来像一个 hack,但它与 `valueOf` 使用的过程非常相似,只是更明确。转换为字符串进行第一次舍入。 (2认同)

Abi*_*san 8

API所述

  1. 这个构造函数的结果可能有点不可预测.有人可能会假设在Java中编写新的BigDecimal(0.1)会创建一个BigDecimal,它恰好等于0.1(未缩放值为1,标度为1),但它实际上等于0.1000000000000000055511151231257827021181583404541015625.这是因为0.1不能精确地表示为double(或者,就此而言,作为任何有限长度的二进制分数).因此,传递给构造函数的值并不完全等于0.1,尽管有外观.

  2. 另一方面,String构造函数是完全可预测的:写入新的BigDecimal("0.1")会创建一个BigDecimal,它正好等于0.1,正如人们所期望的那样.因此,通常建议优先使用String构造函数.

  3. 当double必须用作BigDecimal的源时,请注意此构造函数提供了精确的转换; 它不会产生与使用Double.toString(double)方法将double转换为String然后使用BigDecimal(String)构造函数相同的结果.要获得该结果,请使用static valueOf(double)方法.

这是因为不能准确地代表双重价值.所以你必须使用BigDecimal bigDecimal = BigDecimal.valueOf(d);而不是BigDecimal bigDecimal = new BigDecimal(d);


ski*_*iwi 7

舍入一个doubleresp Double本身没有多大意义,因为double数据类型不能舍入(很容易,或者根本不是?).

你在做什么是:

  1. 在分隔符后面Double d输入一个输入和int precise一些数字.
  2. BigDecimal从中创建一个d.
  3. BigDecimal正确.
  4. 返回double那个BigDecimal没有应用舍入值的值.

你可以采取两种方式:

  1. 你可以返回一个BigDecimal代表圆角双精度的东西,然后决定你用它做什么.
  2. 你可以返回一个String圆形代表BigDecimal.

这两种方式都是有道理的.


Lev*_*Lev 6

十进制数不能精确表示为double.

所以2.655最终是这样的:2.65499999999999980460074766597

而1.655最终是这样的:1.655000000000000026645352591

  • 这不准确;*无法表示某些/多个*十进制数. (5认同)