8

SOコミュニティは正しかったので、パフォーマンスの質問をする前にコードをプロファイリングする方が、ランダムに推測するという私のアプローチよりも理にかなっているようです:-)コードをプロファイリングし(非常に集中的な数学)、コードの70%以上が明らかに部分的には、小数の四捨五入というスローダウンの原因だとは思いませんでした。

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

私の問題は、通常は.01、.02などの10進数を取得することですが、.070000000001のようなものを取得することもあります(実際には0.07のみを気にしますが、浮動小数点の精度により、他の数式が失敗します)、Iこの問題を回避するには、最初の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分の1に変換して(つまり、すべてに1000を掛けて) 、整数データ型( int、、など)を使用するのが最善の策です。long

その場合、1000による最後の除算をスキップし、積分値123568計算に使用します。結果をパーセンテージの形式で表示する場合は、10で割って表示します。

123→12.3%
568→56.8%

于 2012-04-05T03:17:34.480 に答える
4

キャストを使用すると、床や円形を使用するよりも高速です。キャストはHotSpotコンパイラによってより高度に最適化されていると思います。

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;
    }
}

プリント

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

注:Java 7の時点では、floor(x + 0.5)とround(x)は、この問題とまったく同じことをしません。Math.round(0.49999999999999994)が1を返すのはなぜですか

これにより、表現エラー内に正しく丸められます。つまり、結果は正確ではありませんが、たとえば0.001は正確に表されませんが、toString()を使用すると、これが修正されます。BigDecimalに変換するか、算術演算を実行した場合にのみ、この表現エラーが表示されます。

于 2012-04-05T06:08:31.113 に答える