I'm using Java with Jasper Reports and would like to format a decimal value using this format mask "#,##0.00"
. At the first sight all looks fine, but I found that my decimal values are rounded using RoundingMode.HALF_EVEN
rounding mode and this is not correct in my case.
Is this possible to specify an other rounding mode (I need HALF_DOWN
mode)?
You can use the scriptlets mechanism.
The sample
package utils;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
public class RoundingHelper {
public static String round(BigDecimal value, RoundingMode mode, String pattern) {
DecimalFormat format = new DecimalFormat(pattern);
format.setRoundingMode(mode);
return format.format(value);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd" name="format_decimal" pageWidth="595" pageHeight="842" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20">
<import value="utils.RoundingHelper"/>
<queryString>
<![CDATA[SELECT id, (cost/10) as cost from product]]>
</queryString>
<field name="ID" class="java.lang.Integer"/>
<field name="COST" class="java.math.BigDecimal"/>
<columnHeader>
<band height="50">
<staticText>
<reportElement x="0" y="0" width="154" height="50"/>
<box leftPadding="10">
<topPen lineWidth="1.0"/>
<leftPen lineWidth="1.0"/>
<bottomPen lineWidth="1.0"/>
<rightPen lineWidth="1.0"/>
</box>
<textElement textAlignment="Center" verticalAlignment="Middle">
<font isBold="true"/>
</textElement>
<text><![CDATA[Original value]]></text>
</staticText>
<staticText>
<reportElement x="154" y="0" width="191" height="50"/>
<box leftPadding="10">
<topPen lineWidth="1.0"/>
<leftPen lineWidth="1.0"/>
<bottomPen lineWidth="1.0"/>
<rightPen lineWidth="1.0"/>
</box>
<textElement textAlignment="Center" verticalAlignment="Middle">
<font isBold="true"/>
</textElement>
<text><![CDATA[Using RoundingMode.HALF_DOWN]]></text>
</staticText>
<staticText>
<reportElement x="345" y="0" width="191" height="50"/>
<box leftPadding="10">
<topPen lineWidth="1.0"/>
<leftPen lineWidth="1.0"/>
<bottomPen lineWidth="1.0"/>
<rightPen lineWidth="1.0"/>
</box>
<textElement textAlignment="Center" verticalAlignment="Middle">
<font isBold="true"/>
</textElement>
<text><![CDATA[Using RoundingMode.HALF_DOWN]]></text>
</staticText>
</band>
</columnHeader>
<detail>
<band height="20" splitType="Stretch">
<textField pattern="#,##0.000000000000">
<reportElement x="0" y="0" width="154" height="20"/>
<box leftPadding="10">
<topPen lineWidth="1.0"/>
<leftPen lineWidth="1.0"/>
<bottomPen lineWidth="1.0"/>
<rightPen lineWidth="1.0"/>
</box>
<textElement/>
<textFieldExpression class="java.math.BigDecimal"><![CDATA[$F{COST}]]></textFieldExpression>
</textField>
<textField>
<reportElement x="154" y="0" width="191" height="20"/>
<box leftPadding="10">
<topPen lineWidth="1.0"/>
<leftPen lineWidth="1.0"/>
<bottomPen lineWidth="1.0"/>
<rightPen lineWidth="1.0"/>
</box>
<textElement/>
<textFieldExpression class="java.lang.String"><![CDATA[RoundingHelper.round($F{COST}, RoundingMode.HALF_DOWN, "#,##0.")]]></textFieldExpression>
</textField>
<textField>
<reportElement x="345" y="0" width="191" height="20"/>
<box leftPadding="10">
<topPen lineWidth="1.0"/>
<leftPen lineWidth="1.0"/>
<bottomPen lineWidth="1.0"/>
<rightPen lineWidth="1.0"/>
</box>
<textElement/>
<textFieldExpression class="java.lang.String"><![CDATA[RoundingHelper.round($F{COST}, RoundingMode.HALF_UP, "#,##0.")]]></textFieldExpression>
</textField>
</band>
</detail>
</jasperReport>
- The result will be (preview in iReport)
Another solution
Another way is to use BigDecimal.setScale(int, java.math.RoundingMode) method (for double field):
<textFieldExpression class="java.lang.String"><![CDATA[new BigDecimal($F{COST}).setScale(0, BigDecimal.ROUND_HALF_DOWN).toString()]]></textFieldExpression>
or just (for BigDecimal field):
<textFieldExpression class="java.lang.String"><![CDATA[$F{COST}.setScale(0, BigDecimal.ROUND_HALF_DOWN).toString()]]></textFieldExpression>
More info about the scriptlets in JR: Scriptlets
Here is another solution.
You can change the default rounding mode of the number formatter by extending the default JR format factory (net.sf.jasperreports.engine.util.DefaultFormatFactory) and sending an instance of the extended factory via the JRParameter.REPORT_FORMAT_FACTORY parameter.
The rounding mode of a java.text.DecimalFormat instance can be changed via the setRoundingMode() method.
https://community.jaspersoft.com/questions/528989/decimal-formatting-rounding-mode-problem
I've created inner class:
getParameters().put(REPORT_FORMAT_FACTORY, new DefaultFormatFactory() {
@Override
public NumberFormat createNumberFormat(String pattern, Locale locale) {
NumberFormat format = null;
if (pattern != null && pattern.trim().length() > 0) {
if (STANDARD_NUMBER_FORMAT_DURATION.equals(pattern)) {
format = new DurationNumberFormat();
} else {
format = locale == null ?
NumberFormat.getNumberInstance() :
NumberFormat.getNumberInstance(locale);
if (format instanceof DecimalFormat) {
format.setRoundingMode(HALF_UP);
((DecimalFormat) format).applyPattern(pattern);
}
}
}
return format;
}
});`