Code review request for 6908131 Pure Java implementations of StrictMath.floor(double) &StrictMath.ceil(double)

Dmitry Nadezhin Dmitry.Nadezhin at Sun.COM
Thu Jan 21 06:27:05 UTC 2010


Jeff,

Your"ceil" and "floor" seems correct for me.
I benchmarked your code by attached benchmark program.
It is faster on some test patterns and with some Jvm options and it is 
slower on some others.

Joe,

I'm curious how performance decisions are made in Jdk development process.
Are there performance benchmarks in Jdk source tree ? Which platforms are
considered and with which weight ?

  -Dima

The benchmarks results on my OpenSolaris opteron computer (jdk7-b79):

pattern1 is  {+0.0, +0.5, +1.0, +1.5, +2.0, +2.5, +3.0, +3.5, +4.0}.
pattern2 is {+0.0, -0.0, +1.5, -1.5, 1000.0, -1000.0, (1L << 40), -(1L 
<< 40), Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY}.


*** pattern1  -d32 -client
empty                took       2,52 nsec 
Math.ceil            took      24,23 nsec 
Math.floor           took      23,56 nsec 
StrictMath.ceil      took      24,59 nsec 
StrictMath.floor     took      24,08 nsec 
FloorCeil.ceil       took      13,91 nsec 
FloorCeil.floor      took      11,31 nsec 

*** pattern1  -d32 -server
empty                took       1,67 nsec 
Math.ceil            took      10,29 nsec 
Math.floor           took      10,44 nsec 
StrictMath.ceil      took      10,27 nsec 
StrictMath.floor     took      10,44 nsec 
FloorCeil.ceil       took       9,01 nsec 
FloorCeil.floor      took       5,28 nsec 

*** pattern1  -d64 -client
empty                took       1,69 nsec 
Math.ceil            took       6,92 nsec 
Math.floor           took       6,18 nsec 
StrictMath.ceil      took       6,90 nsec 
StrictMath.floor     took       6,18 nsec 
FloorCeil.ceil       took       8,31 nsec 
FloorCeil.floor      took       5,35 nsec 

*** pattern1  -d64 -server
empty                took       1,67 nsec 
Math.ceil            took       6,73 nsec 
Math.floor           took       6,07 nsec 
StrictMath.ceil      took       6,73 nsec 
StrictMath.floor     took       6,07 nsec 
FloorCeil.ceil       took       9,00 nsec 
FloorCeil.floor      took       5,25 nsec 

*** pattern2  -d32 -client
empty                took       2,58 nsec
Math.ceil            took      24,24 nsec
Math.floor           took      23,99 nsec
StrictMath.ceil      took      24,25 nsec
StrictMath.floor     took      24,34 nsec
FloorCeil.ceil       took      15,94 nsec
FloorCeil.floor      took      16,45 nsec

*** pattern2  -d32 -server
empty                took       1,67 nsec
Math.ceil            took      12,38 nsec
Math.floor           took      12,26 nsec
StrictMath.ceil      took      12,54 nsec
StrictMath.floor     took      12,31 nsec
FloorCeil.ceil       took       9,08 nsec
FloorCeil.floor      took       9,64 nsec

*** pattern2  -d64 -client
empty                took       1,67 nsec
Math.ceil            took       6,07 nsec
Math.floor           took       6,33 nsec
StrictMath.ceil      took       6,00 nsec
StrictMath.floor     took       6,39 nsec
FloorCeil.ceil       took       9,81 nsec
FloorCeil.floor      took       7,39 nsec

*** pattern2  -d64 -server
empty                took       1,67 nsec
Math.ceil            took       6,32 nsec
Math.floor           took       6,27 nsec
StrictMath.ceil      took       6,40 nsec
StrictMath.floor     took       6,37 nsec
FloorCeil.ceil       took       9,69 nsec
FloorCeil.floor      took       7,39 nsec

------------------------ The benchmark code
package floorceilingtests;

public class Main {

    public static void main(String[] args) {
        double[] a = new double[1000];
        int pattern = 1;
        switch (pattern) {
            case 1:
                pattern1(a);
                break;
            case 2:
                pattern2(a);
                break;
        }
        new Main().test(a);
    }

    private static void pattern1(double[] a) {
        for (int i = 0; i < a.length; i += 10) {
            a[i+0] = +0.0;
            a[i+2] = +0.5;
            a[i+3] = +1.0;
            a[i+4] = +1.5;
            a[i+5] = +2.0;
            a[i+6] = +2.5;
            a[i+7] = +3.0;
            a[i+8] = +3.5;
            a[i+9] = +4.0;
        }
    }

    private static void pattern2(double[] a) {
        for (int i = 0; i < a.length; i += 10) {
            a[i+0] = +0.0;
            a[i+1] = -0.0;
            a[i+2] = +1.5;
            a[i+3] = -1.5;
            a[i+4] = 1000.0;
            a[i+5] = -1000.0;
            a[i+6] = (1L << 40);
            a[i+7] = -(1L << 40);
            a[i+8] = Double.POSITIVE_INFINITY;
            a[i+9] = Double.NEGATIVE_INFINITY;
        }
    }

    private void test (double[] args) {
        for (int i = 0; i < 5; i++) {
            System.out.println();
            for (Benchmark b: benchmarks) {
                b.run(args, 100000);
            }
        }
    }

    private Benchmark[] benchmarks = {
        new Benchmark("empty") {
            double run(double[] args) {
                double s = 0;
                for (double d: args) {
                    s += d;
                }
                return s;
            }
        },
        new Benchmark("Math.ceil") {
            double run(double[] args) {
                double s = 0;
                for (double d: args) {
                    s += Math.ceil(d);
                }
                return s;
            }
        },
        new Benchmark("Math.floor") {
            double run(double[] args) {
                double s = 0;
                for (double d: args) {
                    s += Math.floor(d);
                }
                return s;
            }
        },
        new Benchmark("StrictMath.ceil") {
            double run(double[] args) {
                double s = 0;
                for (double d: args) {
                    s += StrictMath.ceil(d);
                }
                return s;
            }
        },
        new Benchmark("StrictMath.floor") {
            double run(double[] args) {
                double s = 0;
                for (double d: args) {
                    s += StrictMath.floor(d);
                }
                return s;
            }
        },
        new Benchmark("FloorCeil.ceil") {
            double run(double[] args) {
                double s = 0;
                for (double d: args) {
                    s += FloorCeil.ceil(d);
                }
                return s;
            }
        },
        new Benchmark("FloorCeil.floor") {
            double run(double[] args) {
                double s = 0;
                for (double d: args) {
                    s += FloorCeil.floor(d);
                }
                return s;
            }
        }
    };


    private abstract static class Benchmark {
        private final String name;

        private Benchmark(String name) {
            this.name = name;
        }

        abstract double run(double[] args);

        void run(double[] args, int repeatCount) {
            long startTime = System.nanoTime();
            double s = 0;
            for (int i = 0; i < repeatCount; i++) {
                s += run(args);
            }
            long stopTime = System.nanoTime();
            System.out.printf("%-20s took %10.2f nsec %s\n", name, 
(stopTime - startTime)/(double)repeatCount/args.length, s > 0 ? " " : "");
        }
    }
}
------------------------




Jeff Hain wrote:
> Hello.
>  
> I happen to already have developped some pure Java version of 
> ceil(double) and floor(double).
> It looks faster to me. But maybe is it incorrect ? (it's tested, but 
> I'm never sure)
> Here it is :
>  
> public class FloorCeil {
>     private static final double TWO_POW_26 = 
> Double.longBitsToDouble(0x4190000000000000L);
>     private static final double TWO_POW_N26 = 
> Double.longBitsToDouble(0x3E50000000000000L);
>     private static final double TWO_POW_52 = 
> Double.longBitsToDouble(0x4330000000000000L);
>     public static double floor(double value) {
>         // Faster than to work directly on bits.
>         if (Math.abs(value) <= (double)Integer.MAX_VALUE) {
>             if (value > 0.0) {
>                 return (double)(int)value;
>             } else if (value < 0.0) {
>                 double anteComaDigits = (double)(int)value;
>                 if (value != anteComaDigits) {
>                     return anteComaDigits - 1.0;
>                 } else {
>                     return anteComaDigits;
>                 }
>             } else { // value is +-0.0 (not NaN due to test against 
> Integer.MAX_VALUE)
>                 return value;
>             }
>         } else if (Math.abs(value) < TWO_POW_52) {
>             // We split the value in two:
>             // high part, which is a mathematical integer,
>             // and the rest, for which we can get rid of the
>             // post coma digits by casting into an int.
>             double highPart = ((int)(value * TWO_POW_N26)) * TWO_POW_26;
>             if (value > 0.0) {
>                 return highPart + (double)((int)(value - highPart));
>             } else {
>                 double anteComaDigits = highPart + 
> (double)((int)(value - highPart));
>                 if (value != anteComaDigits) {
>                     return anteComaDigits - 1.0;
>                 } else {
>                     return anteComaDigits;
>                 }
>             }
>         } else { // abs(value) >= 2^52, or value is NaN
>             return value;
>         }
>     }
>     public static double ceil(double value) {
>         return -floor(-value);
>     }
> }
>  
> Jeff
>
>  
>  
>




More information about the core-libs-dev mailing list