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

2022-08-11 00:00:00 precision java bigdecimal

我正在用Java制作一个货币转换应用程序。其他一些很棒的StackOverflwians给了我一些建议,让我仔细阅读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的intCompt值为278(这对我来说很有意义)

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

这会导致生成所有其他计算并出错。

我刚开始使用BigDecimal,所以我可能犯了一个很明显的错误;但我很高兴学习,所以我征求了你们的建议:)

感谢任何帮助。


解决方案

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类的要点是为了避免floating-point技术中的inherent inaccuracies。浮点类型,如float/Floatdouble/Double以精度换取执行速度。相比之下,BigDecimal速度较慢,但很准确。

您的代码:

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

…正在传递double基元文本。在编译过程中,您键入的文本0.76135被解析为一个数字,特别是double(一个64位浮点值)。在这一点上,您引入了这种类型固有的不准确性。换句话说,从0.76135产生的double可能不再是确切的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的舍入和截断。已多次涉及堆栈溢出。

相关文章