【问题标题】:Java numbers, "human" rounding [closed]Java数字,“人”四舍五入[关闭]
【发布时间】:2014-04-26 16:09:37
【问题描述】:

我敢打赌这里有一个类似的问题,我只是找不到它。 问题是关于修复 java 数学不准确的问题,例如当我得到像 235.000000029875.999999997 这样的数字时。天啊,对于人类来说,这两个实际上是指2359876,当给定一定的准确度阈值时,我们称之为“5 个零或九”。

但这不仅仅是关于小数点右边的数字,同样的规则也应该适用于像23500000001.05699999999.0这样的数字

有什么想法/库吗?

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

更新 2: 这就是在匆忙或对问题没有足够关注时发生的情况。对不起。我说的是“人类”四舍五入。一个很好的例子是在 linux 机器上比较 dfdf -h 的输出。至于我说的“不准确”,请运行以下代码:

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

响应绝对不是您在购物车中显示的响应。就我而言,情况更糟,不仅仅是我处理的钱,它可以是任何东西——百分比、大数字、小分数,所有这些都是相对繁重的数学计算的结果。所以我说的是对人类有意义的四舍五入。

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

【问题讨论】:

  • 这里似乎没有真正的问题。
  • @OliCharlesworth,是什么阻止了它成为问题?
  • 您需要更准确地定义您的问题。尝试定义一般的舍入规则(根据您的任何要求)。然后,如果您在实施它时遇到任何问题,请在此处发布并寻求帮助。你现在的问题非常模糊。
  • 好的,明白了,我只是认为这是一个明显且众所周知的问题,有一个已知的解决方案。我会重写问题。
  • 我刚刚意识到您的舍入规则不一致:为什么240000000.0 保留.034000034 没有?此外,Formatter %e 提供的科学记数法可能会对您有所帮助,它为 239999995.546 提供 2.400000e+08

标签: java rounding rounding-error


【解决方案1】:

我已经在 cmets 中建议过使用科学记数法。这是我想出的:

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

编辑我调整了代码以使用双精度,修复了指数 DecimalFormat。请注意,添加更多# 可能会改变一些结果,即-0.053999949 现在将显示为-0.054,更多的数字将导致-0.05399995

【讨论】:

  • 看起来很有趣。但是-4334.345345怎么了,为什么会变呢?
  • 科学计数法是-4.334345e+03,代码抓取将其分为-4.3343453并计算4.334345 * 10^3 = 4334.345,然后将其转换为long,因此会抛出十进制数字离开。将返回类型更改为double,您将获得更好的结果,尽管在此期间您必须与大量的 0 作斗争。编辑:为此已经有了答案:stackoverflow.com/questions/11284938/…
  • 好的,我明白了。刚刚测试过,例如它不适用于0.30000007,但我想我明白你的想法,可以自己修复它。感谢您的耐心等待))
  • 我检查了,尝试使用+1 而不是+2 作为指数,我没有考虑负指数。我会更新我的答案,以反映双打。
【解决方案2】:

您可能想问如何存储和处理货币。简短的回答是:使用 BigDecimal 类型或使用单独的整数字段创建您自己的货币实现,用于欧元和美分(或美元,如果您喜欢 :))。

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

Why not use Double or Float to represent currency?

【讨论】:

  • 这实际上不是关于货币,而是关于数字的类人四舍五入。
  • 好吧,我一开始没看懂这个问题。更新后我会这样做。
【解决方案3】:

既然你问的是想法,那么这个怎么样:使 N 不是连续的 0 或 9 的数量,而是一个阈值,在该阈值处舍入一个数字仍然接近原始数字。然后从最不重要的小数位开始,一次将第一位四舍五入,然后将得到的数字除以之前的数字。如果比率超过您的阈值,则停止。尝试四舍五入看起来不错的比率和边框条件。使用 BigDecimal 避免浮点运算问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-11-12
    • 1970-01-01
    • 1970-01-01
    • 2012-04-23
    • 1970-01-01
    • 1970-01-01
    • 2011-05-31
    相关资源
    最近更新 更多