Java数字,"人类"四舍五入

时间:2014-04-26 16:09:38

标签: java rounding rounding-error

我打赌在这里有一个类似的问题,我找不到它。 问题是关于修复java数学不准确性,例如我得到235.000000029875.999999997这样的数字。当然,对于一个人而言,这两个人实际上意味着2359876,当给出一定的准确度阈值时,让我们称之为" 5个零或九个"

但不仅是小数点右边的数字,同样的规则也适用于23500000001.05699999999.0等数字

任何想法/图书馆?

更新 看起来我错了,说这是一个已知的主题,只需要几分钟,直到有人弹出一个已知的库。所以这是我期望的逻辑: 对于给定的数字,当在其字符串表示中遇到N个连续的零或九时,则这些零和九与最右边的数字的其余部分一起舍入" 0"或" 9"。 例如,当N = 5时,数字239999995.546变为240000000.0,数字34.0000007变为34340000.007变为340000

更新2: 这就是在匆忙或对问题不够重视时会发生什么。抱歉。我正在谈论"人类"四舍五入。一个很好的例子是在linux框中比较dfdf -h的输出。至于"不准确"我在谈论,请运行以下代码:

double d = 1000.0;
double mult = 0.12351665;
System.out.println(d * mult / mult);

回复肯定不是您在购物车中显示的回复。在我的情况下,情况更糟,它不仅是我处理的钱,它可以是任何东西 - 百分比,大数,小分数,以及所有这些都是相对繁重的数学计算的结果。所以我在谈论对人类有意义的舍入。

我已经完成了代码,但仍有可能有人做得更好。

3 个答案:

答案 0 :(得分:2)

您可能正在尝试询问如何存储和处理货币。简短的回答是:使用BigDecimal类型或使用单独的整数字段为欧元和美分创建您自己的货币实现(如果您愿意,则为美元:)。

已经解释过: Using BigDecimal to work with currencies

Why not use Double or Float to represent currency?

答案 1 :(得分:2)

我在评论中已经提到了科学记数法。以下是我提出的建议:

public static double simplify(Number n) {
    String numberString = String.format("%e", n);
    int indexE = numberString.indexOf('e');
    String baseValue = numberString.substring(0, indexE);
    String exponent = numberString.substring(indexE + 1);
    double base = Double.parseDouble(baseValue);
    int    exp = Integer.parseInt(exponent);

    return base * Math.pow(10, exp);
}

我使用了我在你的问题中找到的所有数字,并添加了一个负值来测试它。

public static void main(String[] args) {
    Number[] ns = new Number[]{
            239999995.546,
            239989995.546,
               340000.007,
                   34.0000007,
           5699999999.0,
                  235.00000002,
                 9875.999999997,
                -4334.345345,
          23500000001.0,
                    0.30000007,
                   -0.053999949
    };
    DecimalFormat df = new DecimalFormat("0.#####");
    for(Number n : ns) {
        String s = df.format(simplify(n));
        System.out.println("    " + n + " is " + s);
    }
}

结果是:

2.39999995546E8 is 240000000
2.39989995546E8 is 239990000
340000.007 is 340000
34.0000007 is 34
5.699999999E9 is 5700000000
235.00000002 is 235
9875.999999997 is 9876
-4334.345345 is -4334.345
2.3500000001E10 is 23500000000
0.30000007 is 0.3
-0.053999949 is -0.054

编辑我调整了代码以使用double,用exponents<修正了错误0并添加了另一个例子。另外我插入了DecimalFormat。请注意,添加更多#可能会更改某些结果,即-0.053999949现在会显示为-0.054,更多数字会导致-0.05399995

答案 2 :(得分:1)

既然你提出了想法,那么这个怎么样:使N不是一个连续的零或九的数量,而是一个数字四舍五入仍然接近原始数字的阈值。然后从最不重要的小数位开始,一次将第一个位置四舍五入,然后将你得到的数字除以之前的数字。如果比率超过您的阈值则停止。试验圆角看起来对你好的比例和边界条件。使用BigDecimal可以避免浮点运算的问题。