我有以下程序(取自SO链接在java中将美元(大十进制)转换为美分(整数)的最佳方法是什么?但是,输出并不符合我的预期。
电流输出:
12345
8
预期输出:
12345
9
主.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);
}
}
}
tl;博士
使用字符串,而不是数字文本。
new BigDecimal( "123.45" )
避免浮点
您在构造BigDecimal
时隐式地将double
类型基元引入到代码中。
new BigDecimal( 123.45 ) // <-- Passing `double` primitive
new BigDecimal( 0.09 )
当你以为自己在通过123.45
时,你实际上是在传递更像123.4500000000000028421709430404007434844970703125
的东西。而不是0.09
你传递的东西更像0.0899999999999999966693309261245303787291049957275390625
.
您违背了将 BigDecimal 与货币一起使用的真正目的,即避免浮点类型在确定其小数部分时固有的不准确性。
Java类型float
、Float
、double
Double
都使用浮点技术。浮点技术在计算中牺牲了准确性和执行速度。适用于某些游戏、图形和科学/工程目的,但对金钱不利。
BigDecimal
不使用浮点技术。所以计算的执行要慢得多,但结果是准确的。
解决方案:构造BigDecimal
时避免浮点。使用字符串。
new BigDecimal( "123.45" )
new BigDecimal( "0.09" )
您的问题是双精度不能代表所有值。 例如,9/100
只能是近似的。试试System.out.println(new BigDecimal(0.09))
.你应该得到0.0899999999999999966693309261245303787291049957275390625
.因此,您将其四舍五入为 0.08,然后乘以 100。
试试new BigDecimal(String.valueOf(0.09))
编辑:更好的解决方案是使用BigDecimal.valueOf(0.09)
,这是等效的。
来自 javadoc for BigDecimal(double):
将双精度转换为 BigDecimal ,后者是双精度的二进制浮点值的精确十进制表示形式。
...
当必须将双精度值用作 BigDecimal 的源时,请注意,此构造函数提供了精确的转换;它给出的结果与使用 Double.toString(double) 方法将双精度转换为字符串,然后使用 BigDecimal(String) 构造函数的结果不同。若要获得该结果,请使用静态值Of(double)方法。
对于 BigDecimal.valueOf(double):
使用 Double.toString(double) 方法提供的双精度的规范字符串表示形式将双精度转换为 BigDecimal。
注意:这通常是将双精度(或浮点数)转换为 BigDecimal 的首选方法,因为返回的值等于使用 Double.toString(double) 的结果构造 BigDecimal 产生的值。