如何处理Java中BigDecimal的舍入误差

3
我正在使用实现Java应用程序内脚本引擎的开源项目(axil)。在尝试利用BigDecimal舍入时遇到了一个主要障碍。似乎BigDecimal将我的输入转换为科学计数法,然后将传递进来的精度应用于数字的SN表示中的系数,而不是其非SN表示。例如:
new BigDecimal("-232454.5324").round(new MathContext(2, RoundingMode.HALF_UP)).toString()
产生了一个结果-2.3E+5。这给我带来了两个问题。首先,我期望得到一个结果为-232454.5(-2.324545E+5),因此得到-230000会扰乱任何涉及结果的数学运算。其次,我既没有预料到也找不到解决方法,无法避免使用科学计数法得出的结果(虽然我预计还有一种格式化方法我还没有发现)。

由于项目的性质,我们对传递给round()方法的数字的大小/类型几乎没有任何期望值,因此任何解决方案都需要高度模块化。有人有什么建议吗?如果这里有用的话,这是该项目中此错误的Google代码问题报告链接。这是项目主页的链接。

非常感谢任何帮助。

3个回答

9

不要使用round方法,而是使用setScale,其中参数为小数位数:

BigDecimal bd = new BigDecimal("-232454.5324").setScale(1,
                RoundingMode.HALF_UP);
String string = bd.toPlainString();
System.out.println(string); // prints -232454.5

注意,setScale方法返回一个新的BigDecimal实例,它不会改变当前实例的标度。


谢谢回复,但这并不能解决其他舍入模式的问题。例如:new BigDecimal("23.2222").setScale(0, RoundingMode.HALF_EVEN).toPlainString()当使用半偶舍入规则时,应该得到24(最接近的偶数整数),但实际上结果是23。我该如何将舍入规则应用于缩放操作? - JoeliusCaeser
@JoeliusCaeser,System.out.println(new BigDecimal("23.5").setScale(0,RoundingMode.HALF_EVEN).toPlainString()); 输出结果为24。 但是当没有指定小数位的时候,23.222不论采用何种舍入模式,总是会舍弃小数并向下取整到23吧? - Kennet
2
在这种情况下,将23四舍五入是正确的。HALF_EVEN指定当小数部分恰好为0.5时,只四舍五入到偶数整数,并向最近的整数四舍五入。 - Louis Wasserman
哦,好的没错。那是我的错,非常感谢你们的帮助! - JoeliusCaeser
这个关于JDK的旧[无问题]页面(http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4508009)在我搜索四舍五入错误时给了我有用的信息,所以我想在这里链接它。 - watery

1
一个MathContext需要一个精度,这是结果中小数点前后的有效数字数量。在这里,BigDecimal的逻辑是正确的。

1
我会这样做:
BigDecimal number = new BigDecimal("22.2222").setScale(0, RoundingMode.UP);

if (number.intValue() % 2 != 0) {
    number = number.add(BigDecimal.ONE);
}

System.out.println(number); // => 24

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