Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the fastest way to round to three decimal places?

Tags:

java

The SO community was right, profiling your code before you ask performance questions seems to make more sense then my approach of randomly guessing :-) I profiled my code(very intensive math) and didn't realize over 70% of my code is apparently in a part I didn't think was a source of slowdown, rounding of decimals.

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

My problem is I get decimal numbers that are normally .01,.02,etc..but sometimes I get something like .070000000001 (I really only care about the 0.07 but floating point precision causes my other formulas that result to fail), I simply want the first 3 decimals to avoid this problem.

So is there a better/faster way to do this?

like image 520
Lostsoul Avatar asked Apr 05 '12 03:04

Lostsoul


People also ask

What is the best way to round a number to 3 decimal places?

Rounding to the nearest thousandth means the same as rounding to 3 decimal places. For example, round 3.3685 to the nearest thousandth. The 3rd digit after the decimal point is 8 and so the choice is to round up to 3.369 or down to 3.368.

What is an answer to 3 decimal places?

The third decimal digit from the decimal point is the thousandths digit. For example, 0.008 is eight thousandths. Read the whole set of three decimal digits as a number, and say “thousandths.” 0.825 has 8 tenths, 2 hundredths, and 5 thousandths.


2 Answers

The standard way to round (positive) numbers would be something like this:

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

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

But as @nuakh commented, you'll always be plagued by rounding errors to some extent. If you want exactly 3 decimal places, your best bet is to convert to thousandths (that is, multiply everything by 1000) and use an integral data type (int, long, etc.)

In that case, you'd skip the final division by 1000 and use the integral values 123 and 568 for your calculations. If you want the results in the form of percentages, you'd divide by 10 for display:

123 → 12.3%
568 → 56.8%

like image 58
Adam Liss Avatar answered Sep 26 '22 03:09

Adam Liss


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.

like image 26
Peter Lawrey Avatar answered Sep 24 '22 03:09

Peter Lawrey