繁体   English   中英

由于 BigDecimals 中的二进制表示,数字不准确。 我该如何解决?

[英]Inaccurate Digits due to binary representation in BigDecimals. How do I get around it?

我想编写一个将 String 转换为 BigDecimal 的解析器。 要求它是 100% 准确的。 (好吧,我目前正在为乐趣而编程。所以我宁愿要求它......;-P)

所以我想出了这个程序:

public static BigDecimal parse(String term) {
    char[] termArray = term.toCharArray();

    BigDecimal val = new BigDecimal(0D);
    int decimal = 0;
    for(char c:termArray) {
        if(Character.isDigit(c)) {
            if(decimal == 0) {
                val = val.multiply(new BigDecimal(10D));
                val = val.add(new BigDecimal(Character.getNumericValue(c)));
            } else {
                val = val.add(new BigDecimal(Character.getNumericValue(c) * Math.pow(10, -1D * decimal)));
                decimal++;
            }
        }
        if(c == '.') {
            if(decimal != 0) {
                throw new IllegalArgumentException("There mustn't be multiple points in this number: " + term);
            } else {
                decimal++;
            }
        }
    }

    return val;
}

所以我尝试了:

parse("12.45").toString();

我预计它是12.45 相反,它是12.45000000000000002498001805406602215953171253204345703125 我知道这可能是由于二进制表示的限制。 但是我怎样才能解决这个问题呢?

注意:我知道你可以使用new BigDecimal("12.45"); . 但这不是我的意思——我想自己写,不管这可能有多愚蠢。

是的,这是由于二进制表示的局限性。 10 的任何负幂都不能精确表示为double

要解决此问题,请将所有double算术替换为所有BigDecimal算术。

val = val.add(
    new BigDecimal(Character.getNumericValue(c)).divide(BigDecimal.TEN.pow(decimal)));

有了这个我得到12.45

这可以稍微改进一下。 只分一次。 只需忽略循环内的小数点。 只计算小数点。 所以"12.45"变成1245 decimal == 2 现在最后,您只需将其除以BigDecimal.TEN.pow(2) (或 100)即可得到12.45

public static BigDecimal parse(String term) 
{
    char[] termArray = term.toCharArray();

    // numDecimals:  -1: no decimal point at all, so no need to divide
    //                0: decimal point found, but no digits counted yet
    //              > 0: count of digits after decimal point    

    int numDecimals = -1;
    BigDecimal val = new BigDecimal.ZERO;

    for(char c: termArray) 
    {
        if (Character.isDigit(c)) 
        {
            val = val.multiply(BigDecimal.TEN).add(BigDecimal.valueOf(Character.getNumericValue(c)));
            if (numDecimals != -1)
                numDecimals++;
        }
        else if (c == '.') 
        {
            if (numDecimals != -1) 
                throw new IllegalArgumentException("There mustn't be multiple points in this number: " + term);
            else 
                numDecimals = 0;
        }

    }

    if (numDecimals > 0)
        return val.divide(BigDecimal.TEN.pow(numDecimals));
    else
        return val;
}

请注意,此功能不适用于负值,也不识别科学记数法。 为此,使用原始字符串、索引和charAt(index)可能比当前循环更理想。 但这不是问题。

暂无
暂无

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

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