I have two fields in my struts form as:
Long currentUsage; // 209715200l
BigDecimal totalQuota; // 343597383680
On jsp I'm using el to display the percentage as:
<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.
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, i.e. the coercion of variables to BigDecimal, is being done.
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
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. This will then be coerced to a BigDecimal (becuase totalQuota is a BigDecimal), and then perform 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). If either are scale 0, this rounding up will result in an integer result which is what you're seeing (then multiplied by 100).
The following plain Java shows what is going on step by step and the result
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));
: 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()));
, you would get the value you expect (i.e. 0.625 as result of the divisions and 62.5 displayed)
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.
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. I can't be sure it will work as I don't have the environment to test on, but it seems to.