将 double 转换为 BigDecimal 并设置 BigDecimal Precision

在 Java 中,我希望获取一个双精度值,并将其转换为 BigDecimal,然后将其 String 值打印到一定的精度。

import java.math.BigDecimal;
public class Main {
public static void main(String[] args) {
double d=-.00012;
System.out.println(d+""); //This prints -1.2E-4


double c=47.48000;
BigDecimal b = new BigDecimal(c);
System.out.println(b.toString());
//This prints 47.47999999999999687361196265555918216705322265625
}
}

它能印出这么大的东西:

47.4799999999999996873611962655591821670532265625

而不是

47.48

我之所以要进行 BigDecimal转换,是因为有时候双精度值会包含很多小数位(比如 -.000012) ,而且当把双精度值转换成字符串时,会产生科学记数法为 -1.2E-4的值。我想用非科学符号存储 String 值。

我希望 BigDecimal 总是有两个这样的精度单位: “47.48”。BigDecimal 可以限制转换为字符串时的精度吗?

307183 次浏览

It prints 47.48000 if you use another MathContext:

BigDecimal b = new BigDecimal(d, MathContext.DECIMAL64);

Just pick the context you need.

You want to try String.format("%f", d), which will print your double in decimal notation. Don't use BigDecimal at all.

Regarding the precision issue: You are first storing 47.48 in the double c, then making a new BigDecimal from that double. The loss of precision is in assigning to c. You could do

BigDecimal b = new BigDecimal("47.48")

to avoid losing any precision.

It's printing out the actual, exact value of the double.

Double.toString(), which converts doubles to Strings, does not print the exact decimal value of the input -- if x is your double value, it prints out exactly enough digits that x is the closest double to the value it printed.

The point is that there is no such double as 47.48 exactly. Doubles store values as binary fractions, not as decimals, so it can't store exact decimal values. (That's what BigDecimal is for!)

The String.format syntax helps us convert doubles and BigDecimals to strings of whatever precision.

This java code:

double dennis = 0.00000008880000d;
System.out.println(dennis);
System.out.println(String.format("%.7f", dennis));
System.out.println(String.format("%.9f", new BigDecimal(dennis)));
System.out.println(String.format("%.19f", new BigDecimal(dennis)));

Prints:

8.88E-8
0.0000001
0.000000089
0.0000000888000000000

The reason of such behaviour is that the string that is printed is the exact value - probably not what you expected, but that's the real value stored in memory - it's just a limitation of floating point representation.

According to javadoc, BigDecimal(double val) constructor behaviour can be unexpected if you don't take into consideration this limitation:

The results of this constructor can be somewhat unpredictable. One might assume that writing new BigDecimal(0.1) in Java creates a BigDecimal which is exactly equal to 0.1 (an unscaled value of 1, with a scale of 1), but it is actually equal to 0.1000000000000000055511151231257827021181583404541015625. This is because 0.1 cannot be represented exactly as a double (or, for that matter, as a binary fraction of any finite length). Thus, the value that is being passed in to the constructor is not exactly equal to 0.1, appearances notwithstanding.

So in your case, instead of using

double val = 77.48;
new BigDecimal(val);

use

BigDecimal.valueOf(val);

Value that is returned by BigDecimal.valueOf is equal to that resulting from invocation of Double.toString(double).

Why not :

b = b.setScale(2, RoundingMode.HALF_UP);
BigDecimal b = new BigDecimal(c).setScale(2,BigDecimal.ROUND_HALF_UP);

In Java 9 the following is deprecated:

BigDecimal.valueOf(d).setScale(2, BigDecimal.ROUND_HALF_UP);

instead use:

BigDecimal.valueOf(d).setScale(2, RoundingMode.HALF_UP);

Example:

    double d = 47.48111;


System.out.println(BigDecimal.valueOf(d)); //Prints: 47.48111


BigDecimal bigDecimal = BigDecimal.valueOf(d).setScale(2, RoundingMode.HALF_UP);
System.out.println(bigDecimal); //Prints: 47.48