使用BigDecimal作为货币的一个实际例子严格地比使用double更好 [英] A realistic example where using BigDecimal for currency is strictly better than using double

查看:161
本文介绍了使用BigDecimal作为货币的一个实际例子严格地比使用double更好的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们知道对于货币使用 double 是错误的,倾向于和不推荐。但是,我还没有看到逼真的示例,其中 BigDecimal 可以工作,而 double




请注意,一些小问题

  double total = 0.0; (int i = 0; i< 10; i ++)total + = 0.1; 
for(int i = 0; i <10; i ++)total - = 0.1;
assertTrue(total == 0.0);

并不算数,因为它们通过四舍五入来解决(在本例中为零到十六小数位就可以了)。



计算涉及大数值的加法可能需要一些中间路由,但是给定流通货币总额为 USD 1e12 ,Java double (即,标准的 IEEE双精度),其小数点后15位仍然是足够的事件美分。




计算涉及划分即使使用 BigDecimal 也不准确。我可以构造一个无法用 double s执行的计算,但可以用 BigDecimal 100,但它不是你可以遇到的现实。






我不认为这样一个这个例子不存在,只是我还没有看到。



我也非常同意, double 更容易出错。

示例



我正在寻找的方法如下(基于Roland Illig的答案)

b $ b *给定一个有三位小数的输入,
*使用HALF_EVEN将其四舍五入到两位小数。
* /
BigDecimal roundToTwoPlaces(BigDecimal n){
//确保输入有三位小数。
checkArgument(n.scale()== 3);
return n.round(new MathContext(2,RoundingMode.HALF_EVEN));

$ / code>

加上一个像

  public void testRoundToTwoPlaces(){
final BigDecimal n = new BigDecimal(0.615);
final BigDecimal expected = new BigDecimal(0.62);
final BigDecimal actual = roundToTwoPlaces(n);
Assert.assertEquals(expected,actual);



$ b

如果用 double ,那么测试可能会失败(它不适用于给定的输入,但它可以为其他输入)。但是,它可以正确地完成:

$ pre $ code $>静态双重roundToTwoPlaces(双n){
最后长m =数学。一轮(1000.0 * n);
final double x = 0.1 * m;
final long r =(long)Math.rint(x);
return r / 100.0;



$ b $ p
$ b

这很丑且容易出错(也可能简化),但它可以很容易地封装在某个地方。这就是为什么我正在寻找更多的答案。

解决方案

当您$ double price = 0.615 到小数点后两位,得到0.61(下舍入),但可能预期为0.62(因为5而凑整)。

这是因为double 0.615实际上是0.6149999999999999911182158029987476766109466552734375。


We know that using double for currency is error-prone and not recommended. However, I'm yet to see a realistic example, where BigDecimal works while double fails and can't be simply fixed by some rounding.


Note that trivial problems

double total = 0.0;
for (int i = 0; i < 10; i++) total += 0.1;
for (int i = 0; i < 10; i++) total -= 0.1;
assertTrue(total == 0.0);

don't count as they're trivially solved by rounding (in this example anything from zero to sixteen decimal places would do).


Computations involving summing big values may need some intermediate rouding, but given the total currency in circulation being USD 1e12, Java double (i.e., the standard IEEE double precision) with its 15 decimal digits is still sufficient event for cents.


Computations involving division are in general imprecise even with BigDecimal. I can construct a computation which can't be performed with doubles, but can be performed with BigDecimal using a scale of 100, but it's not something you can encounter in reality.


I don't claim that such a realistic example does not exist, it's just that I haven't seen it yet.

I also surely agree, that using double is more error-prone.

Example

What I'm looking for is a method like the following (based on the answer by Roland Illig)

/** 
  * Given an input which has three decimal places,
  * round it to two decimal places using HALF_EVEN.
*/
BigDecimal roundToTwoPlaces(BigDecimal n) {
    // To make sure, that the input has three decimal places.
    checkArgument(n.scale() == 3);
    return n.round(new MathContext(2, RoundingMode.HALF_EVEN));
}

together with a test like

public void testRoundToTwoPlaces() {
    final BigDecimal n = new BigDecimal("0.615");
    final BigDecimal expected = new BigDecimal("0.62");
    final BigDecimal actual = roundToTwoPlaces(n);
    Assert.assertEquals(expected, actual);
}

When this gets naively rewritten using double, then the test could fail (it doesn't for the given input, but it does for others). However, it can be done correctly:

static double roundToTwoPlaces(double n) {
    final long m = Math.round(1000.0 * n);
    final double x = 0.1 * m;
    final long r = (long) Math.rint(x);
    return r / 100.0;
}

It's ugly and error-prone (and can probably be simplified), but it can be easily encapsulated somewhere. That's why I'm looking for more answers.

解决方案

When you round double price = 0.615 to two decimal places, you get 0.61 (rounded down) but probably expected 0.62 (rounded up, because of the 5).

This is because double 0.615 is actually 0.6149999999999999911182158029987476766109466552734375.

这篇关于使用BigDecimal作为货币的一个实际例子严格地比使用double更好的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆