使用Java BigDecimal仍然无法正确解决问题

5

我有一个方法,需要输入两个double值。在尝试将它们相加时,当超过某个阈值时,我得到的值是不正确的,因此我开始使用BigDecimal

然而,即使使用BigDecimal,我仍然得到不正确的值?

double value1 = 2789.45;
double value2 = 557.89;
System.out.println(BigDecimal.valueOf(value1 + value2));

输出打印
3347.3399999999997

当应该这样阅读

3347.34

即使value1value2在当前范围之外(它们是在另一个方法中计算的),我该如何正确地处理这个问题?

我应该使用四舍五入吗?

3个回答

8

我应该只使用四舍五入吗?

不是这样的,你正在经历double总和的精度丢失。


你先对双倍数进行求和 (value1 + value2),然后将双倍总和(已失去精度)转换为BigDecimal

为了避免这种情况,请改用以下方法:

double value1 = 2789.45;
double value2 = 557.89;
System.out.println(BigDecimal.valueOf(value1).add(BigDecimal.valueOf(value2)));

输出:

3347.34

在此处查看IDEONE DEMO


更新

-1. 不要使用仍然脆弱的BigDecimal.valueOf,而应该从一开始就使用BigDecimal,并使用new BigDecimal("2789.45")new BigDecimal("557.89")创建它们。一旦你使用了double文字,你就引入了不确定性。BigDecimal.valueOf尝试将其恢复,但并不总是有效。- Louis Wasserman

我并不完全同意这种说法。您不能硬编码创建新十进制数的值,我同意的是(如果OP可能的话)直接在String中读取值,以避免double精度丢失:

String value1 = "2789.45"; 
BigDecimal one = new BigDecimal(value1);
String value2 = "557.89";
BigDecimal two = new BigDecimal(value2);
System.out.println(one.add(two));

输出:

3347.34

这将避免Louis Wasserman注意到的问题。

新的工作演示


1
嘿,你比我快了 :) - Mena
不要使用仍然脆弱的BigDecimal.valueOf,而应该从一开始就使用BigDecimal并使用new BigDecimal("2789.45")new BigDecimal("557.89")进行创建。一旦使用了double字面量,就会引入不精确性。BigDecimal.valueOf试图将其恢复,但并不总是有效。 - Louis Wasserman
@LouisWasserman,你假设我无限制地创建这些值。在问题中,它们只是其他方法的产物,是任意的值。我在这里将它们声明为变量,以帮助说明,并避免在问题中添加数百行无关代码。 - Adam Jarvis
@LouisWasserman,您提议硬编码值而不是使用变量?您可以建议在String中读取变量,我同意避免双重舍入...但是硬编码它们?这从来不是一个好主意 - Jordi Castilla

1
由于编译器将首先计算总和,因此它将首先添加两个 doubles,从而丢失精度,你实际上正在做这件事。
double value1 = 2789.45;
double value2 = 557.89;
double sum = value1 + value2; // precision lost here
System.out.println(BigDecimal.valueOf(sum));

1
你在将变量分配给双精度浮点数时已经失去了精度。
考虑以下程序:
import java.math.BigDecimal;

public class Test {
  public static void main(String[] args) {
    System.out.println(new BigDecimal(2789.45));
    System.out.println(new BigDecimal("2789.45"));
  }
}

输出:

2789.4499999999998181010596454143524169921875
2789.45

在BigDecimal中进行加法运算可以得到输入的精确和,但是如果你先将其转换为double,则输入将变为最接近你写的值的双精度浮点数。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接