从Java方法返回时,BigDecimal不保留实际值

时间:2018-10-30 19:53:49

标签: java precision bigdecimal

我正在用Java开发货币转换应用程序。其他一些了不起的StackOverflowians给了我一些建议,请我阅读BigDecimal的内容,以取代double来解决任何精度问题。

我有两种方法的系统;它将起始货币转换为美元,然后将美元值转换为目标货币。

注意,我的转化率是这样存储的:

// Conversion Rates - START (as of October 30, 2018 @ 3:19 AM)
// Rates obtained from exchange-rates.org

//Convert to United States Dollar rates
private final BigDecimal CAD_TO_USD = new BigDecimal(0.76135);
private final BigDecimal EUR_TO_USD = new BigDecimal(1.1345);
private final BigDecimal YEN_TO_USD = new BigDecimal(0.008853);
// Conversion Rates - END

在我用各自的BigDecimal替换了双打之后,我决定对其进行测试,然后看看它们的效果如何。

我的测试人员类运行以下方法来开始转换过程。

public BigDecimal convert()
{
    BigDecimal value;

    value = convertToUSD(); //Converts the current currency into USD 
    value = convertFromUSD(value);  //Converts the previous USD currency value into the destination currency

    return value;
}

当我输入示例变量(将2.78日元转换为加拿大元)时,我逐步执行了该过程,发现一切正常,直到返回值为止。

从前面提到的方法开始,运行convertToUSD()并将其编码如下

private BigDecimal convertToUSD()
{
    switch (fromCurrency)
    {
        case "USD":
            return fromQuantity.multiply(new BigDecimal(1));

        case "CAD":
            return fromQuantity.multiply(CAD_TO_USD);

        case "EUR":
            return fromQuantity.multiply(EUR_TO_USD);

        case "YEN":
            return fromQuantity.multiply(YEN_TO_USD);
    }

    return new BigDecimal(0);
}

如果所有值均正确传递,它会逐步减小到正确的大小写(“ YEN”),并且变量窗格显示“ fromQuantity” BigDecimal的intCompact值为278(这对我来说很有意义)< / p>

enter image description here

一旦断点返回到“ convert”方法,它就会变得一团糟。它不返回2.78 * 0.008853 = 0.0246,而是返回-9223372036854775808

enter image description here

这将导致所有其他计算产生并出错。

我是使用BigDecimal的新手,所以我可能犯了一个完全明显的错误;但是我很高兴学习,所以我寻求你们的建议:)

感谢您的协助。

2 个答案:

答案 0 :(得分:5)

tl; dr

使用String,而不是double文字。

new BigDecimal( "2.78" )           // Pass "2.78" not 2.78
.multiply(
    new BigDecimal( "0.008853" )   // Pass "0.008853" not 0.008853
)
.toString()
  

0.02461134

请勿传递浮点类型

BigDecimal类的重点是避免使用inherent inaccuracies技术中的floating-point。诸如float / Floatdouble / Double之类的浮点类型会牺牲执行速度的准确性。相反,BigDecimal慢但准确。

您的代码:

new BigDecimal( 0.76135 )
new BigDecimal( 1.1345 )
new BigDecimal( 0.008853 )

…正在传递double原语文字。编译期间,您键入的0.76135文本将解析为数字,尤其是double(64位浮点值)。到那时,您已经介绍了这种类型固有的错误。换句话说,从double产生的0.76135可能不再完全是0.76135

实例化后,立即转储您的BigDecimal实例。

System.out.println( new BigDecimal( 0.76135 ) );    // Passing a `double` primitive.
System.out.println( new BigDecimal( 1.1345 ) );
System.out.println( new BigDecimal( 0.008853 ) );
  

0.7613499999999999712230192017159424722194671630859375

     

1.13450000000000006394884621840901672840118408203125

     

0.0088529999999999997584154698415659368038177490234375

因此,通过创建double数字值,您调用了浮点技术并引入了不准确性。

使用字符串

解决方案?使用字符串,完全避免使用double类型。

在这些输入周围加上一些双引号,并瞧瞧

System.out.println( new BigDecimal( "0.76135" ) );  // Passing a `String` object.
System.out.println( new BigDecimal( "1.1345" ) );
System.out.println( new BigDecimal( "0.008853" ) );
  

0.76135

     

1.1345

     

0.008853

示例

您期望2.78 * 0.008853 = 0.0246。试试吧。

BigDecimal x = new BigDecimal( "2.78" );
BigDecimal y = new BigDecimal( "0.008853" );
BigDecimal z = x.multiply( y );
System.out.println( x + " * " + y + " = " + z );
  

2.78 * 0.008853 = 0.02461134

接下来,您应该学习如何用BigDecimal进行四舍五入。关于堆栈溢出已经有很多次了。

答案 1 :(得分:1)

问题是您假设intCompact代表您的float的非浮点版本。在某些情况下可以,但是在大多数情况下不会。

例如,我试图将您的问题减少到最低限度。

import java.math.BigDecimal;

class Main {
  public static void main(String[] args) {
    final BigDecimal YEN_TO_USD = new BigDecimal(0.008853);
    BigDecimal value = new BigDecimal(2.78);

    value = value.multiply(YEN_TO_USD);
    System.out.println(value);
  }
}

使用println在行上停顿,我得到以下信息:

break point

您看到intCompact与您的-9223372036854775808相同。但是在这种情况下,stringCache是期​​望值。

有关为什么应使用字符串而不是双精度字构造BigDecimal的信息,请参阅Basil的答案。