8

SO 社区是对的,在你问性能问题之前分析你的代码似乎比我随机猜测的方法更有意义:-) 我分析了我的代码(非常密集的数学)并且没有意识到我超过 70% 的代码显然是在某种程度上,我认为这不是导致减速的原因,四舍五入。

static double roundTwoDecimals(double d) {
    DecimalFormat twoDForm = new DecimalFormat("#.###");
    return Double.valueOf(twoDForm.format(d));
}

我的问题是我得到的十进制数通常是 .01、.02 等。但有时我会得到类似 0.070000000001 的东西(我真的只关心 0.07,但浮点精度会导致我的其他公式失败),我只是想要前 3 位小数来避免这个问题。

那么有没有更好/更快的方法来做到这一点?

4

2 回答 2

16

舍入(正)数字的标准方法是这样的:

double rounded = floor(1000 * doubleVal + 0.5) / 1000;

示例 1:floor(1000 * .1234 + 0.5) / 1000= floor(123.9)/1000=0.123
示例 2:floor(1000 * .5678 + 0.5) / 1000= floor(568.3)/1000=0.568

但正如@nuakh 评论的那样,你总是会在某种程度上受到四舍五入错误的困扰。如果您想要精确的 3 位小数,最好的办法是转换为千分之一(即,将所有内容乘以 1000)并使用整数数据类型intlong等)

在这种情况下,您将跳过最后除以 1000 并使用整数值123568进行计算。如果你想要百分比形式的结果,你可以除以 10 来显示:

123 → 12.3%
568 → 56.8%

于 2012-04-05T03:17:34.480 回答
4

Using a cast is faster than using floor or round. I suspect a cast is more heavily optimised by the HotSpot compiler.

public class Main {
    public static final int ITERS = 1000 * 1000;

    public static void main(String... args) {
        for (int i = 0; i < 3; i++) {
            perfRoundTo3();
            perfCastRoundTo3();
        }
    }

    private static double perfRoundTo3() {
        double sum = 0.0;
        long start = 0;
        for (int i = -20000; i < ITERS; i++) {
            if (i == 0) start = System.nanoTime();
            sum += roundTo3(i * 1e-4);
        }
        long time = System.nanoTime() - start;
        System.out.printf("Took %,d ns per round%n", time / ITERS);
        return sum;
    }

    private static double perfCastRoundTo3() {
        double sum = 0.0;
        long start = 0;
        for (int i = -20000; i < ITERS; i++) {
            if (i == 0) start = System.nanoTime();
            sum += castRoundTo3(i * 1e-4);
        }
        long time = System.nanoTime() - start;
        System.out.printf("Took %,d ns per cast round%n", time / ITERS);
        return sum;
    }

    public static double roundTo3(double d) {
        return Math.round(d * 1000 + 0.5) / 1000.0;
    }

    public static double castRoundTo3(double d) {
        return (long) (d * 1000 + 0.5) / 1000.0;
    }
}

prints

Took 22 ns per round
Took 9 ns per cast round
Took 23 ns per round
Took 6 ns per cast round
Took 20 ns per round
Took 6 ns per cast round

Note: as of Java 7 floor(x + 0.5) and round(x) don't do quite the same thing as per this issue. Why does Math.round(0.49999999999999994) return 1

This will round correctly to within the representation error. This means that while the result is not exact the decimal e.g. 0.001 is not represented exactly, when you use toString() it will correct for this. Its only when you convert to BigDecimal or perform an arithmetic operation that you will see this representation error.

于 2012-04-05T06:08:31.113 回答