当我执行以下代码时:
public class Test {
public static void main(String args[]){
DecimalFormat format = new DecimalFormat();
Double value = new Double(-1350825904190559999913623552.00);
StringBuffer buffer = new StringBuffer();
FieldPosition position = new FieldPosition(0);
format.format(new BigDecimal(value), buffer, position);
System.out.println(buffer);
}
}
这将正确打印-1,350,825,904,190,559,999,913,623,552
。我的代码确实经历了很多双精度,所以我不希望从双精度转换为大十进制。我认为BigDecimal 的处理时间很大。所以我做format.format(value, buffer, position)
我看到精度丢失了。我得到的输出是-1,350,825,904,190,560,000,000,000,000
.
我在这里做错了什么?有没有更好的方法来解决这个问题并仍然保持精度。我不想在这里处理大十进制,而只是使用小数。
有什么建议吗?
double
没有无限的精度,并且通过将double
转换为BigDecimal
,您无法获得比double
更高的精度(就像当您执行double r = 1/3;
0.0
时无法获得更高的精度int
,因为它将int
扩大到double
)。相反,您可以使用String
。类似的东西
DecimalFormat format = new DecimalFormat();
String value = "-1350825904190559999913623552.00";
System.out.println(format.format(new BigDecimal(value)));
问题出在输出格式上,特别是默认情况下如何将双精度转换为字符串。每个双精度数都有一个精确的值,但它也是小数部分范围的字符串到双精度转换的结果。在这种情况下,双精度的确切值为 -1350825904190559999913623552,但范围为 [-1350825904190560137352577024,-1350825904190559862474670080]。
双精度到字符串转换从该范围内选取有效数字最少的数字 -1.35082590419056E27。该字符串确实会转换回原始值。
如果您真的想查看确切的值,而不仅仅是足够的数字来唯一标识双精度,那么您当前的 BigDecimal 方法效果很好。
这是我用来计算这个答案中的数字的程序:
import java.math.BigDecimal;
public class Test {
public static void main(String args[]) {
double value = -1350825904190559999913623552.00;
/* Get an exact printout of the double by conversion to BigDecimal
* followed by BigDecimal output. Both those operations are exact.
*/
BigDecimal bdValue = new BigDecimal(value);
System.out.println("Exact value: " + bdValue);
/* Determine whether the range is open or closed. The half way
* points round to even, so they are included in the range for a number
* with an even significand, but not for one with an odd significand.
*/
boolean isEven = (Double.doubleToLongBits(value) & 1) == 0;
/* Find the lower bound of the range, by taking the mean, in
* BigDecimal arithmetic for exactness, of the value and the next
* exactly representable value in the negative infinity direction.
*/
BigDecimal nextDown = new BigDecimal(Math.nextAfter(value,
Double.NEGATIVE_INFINITY));
BigDecimal lowerBound = bdValue.add(nextDown).divide(BigDecimal.valueOf(2));
/* Similarly, find the upper bound of the range by going in the
* positive infinity direction.
*/
BigDecimal nextUp = new BigDecimal(Math.nextAfter(value,
Double.POSITIVE_INFINITY));
BigDecimal upperBound = bdValue.add(nextUp).divide(BigDecimal.valueOf(2));
/* Output the range, with [] if closed, () if open.*/
System.out.println("Range: " + (isEven ? "[" : "(") + lowerBound + ","
+ upperBound + (isEven ? "]" : ")"));
/* Output the result of applying Double's toString to the value.*/
String valueString = Double.toString(value);
System.out.println("toString result: " + valueString);
/* And use BigDecimal as above to print the exact value of the result
* of converting the toString result back again.
*/
System.out.println("exact value of toString result as double: "
+ new BigDecimal(Double.parseDouble(valueString)));
}
}
输出:
Exact value: -1350825904190559999913623552
Range: [-1350825904190560137352577024,-1350825904190559862474670080]
toString result: -1.35082590419056E27
exact value of toString result as double: -1350825904190559999913623552
它在格式化过程中不会丢失。它就在这里丢失了:
Double value = new Double(-1350825904190559999913623552.00);
double
只有大约 15.9 个有效的十进制数字。它不适合。转换浮点文本时,编译时存在精度损失。
不能用Double
准确表示 1350825904190559999913623552.00。如果您想知道原因,请浏览本文。
如果你想表示值,我建议使用你在问题中使用的代码:new BigDecimal( value )
,其中value
实际上是一个String
表示。