1/*
2 * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24import sun.java2d.marlin.FloatMath;
25
26/*
27 * @test
28 * @summary Check for correct implementation of FloatMath.ceil/floor
29 * @run main CeilAndFloorTests
30 * @modules java.desktop/sun.java2d.marlin
31 */
32public class CeilAndFloorTests {
33
34    public static String toHexString(float f) {
35        if (!Float.isNaN(f))
36            return Float.toHexString(f);
37        else
38            return "NaN(0x" + Integer.toHexString(Float.floatToRawIntBits(f)) + ")";
39    }
40
41    public static int test(String testName, float input,
42                           float result, float expected) {
43        if (Float.compare(expected, result) != 0) {
44            System.err.println("Failure for " + testName + ":\n" +
45                               "\tFor input " + input    + "\t(" + toHexString(input) + ")\n" +
46                               "\texpected  " + expected + "\t(" + toHexString(expected) + ")\n" +
47                               "\tgot       " + result   + "\t(" + toHexString(result) + ").");
48            return 1;
49        }
50        else
51            return 0;
52    }
53
54    public static int test_skip_0(String testName, float input,
55                           float result, float expected)
56    {
57        // floor_int does not distinguish +0f and -0f
58        // but it is not critical for Marlin
59        if (Float.compare(expected, result) != 0 && (expected != 0f))
60        {
61            System.err.println("Failure for " + testName + ":\n" +
62                               "\tFor input " + input    + "\t(" + toHexString(input) + ")\n" +
63                               "\texpected  " + expected + "\t(" + toHexString(expected) + ")\n" +
64                               "\tgot       " + result   + "\t(" + toHexString(result) + ").");
65            return 1;
66        }
67        else
68            return 0;
69    }
70
71    private static int testCeilCase(float input, float expected) {
72        int failures = 0;
73        // float result:
74        failures += test("FloatMath.ceil_f", input, FloatMath.ceil_f(input), expected);
75        // int result:
76        failures += test("FloatMath.ceil_int", input, FloatMath.ceil_int(input), (int)expected);
77        failures += test("FloatMath.ceil_f (int)", input, (int)FloatMath.ceil_f(input), (int)expected);
78        return failures;
79    }
80
81    private static int testFloorCase(float input, float expected) {
82        int failures = 0;
83        // float result:
84        failures += test       ("FloatMath.floor_f", input, FloatMath.floor_f(input), expected);
85        // ignore difference between +0f and -0f:
86        failures += test_skip_0("FloatMath.floor_int", input, FloatMath.floor_int(input), (int)expected);
87        failures += test_skip_0("FloatMath.floor_f (int)", input, (int)FloatMath.floor_f(input), (int)expected);
88        return failures;
89    }
90
91    private static int nearIntegerTests() {
92        int failures = 0;
93
94        float [] fixedPoints = {
95            -0.0f,
96             0.0f,
97            -1.0f,
98             1.0f,
99            -0x1.0p52f,
100             0x1.0p52f,
101            -Float.MAX_VALUE,
102             Float.MAX_VALUE,
103             Float.NEGATIVE_INFINITY,
104             Float.POSITIVE_INFINITY,
105             Float.NaN,
106        };
107
108        for(float fixedPoint : fixedPoints) {
109            failures += testCeilCase(fixedPoint, fixedPoint);
110            failures += testFloorCase(fixedPoint, fixedPoint);
111        }
112
113        for(int i = Float.MIN_EXPONENT; i <= Float.MAX_EXPONENT; i++) {
114            float powerOfTwo   = Math.scalb(1.0f, i);
115            float neighborDown = Math.nextDown(powerOfTwo);
116            float neighborUp   = Math.nextUp(powerOfTwo);
117
118            if (i < 0) {
119                failures += testCeilCase( powerOfTwo,  1.0f);
120                failures += testCeilCase(-powerOfTwo, -0.0f);
121
122                failures += testFloorCase( powerOfTwo,  0.0f);
123                failures += testFloorCase(-powerOfTwo, -1.0f);
124
125                failures += testCeilCase( neighborDown, 1.0f);
126                failures += testCeilCase(-neighborDown, -0.0f);
127
128                failures += testFloorCase( neighborUp,  0.0f);
129                failures += testFloorCase(-neighborUp, -1.0f);
130            } else {
131                failures += testCeilCase(powerOfTwo, powerOfTwo);
132                failures += testFloorCase(powerOfTwo, powerOfTwo);
133
134                if (neighborDown==Math.rint(neighborDown)) {
135                    failures += testCeilCase( neighborDown,  neighborDown);
136                    failures += testCeilCase(-neighborDown, -neighborDown);
137
138                    failures += testFloorCase( neighborDown, neighborDown);
139                    failures += testFloorCase(-neighborDown,-neighborDown);
140                } else {
141                    failures += testCeilCase( neighborDown, powerOfTwo);
142                    failures += testFloorCase(-neighborDown, -powerOfTwo);
143                }
144
145                if (neighborUp==Math.rint(neighborUp)) {
146                    failures += testCeilCase(neighborUp, neighborUp);
147                    failures += testCeilCase(-neighborUp, -neighborUp);
148
149                    failures += testFloorCase(neighborUp, neighborUp);
150                    failures += testFloorCase(-neighborUp, -neighborUp);
151                } else {
152                    failures += testFloorCase(neighborUp, powerOfTwo);
153                    failures += testCeilCase(-neighborUp, -powerOfTwo);
154                }
155            }
156        }
157
158        for(int i = -(0x10000); i <= 0x10000; i++) {
159            float f = (float) i;
160            float neighborDown = Math.nextDown(f);
161            float neighborUp   = Math.nextUp(f);
162
163            failures += testCeilCase( f, f);
164            failures += testCeilCase(-f, -f);
165
166            failures += testFloorCase( f, f);
167            failures += testFloorCase(-f, -f);
168
169            if (Math.abs(f) > 1.0) {
170                failures += testCeilCase( neighborDown, f);
171                failures += testCeilCase(-neighborDown, -f+1);
172
173                failures += testFloorCase( neighborUp, f);
174                failures += testFloorCase(-neighborUp, -f-1);
175            }
176        }
177
178        return failures;
179    }
180
181    public static int roundingTests() {
182        int failures = 0;
183        float [][] testCases = {
184            { Float.MIN_VALUE,                           1.0f},
185            {-Float.MIN_VALUE,                          -0.0f},
186            { Math.nextDown(Float.MIN_NORMAL),           1.0f},
187            {-Math.nextDown(Float.MIN_NORMAL),          -0.0f},
188            { Float.MIN_NORMAL,                          1.0f},
189            {-Float.MIN_NORMAL,                         -0.0f},
190
191            { 0.1f,                                        1.0f},
192            {-0.1f,                                       -0.0f},
193
194            { 0.5f,                                        1.0f},
195            {-0.5f,                                       -0.0f},
196
197            { 1.5f,                                        2.0f},
198            {-1.5f,                                       -1.0f},
199
200            { 2.5f,                                        3.0f},
201            {-2.5f,                                       -2.0f},
202
203            { 12.3456789f,                                13.0f},
204            {-12.3456789f,                               -12.0f},
205
206            { Math.nextDown(1.0f),                         1.0f},
207            { Math.nextDown(-1.0f),                       -1.0f},
208
209            { Math.nextUp(1.0f),                           2.0f},
210            { Math.nextUp(-1.0f),                         -0.0f},
211
212            { 0x1.0p22f,                                 0x1.0p22f},
213            {-0x1.0p22f,                                -0x1.0p22f},
214
215            { Math.nextDown(0x1.0p22f),                  0x1.0p22f},
216            {-Math.nextUp(0x1.0p22f),                   -0x1.0p22f},
217
218            { Math.nextUp(0x1.0p22f),                    0x1.0p22f+1f},
219            {-Math.nextDown(0x1.0p22f),                 -0x1.0p22f+1f},
220
221            { Math.nextDown(0x1.0p23f),                  0x1.0p23f},
222            {-Math.nextUp(0x1.0p23f),                   -0x1.0p23f-1f},
223
224            { Math.nextUp(0x1.0p23f),                    0x1.0p23f+1f},
225            {-Math.nextDown(0x1.0p23f),                 -0x1.0p23f+1f},
226        };
227
228        for(float[] testCase : testCases) {
229            failures += testCeilCase(testCase[0], testCase[1]);
230            failures += testFloorCase(-testCase[0], -testCase[1]);
231        }
232        return failures;
233    }
234
235    public static void main(String... args) {
236        int failures = 0;
237
238        System.out.println("nearIntegerTests");
239        failures += nearIntegerTests();
240
241        System.out.println("roundingTests");
242        failures += roundingTests();
243
244        if (failures > 0) {
245            System.err.println("Testing {FloatMath}.ceil/floor incurred "
246                               + failures + " failures.");
247            throw new RuntimeException();
248        }
249    }
250}
251