简体   繁体   English

BigDecimal除法问题

[英]BigDecimal division issue

I have two fields in my struts form as: 我在struts表单中有两个字段:

Long currentUsage; // 209715200l
BigDecimal totalQuota; // 343597383680

On jsp I'm using to display the percentage as: jsp上,我使用将百分比显示为:

<c:set var="totalQuota"  
value="${(strutsForm.totalQuota div 1024 div 1024 div 1024)}"></c:set>
${ ( (strutsForm.currentUsage div 1024 div 1024) div (totalQuota) ) * 100 }

But the output being displayed is 100 . 但是显示的输出是100
Why this is happening? 为什么会这样呢? And how could I get the proper result? 我怎么能得到正确的结果呢?

Why is this happening? 为什么会这样呢?

This is counter intuitive behaviour, but I think it is the way that the EL spec ( LInk to oracle EL spec ) for division, ie the coercion of variables to BigDecimal, is being done. 这是违反直觉的行为,但我认为这是EL规格(从LInk到oracle EL规格 )的除法,即将变量强制转换为BigDecimal的方式。

The first line of your code: 您的代码的第一行:

${(strutsForm.totalQuota div 1024 div 1024 div 1024)}

will coerce totalQuota the 1024 values to BigDecimal and then perform the operations, returning a BigDecimal 将强制将totalQuota的1024个值强制转换为BigDecimal,然后执行操作,返回一个BigDecimal

The second line 第二行

${ ( (strutsForm.currentUsage div 1024 div 1024) div (totalQuota) ) * 100 }

will first coerce currentUsage to a Double and do the division by 1024*1024, returning a Double. 首先将currentUsage强制为Double,然后除以1024 * 1024,返回Double。 This will then be coerced to a BigDecimal (becuase totalQuota is a BigDecimal), and then perform A.divide(B, BigDecimal.ROUND_HALF_UP) . 然后将其强制为BigDecimal(因为totalQuota为BigDecimal),然后执行A.divide(B, BigDecimal.ROUND_HALF_UP) The presicion of the output of this depends on the scale of the BigDecimal variables (affects which decimal place the rounding up occurs). 其输出的精度取决于BigDecimal变量的标度(影响向上舍入的小数位)。 If either are scale 0, this rounding up will result in an integer result which is what you're seeing (then multiplied by 100). 如果其中一个是小数位数0,则此舍入将得出您所看到的整数结果(然后乘以100)。

The following plain Java shows what is going on step by step and the result 以下简单的Java逐步显示了正在进行的操作以及结果

public class jsp_problem_steps
{
  public static void main(String[] args)
  {
    Long currentUsage=209715200l; // 209715200l
    BigDecimal totalQuota = new BigDecimal("343597383680"); // 343597383680

    BigDecimal coerced = new BigDecimal("1024");

    BigDecimal t = totalQuota.divide(coerced, BigDecimal.ROUND_HALF_UP).divide(coerced, BigDecimal.ROUND_HALF_UP).divide(coerced, BigDecimal.ROUND_HALF_UP);
    System.out.println("coerced BigDecimal quota : " + t);

    Double d = currentUsage.doubleValue() / 1024d / 1024d;

    System.out.println("coerced Double usage : " + d);

    BigDecimal coerced_d = (new BigDecimal(d));

    System.out.println(coerced_d + " at scale " + coerced_d.scale());

    BigDecimal res = coerced_d.divide(t,BigDecimal.ROUND_HALF_UP);

    System.out.println("Result of division : " + res + " THIS IS UNEXPECTED!!!");

    res = res.multiply(new BigDecimal("100"));

    System.out.println("Final value : " + res);
  }
}

Gives output 提供输出

coerced BigDecimal quota : 320
coerced Double usage : 200.0
200 at scale 0
Result of division : 1 THIS IS UNEXPECTED!!!
Final value : 100

The problem step is BigDecimal coerced_d = (new BigDecimal(d)); 问题步骤是BigDecimal coerced_d = (new BigDecimal(d)); : that coercion of Double to BigDecimal is being done using a BigDecimal constructor that gives a BigDecimal with scale 0. If construction was done using, say BigDecimal coerced_d = (new BigDecimal(d.toString())); :使用BigDecimal构造函数完成Double到BigDecimal的强制转换,该构造函数给出一个小数BigDecimal coerced_d = (new BigDecimal(d.toString())); 0的BigDecimal。如果使用构建完成,则说BigDecimal coerced_d = (new BigDecimal(d.toString())); , you would get the value you expect (ie 0.625 as result of the divisions and 62.5 displayed) ,您将获得期望的值(例如,除法结果为0.625,显示为62.5)

It is arguable whether this is expected behaviour or a bug - it is not clear from the spec how coercion from a Double to a BigDecimal should be done. 这是预期的行为还是错误是有争议的-从规范中尚不清楚应如何从Double强制转换为BigDecimal。

How could I get the proper result? 我怎样才能得到正确的结果?

Change the last line to: 将最后一行更改为:

${ ( (strutsForm.currentUsage div 1024 div 1024) div (totalQuota.doubleValue()) ) * 100 }

that should avoid the problematic coercion of a Double to a BigDecimal. 应该避免从Double到BigDecimal的强制性问题。 I can't be sure it will work as I don't have the environment to test on, but it seems to. 我无法确定它是否可以工作,因为我没有可以测试的环境,但是似乎可以。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM