Java最后用一堆9进行四舍五入

时间:2012-02-13 10:41:38

标签: java rounding

看起来BigDecimal.setScale截断到比例+ 1小数位,然后仅基于该小数进行舍入。 这是正常的还是有一种干净的方法将舍入模式应用于每一个小数?

输出:0.0697 (这不是他们在学校教我的舍入模式)

double d = 0.06974999999;
BigDecimal bd = BigDecimal.valueOf(d);
bd = bd.setScale(4, RoundingMode.HALF_UP);
System.out.println(bd);


输出:0.0698 (这是他们在学校教我的舍入模式)

double d = 0.0697444445;
BigDecimal bd = BigDecimal.valueOf(d);
int scale = bd.scale();
while (4 < scale) {
    bd = bd.setScale(--scale, RoundingMode.HALF_UP);
}
System.out.println(bd);

EDITED 的 在读完一些答案后,我意识到我搞砸了一切。当我写下我的问题时,我有点沮丧。 所以,我要重写问题原因,即使答案对我有很大帮助,我仍然需要一些建议。

问题是: 我需要将0.06974999999舍入到0.0698,这是因为我知道那些小数实际上是0.6975(在我不能控制的地方出现舍入错误)。
所以我一直在玩一种“双舍入”,它分两步执行舍入:第一轮到更高的精度,然后舍入到所需的精度。
(这里我搞砸了,因为我认为每个小数位的循环会更安全)。 问题是我不知道在第一步中要舍入哪个更高的精度(我使用的是小数位数-1)。另外我不知道是否能为其他案件找到一些意想不到的结果 这是我放弃支持循环的第一种方式,在阅读你的答案后现在看起来好多了:

public static BigDecimal getBigDecimal(double value, int decimals) {
    BigDecimal bd = BigDecimal.valueOf(value);
    int scale = bd.scale();
    if (scale - decimals > 1) {
        bd = bd.setScale(scale - 1, RoundingMode.HALF_UP);
    }
    return bd.setScale(decimals, roundingMode.HALF_UP);
}

这些打印出以下结果:
0.0697444445 = 0.0697
0.0697499994 = 0.0697
0.0697499995 = 0.0698
0.0697499999 = 0.0698
0.0697444445 = 0.069744445 //四舍五入到9位小数
0.0697499999 = 0.069750000 //舍入到9位小数
0.069749 = 0.0698

现在的问题是,是否有更好的方法(可能是不同的舍入模式)?如果这可以安全地用作一般的舍入方法?
我需要对很多值进行舍入,并且必须在运行时选择此标准和标准方法,具体取决于我收到的数字类型似乎非常复杂。

再次感谢您的时间。

4 个答案:

答案 0 :(得分:0)

如果您需要偏向舍入,则可以添加一个小因子。

e.g。最多可以小数点后6位。

double d = 
double rounded = (long) (d * 1000000 + 0.5) / 1e6;

添加一个小因子,你需要决定你想要多少额外的东西。 e.g。

double d = 
double rounded = (long) (d * 1000000 + 0.50000001) / 1e6;

e.g。

public static void main(String... args) throws IOException {
    double d1 = 0.0697499994;
    double r1 = roundTo4places(d1);
    double d2 = 0.0697499995;
    double r2= roundTo4places(d2);
    System.out.println(d1 + " => " + r1);
    System.out.println(d2 + " => " + r2);

}

public static double roundTo4places(double d) {
    return (long) (d * 10000 + 0.500005) / 1e4;
}

打印

0.0697499994 => 0.0697
0.0697499995 => 0.0698

第一个是正确的。

0.44444444 ... 44445舍入为整数是0.0

只有0.500000000 ... 000或更多被舍入到1.0

没有舍入模式,将向下舍入0.4和向上0.45。

如果您考虑一下,您希望随机数被向上或向下舍入的机会相等。如果将足够多的随机数相加,则舍入所产生的错误将取消。


上半轮与

相同
long n = (long) (d + 0.5);

您建议的舍入是

long n = (long) (d + 5.0/9);

Random r = new Random(0);
int count = 10000000;

// round using half up.
long total = 0, total2 = 0;
for (int i = 0; i < count; i++) {
    double d = r.nextDouble();
    int rounded = (int) (d + 0.5);
    total += rounded;

    BigDecimal bd = BigDecimal.valueOf(d);
    int scale = bd.scale();
    while (0 < scale) {
        bd = bd.setScale(--scale, RoundingMode.HALF_UP);
    }

    int rounded2 = bd.intValue();
    total2 += rounded2;
}
System.out.printf("The expected total of %,d rounded random values is %,d,%n\tthe actual total was %,d, using the biased rounding %,d%n",
        count, count / 2, total, total2);

打印

The expected total of 10,000,000 rounded random values is 5,000,000, 
    the actual total was 4,999,646, using the biased rounding 5,555,106

http://en.wikipedia.org/wiki/Rounding#Round_half_up

答案 1 :(得分:0)

当你四舍五入时,你会看到你要四舍五入的最后一位数之后的值,在你的第一个例子中,你将0.06974999999四舍五入到4位小数。所以你有0.0697然后是4999999(或者基本上是697.4999999)。由于舍入模式为HALF_UP,因此0.499999小于0.5,因此向下舍入。

答案 2 :(得分:0)

如果0.06974999999和0.06975之间的差异非常重要,那么您应该更快地切换到BigDecimals。至少,如果性能如此重要,请找出使用long和int的方法。双人和花车不适合能分辨出1.0和0.999999999999999之间差异的人。当您使用它们时,信息会丢失,并且无法恢复它。

(这个信息看起来微不足道,说得客气一点,但是如果旅行1,000,000码让你在悬崖顶端,那么行驶1,000,001码会让你在过去悬崖的顶部那最后一个院子很重要。如果你以10亿美元的价格减掉1便士,当会计师跟你走后,你会遇到更大麻烦。)

答案 3 :(得分:0)

尝试使用上一个和下一个值来查看它们是否会缩小比例呢?

public static BigDecimal getBigDecimal(double value) {
    BigDecimal bd = BigDecimal.valueOf(value);
    BigDecimal next = BigDecimal.valueOf(Math.nextAfter(value, Double.POSITIVE_INFINITY));
    if (next.scale() < bd.scale()) {
        return next;
    }
    next = BigDecimal.valueOf(Math.nextAfter(value, Double.NEGATIVE_INFINITY));
    if (next.scale() < bd.scale()) {
        return next;
    }
    return bd;
}

然后可以将得到的BigDecimal四舍五入到所需的比例。
(我无法分辨这对大量值的性能影响)