JSF不正确的BigDecimal转换

时间:2013-02-04 03:14:03

标签: java jsf jsf-2 bigdecimal

问题:需要通过JSF格式化BigDecimal,但JSF正在破坏BigDecimal的精度。

JSF:

<h:outputText value="#{webUtilMB.roundUp(indexPrice.percentage, 2)}"/>

爪哇:

public class IndexPrice {
  public BigDecimal getPercentage(){ return new BigDecimal("1.325"); }
}

@ManagedBean("webUtilMb")
public class WebUtilManagedBean{
  public BigDecimal roundUp(BigDecimal dbvalue, int scale){
    return dbvalue.setScale(decimalPlaces, BigDecimal.ROUND_HALF_UP);
  }
}

在WebUtilManagedBean.roundUp方法中有一个断点显示我'dbvalue'是'1.3249999999999999555910790149937383830547332763671875'而不是'1.325'。

然后我重载了WebUtilManagedBean中的roundUp方法:

public Double roundUp(Double dvalue, int scale){
  System.out.println(dvalue);
}

在这个重载方法中有一个断点时让我感到惊讶的是:   - 'dvalue'是'1.325',这是正确的。   - 实际调用的方法不是roundUp(BigDecimal,int)方法。

我后来尝试使用BigDecimal构造函数,结果如下:

BigDecimal db1 = new BigDecimal("1.325"); -> 1.325
BigDecimal db2 = new BigDecimal(1.325d); -> 1.3249999999999999555910790149937383830547332763671875

理论:从上面看,似乎JSF将我的BigDecimal值转换为String而不是Double,而不是在值上调用'new BigDecimal(double)'来获取BigDecimal - 它返回错误的值


修复:解决此问题的一种方法是使用以下代码:

@ManagedBean("webUtilMb")
public class WebUtilManagedBean{
  public Double roundUp(Double dvalue, int scale){
    return this.roundUp(**new BigDecimal(dvalue.toString())**, BigDecimal.ROUND_HALF_UP);
  }
  public BigDecimal roundUp(BigDecimal dbvalue, int scale){
    return dbvalue.setScale(decimalPlaces, BigDecimal.ROUND_HALF_UP);
  }
}

但这对我来说似乎是个黑客。

有关解决此问题及其背后原因的任何想法。感谢。

1 个答案:

答案 0 :(得分:2)

理想情况下,这可以通过f:convertNumber完成,如果它有'rounding'属性,但它没有。

执行此操作的干净方法是编写自己的roundup f:converter类,并在XHTML中使用适当的f:converter标记,而不是使用EL方法调用。您的转换器将获取作为Object的值,即BigDecimal,并负责将其转换为String本身。请参阅f:converter的文档。令人惊讶的是JSF没有正确地做到这一点,但我想一旦你进入EL方法调用,一切都是一个字符串。这样的事情(警告:未经测试):

import java.math.BigDecimal;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.FacesConverter;

/**
 * Use via e.g.:
 * <pre>
 * &lt;h:outputText value="#{EL}"&gt;
 *  &lt;f:converter id="com.edsi.jsf.RoundHalfUp"/&gt;
 *  &lt;f:attribute name="decimalPlaces" value="2"/&gt;
 * &lt;/h:outputText&gt;
 * <pre>
 * @author Esmond Pitt
 */
@FacesConverter(value="com.edsi.jsf.RoundHalfUp")
public class RoundHalfUpConverter implements Converter
{

    @Override
    public Object getAsObject(FacesContext context, UIComponent component, String value)
    {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public String getAsString(FacesContext context, UIComponent component, Object value)
    {
        BigDecimal  bd = (BigDecimal)value;
        int decimalPlaces;
        try
        {
            decimalPlaces = Integer.parseInt((String)component.getAttributes().get("decimalPlaces"));
        }
        catch (Exception exc)
        {
            decimalPlaces = 2;  // or whatever
        }
        return bd.setScale(decimalPlaces, BigDecimal.ROUND_HALF_UP).toString();
    }

}