我有以下程序(取自SO链接What is the best way to convert Dollars (Big Decimal) in Cents (Integer) in java?),将美元兑换成美分。但是,输出并不符合我的预期。
当前输出:
12345
8
预期输出:
12345
9
Main.java
public class Main {
private static final BigDecimal x100 = new BigDecimal(100);
static List<BigDecimal> nums = new ArrayList<>();
static BigDecimal a = new BigDecimal(123.45);
static BigDecimal b = new BigDecimal(0.09);
public static void main(String[] args) {
nums.add(a);
nums.add(b);
for (BigDecimal usd : nums) {
BigDecimal rounded = usd.setScale(2, BigDecimal.ROUND_DOWN);
BigDecimal bigDecimalInCents = rounded.multiply(x100);
int cents = bigDecimalInCents.intValueExact();
System.out.println(cents);
}
}
}
答案 0 :(得分:4)
您的问题是双打不能代表所有值。例如,9/100
只能近似。试试System.out.println(new BigDecimal(0.09))
。你应该得到0.0899999999999999966693309261245303787291049957275390625
。因此,您将其舍入为0.08,然后乘以100.
尝试new BigDecimal(String.valueOf(0.09))
修改强>
更好的解决方案是使用BigDecimal.valueOf(0.09)
,这是等效的。
来自BigDecimal(double)的javadoc:
将double转换为BigDecimal,它是double的二进制浮点值的精确十进制表示。
...
当double必须用作BigDecimal的源时,请注意此构造函数提供了精确的转换;它不会产生与使用Double.toString(double)方法将double转换为String然后使用BigDecimal(String)构造函数相同的结果。要获得该结果,请使用静态valueOf(double)方法。
使用Double.toString(double)方法提供的双重规范字符串表示法将double转换为BigDecimal。
注意:这通常是将double(或float)转换为BigDecimal的首选方法,因为返回的值等于使用Double的结果构造BigDecimal所产生的值。的toString(双)。
答案 1 :(得分:4)
使用字符串,而非数字文字。
new BigDecimal( "123.45" )
在构建double
时,您在代码中隐式引入了BigDecimal
类型基元。
new BigDecimal( 123.45 ) // <-- Passing `double` primitive
new BigDecimal( 0.09 )
虽然您认为自己正在传递123.45
,但实际上您传递的内容更像123.4500000000000028421709430404007434844970703125
。而不是0.09
你传递的更像0.0899999999999999966693309261245303787291049957275390625
。
你打败了使用BigDecimal和货币的目的,这是为了避免inherent inaccuracy of floating-point类型来确定它们的小数。
Java类型float
,Float
,double
,Double
全部使用floating point technology。浮点技术在计算中牺牲了执行速度的准确性。适合某些游戏,图形和科学/工程用途,但对金钱不利。
BigDecimal
不使用浮点技术。因此计算的执行要慢得多,但结果是准确的。
解决方案:构建BigDecimal
时避免使用浮点数。使用字符串。
new BigDecimal( "123.45" )
new BigDecimal( "0.09" )